mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
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: stable@vger.kernel.org
Fixes: 51fe6141f0 ("iommufd: Data structure to provide IOVA to PFN mapping")
Reported-by: syzbot+c2f65e2801743ca64e08@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/r/685af644.a00a0220.2e5631.0094.GAE@google.com
Reviewed-by: Yi Liu <yi.l.liu@intel.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Link: https://patch.msgid.link/all/1-v1-7b4a16fc390b+10f4-iommufd_alloc_overflow_jgg@nvidia.com/
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
parent
601b1d0d93
commit
b42497e3c0
|
|
@ -70,20 +70,34 @@ struct iopt_area *iopt_area_contig_next(struct iopt_area_contig_iter *iter)
|
|||
return iter->area;
|
||||
}
|
||||
|
||||
static bool __alloc_iova_check_range(unsigned long *start, unsigned long last,
|
||||
unsigned long length,
|
||||
unsigned long iova_alignment,
|
||||
unsigned long page_offset)
|
||||
{
|
||||
unsigned long aligned_start;
|
||||
|
||||
/* ALIGN_UP() */
|
||||
if (check_add_overflow(*start, iova_alignment - 1, &aligned_start))
|
||||
return false;
|
||||
aligned_start &= ~(iova_alignment - 1);
|
||||
aligned_start |= page_offset;
|
||||
|
||||
if (aligned_start >= last || last - aligned_start < length - 1)
|
||||
return false;
|
||||
*start = aligned_start;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __alloc_iova_check_hole(struct interval_tree_double_span_iter *span,
|
||||
unsigned long length,
|
||||
unsigned long iova_alignment,
|
||||
unsigned long page_offset)
|
||||
{
|
||||
if (span->is_used || span->last_hole - span->start_hole < length - 1)
|
||||
if (span->is_used)
|
||||
return false;
|
||||
|
||||
span->start_hole = ALIGN(span->start_hole, iova_alignment) |
|
||||
page_offset;
|
||||
if (span->start_hole > span->last_hole ||
|
||||
span->last_hole - span->start_hole < length - 1)
|
||||
return false;
|
||||
return true;
|
||||
return __alloc_iova_check_range(&span->start_hole, span->last_hole,
|
||||
length, iova_alignment, page_offset);
|
||||
}
|
||||
|
||||
static bool __alloc_iova_check_used(struct interval_tree_span_iter *span,
|
||||
|
|
@ -91,15 +105,10 @@ static bool __alloc_iova_check_used(struct interval_tree_span_iter *span,
|
|||
unsigned long iova_alignment,
|
||||
unsigned long page_offset)
|
||||
{
|
||||
if (span->is_hole || span->last_used - span->start_used < length - 1)
|
||||
if (span->is_hole)
|
||||
return false;
|
||||
|
||||
span->start_used = ALIGN(span->start_used, iova_alignment) |
|
||||
page_offset;
|
||||
if (span->start_used > span->last_used ||
|
||||
span->last_used - span->start_used < length - 1)
|
||||
return false;
|
||||
return true;
|
||||
return __alloc_iova_check_range(&span->start_used, span->last_used,
|
||||
length, iova_alignment, page_offset);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user