[PATCH v2] iommu/iommufd: avoid selftest dirty bitmap size wrap

From: Samuel Moelius

Date: Tue Jun 09 2026 - 20:25:56 EST


IOMMU_TEST_OP_DIRTY sizes its temporary dirty bitmap from
length / page_size. Very large selftest ranges can make the
DIV_ROUND_UP() additions wrap before allocation, producing a
zero-length allocation while the later test_bit() loop still walks the
original number of bits.

The selftest helper does not need to support unbounded dirty bitmap
sizes. Limit the requested range to SZ_16M pages before converting it
to bitmap allocation and copy sizes. If the maximum range calculation
overflows, treat the representable length limit as ULONG_MAX so that a
large page size is not rejected solely because the mathematical cap is
above the type limit.

Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <sam.moelius@xxxxxxxxxxxxxxx>
---
Changes in v2:
- Adjust how length is checked

drivers/iommu/iommufd/selftest.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/drivers/iommu/iommufd/selftest.c b/drivers/iommu/iommufd/selftest.c
index af07c642a526..4ff6fce362ef 100644
--- a/drivers/iommu/iommufd/selftest.c
+++ b/drivers/iommu/iommufd/selftest.c
@@ -12,6 +12,7 @@
#include <linux/iommu.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/sizes.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>
#include <linux/generic_pt/iommu.h>
@@ -1686,6 +1687,7 @@ static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
u32 flags)
{
unsigned long i, max;
+ unsigned long max_length;
struct iommu_test_cmd *cmd = ucmd->cmd;
struct iommufd_hw_pagetable *hwpt;
struct mock_iommu_domain *mock;
@@ -1705,6 +1707,13 @@ static int iommufd_test_dirty(struct iommufd_ucmd *ucmd, unsigned int mockpt_id,
goto out_put;
}

+ if (check_mul_overflow((unsigned long)SZ_16M, page_size, &max_length))
+ max_length = ULONG_MAX;
+ if (length > max_length) {
+ rc = -EOVERFLOW;
+ goto out_put;
+ }
+
max = length / page_size;
tmp = kvzalloc(DIV_ROUND_UP(max, BITS_PER_LONG) * sizeof(unsigned long),
GFP_KERNEL_ACCOUNT);
--
2.43.0