Skip to content

Commit b42497e

Browse files
committed
iommufd: Prevent ALIGN() overflow
When allocating IOVA the candidate range gets aligned to the target alignment. If the range is close to ULONG_MAX then the ALIGN() can wrap resulting in a corrupted iova. Open code the ALIGN() using get_add_overflow() to prevent this. This simplifies the checks as we don't need to check for length earlier either. Consolidate the two copies of this code under a single helper. This bug would allow userspace to create a mapping that overlaps with some other mapping or a reserved range. Cc: [email protected] Fixes: 51fe614 ("iommufd: Data structure to provide IOVA to PFN mapping") Reported-by: [email protected] Closes: https://lore.kernel.org/r/[email protected] Reviewed-by: Yi Liu <[email protected]> Reviewed-by: Nicolin Chen <[email protected]> Link: https://patch.msgid.link/all/[email protected]/ Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 601b1d0 commit b42497e

File tree

1 file changed

+25
-16
lines changed

1 file changed

+25
-16
lines changed

drivers/iommu/iommufd/io_pagetable.c

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,36 +70,45 @@ struct iopt_area *iopt_area_contig_next(struct iopt_area_contig_iter *iter)
7070
return iter->area;
7171
}
7272

73-
static bool __alloc_iova_check_hole(struct interval_tree_double_span_iter *span,
74-
unsigned long length,
75-
unsigned long iova_alignment,
76-
unsigned long page_offset)
73+
static bool __alloc_iova_check_range(unsigned long *start, unsigned long last,
74+
unsigned long length,
75+
unsigned long iova_alignment,
76+
unsigned long page_offset)
7777
{
78-
if (span->is_used || span->last_hole - span->start_hole < length - 1)
78+
unsigned long aligned_start;
79+
80+
/* ALIGN_UP() */
81+
if (check_add_overflow(*start, iova_alignment - 1, &aligned_start))
7982
return false;
83+
aligned_start &= ~(iova_alignment - 1);
84+
aligned_start |= page_offset;
8085

81-
span->start_hole = ALIGN(span->start_hole, iova_alignment) |
82-
page_offset;
83-
if (span->start_hole > span->last_hole ||
84-
span->last_hole - span->start_hole < length - 1)
86+
if (aligned_start >= last || last - aligned_start < length - 1)
8587
return false;
88+
*start = aligned_start;
8689
return true;
8790
}
8891

89-
static bool __alloc_iova_check_used(struct interval_tree_span_iter *span,
92+
static bool __alloc_iova_check_hole(struct interval_tree_double_span_iter *span,
9093
unsigned long length,
9194
unsigned long iova_alignment,
9295
unsigned long page_offset)
9396
{
94-
if (span->is_hole || span->last_used - span->start_used < length - 1)
97+
if (span->is_used)
9598
return false;
99+
return __alloc_iova_check_range(&span->start_hole, span->last_hole,
100+
length, iova_alignment, page_offset);
101+
}
96102

97-
span->start_used = ALIGN(span->start_used, iova_alignment) |
98-
page_offset;
99-
if (span->start_used > span->last_used ||
100-
span->last_used - span->start_used < length - 1)
103+
static bool __alloc_iova_check_used(struct interval_tree_span_iter *span,
104+
unsigned long length,
105+
unsigned long iova_alignment,
106+
unsigned long page_offset)
107+
{
108+
if (span->is_hole)
101109
return false;
102-
return true;
110+
return __alloc_iova_check_range(&span->start_used, span->last_used,
111+
length, iova_alignment, page_offset);
103112
}
104113

105114
/*

0 commit comments

Comments
 (0)