[PATCH] iommufd: Prevent the use of nil data

Edward Adam Davis posted 1 patch 4 months ago
drivers/iommu/iommufd/io_pagetable.c | 3 +++
1 file changed, 3 insertions(+)
[PATCH] iommufd: Prevent the use of nil data
Posted by Edward Adam Davis 4 months ago
The division exception occurs because:
The denominator bitmap->bitmap is 0, which is derived from the nil value
of bitmap->data passed in by the reproducer.

Before calling iova_bitmap_alloc() to allocate iter, add a check for a
null value in data to avoid the division exception.

syzbot reported:
divide error in iova_bitmap_alloc

Call Trace:
 <TASK>
 iommu_read_and_clear_dirty drivers/iommu/iommufd/io_pagetable.c:543 [inline]
 iopt_read_and_clear_dirty_data+0x271/0x4c0 drivers/iommu/iommufd/io_pagetable.c:603
 iommufd_hwpt_get_dirty_bitmap+0x1c3/0x340 drivers/iommu/iommufd/hw_pagetable.c:485

Reported-by: syzbot+093a8a8b859472e6c257@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=093a8a8b859472e6c257
Signed-off-by: Edward Adam Davis <eadavis@qq.com>
---
 drivers/iommu/iommufd/io_pagetable.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index c0360c450880..9ddaed95e79f 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -540,6 +540,9 @@ iommu_read_and_clear_dirty(struct iommu_domain *domain,
 	if (!ops || !ops->read_and_clear_dirty)
 		return -EOPNOTSUPP;
 
+	if (!bitmap->data)
+		return -EINVAL;
+
 	iter = iova_bitmap_alloc(bitmap->iova, bitmap->length,
 				 bitmap->page_size,
 				 u64_to_user_ptr(bitmap->data));
-- 
2.43.0
Re: [PATCH] iommufd: Prevent the use of nil data
Posted by Jason Gunthorpe 4 months ago
On Wed, Oct 08, 2025 at 03:53:07PM +0800, Edward Adam Davis wrote:
> The division exception occurs because:
> The denominator bitmap->bitmap is 0, which is derived from the nil value
> of bitmap->data passed in by the reproducer.

No, that just causes the reproducer to bail early. The reported
problem is divide by zero. Which is caused by this:

	unsigned long pgsize = 1UL << bitmap->mapped.pgshift;

	return iova / (BITS_PER_TYPE(*bitmap->bitmap) * pgsize);


  //  ioctl$IOMMU_HWPT_GET_DIRTY_BITMAP arguments: [
  //    fd: fd_iommufd (resource)
  //    cmd: const = 0x3b8c (4 bytes)
  //    arg: ptr[in, iommu_hwpt_get_dirty_bitmap] {
  //      iommu_hwpt_get_dirty_bitmap {
  //        size: len = 0x30 (4 bytes)
  //        hwpt_id: hwpt_handle (resource)
  //        flags: iommufd_hwpt_get_dirty_bitmap_flags = 0x0 (4 bytes)
  //        __reserved: const = 0x0 (4 bytes)
  //        iova: int64 = 0x0 (8 bytes)
  //        length: int64 = 0x0 (8 bytes)
  //        page_size: int64 = 0x8000000000000000 (8 bytes)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^

0x8000000000000000 * BITS_PER_TYPE(*bitmap->bitmap) == 0

It should be fixed by adjusting the logic:

+       return (iova >> bitmap->mapped.pgshift) /
+              BITS_PER_TYPE(*bitmap->bitmap);

I will send a patch.

Jason