iommu_gas: avoid overflow in bounds check

Change the range test in iommu_gas_match_one from '< ubound' to '<=
ubound', and pass a smaller-by-one ubound parameter to it, to avoid
overflow in ubound calculation.

Reported by:	andrew
Reviewed by:	andrew (previous version)
MFC after:	3 days
Differential Revision:	https://reviews.freebsd.org/D37764
This commit is contained in:
Doug Moore 2022-12-22 14:31:57 -06:00
parent 3cf65f8a7f
commit 5b9b55fbc4

View File

@ -309,7 +309,7 @@ struct iommu_gas_match_args {
/*
* The interval [beg, end) is a free interval between two iommu_map_entries.
* Addresses can be allocated only in the range [lbound, ubound). Try to
* Addresses can be allocated only in the range [lbound, ubound]. Try to
* allocate space in the free interval, subject to the conditions expressed by
* a, and return 'true' if and only if the allocation attempt succeeds.
*/
@ -332,10 +332,10 @@ iommu_gas_match_one(struct iommu_gas_match_args *a, iommu_gaddr_t beg,
start = roundup2(beg, a->common->alignment);
if (start < beg)
return (false);
end = MIN(end - IOMMU_PAGE_SIZE, ubound);
end = MIN(end - IOMMU_PAGE_SIZE - 1, ubound);
offset = a->offset;
size = a->size;
if (start + offset + size > end)
if (start + offset + size - 1 > end)
return (false);
/* Check for and try to skip past boundary crossing. */
@ -349,7 +349,7 @@ iommu_gas_match_one(struct iommu_gas_match_args *a, iommu_gaddr_t beg,
beg = roundup2(start + offset + 1, a->common->boundary);
start = roundup2(beg, a->common->alignment);
if (start + offset + size > end ||
if (start + offset + size - 1 > end ||
!vm_addr_bound_ok(start + offset, size,
a->common->boundary)) {
/*
@ -453,7 +453,7 @@ iommu_gas_find_space(struct iommu_domain *domain,
* Walk the big-enough ranges tree until one satisfies alignment
* requirements, or violates lowaddr address requirement.
*/
addr = a->common->lowaddr + 1;
addr = a->common->lowaddr;
for (curr = first; curr != NULL;
curr = iommu_gas_next(curr, min_free)) {
if ((first = RB_LEFT(curr, rb_entry)) != NULL &&
@ -464,7 +464,7 @@ iommu_gas_find_space(struct iommu_domain *domain,
return (0);
}
if (curr->end >= addr) {
/* All remaining ranges >= addr */
/* All remaining ranges > addr */
break;
}
if ((first = RB_RIGHT(curr, rb_entry)) != NULL &&
@ -502,14 +502,14 @@ iommu_gas_find_space(struct iommu_domain *domain,
curr = iommu_gas_next(curr, min_free)) {
if ((first = RB_LEFT(curr, rb_entry)) != NULL &&
iommu_gas_match_one(a, first->last, curr->start,
addr + 1, domain->end)) {
addr + 1, domain->end - 1)) {
RB_INSERT_PREV(iommu_gas_entries_tree,
&domain->rb_root, curr, a->entry);
return (0);
}
if ((first = RB_RIGHT(curr, rb_entry)) != NULL &&
iommu_gas_match_one(a, curr->end, first->first,
addr + 1, domain->end)) {
addr + 1, domain->end - 1)) {
RB_INSERT_NEXT(iommu_gas_entries_tree,
&domain->rb_root, curr, a->entry);
return (0);