The veventq memory allocation happens inside the spinlock. Given its depth
is decided by the user space, this leaves a vulnerability, where userspace
can allocate large queues to exhaust atomic memory reserves.

Move the allocation outside the spinlock and use GFP_NOWAIT, which can fail
fast under memory pressure without dipping into the GFP_ATOMIC reserves or
direct-reclaiming from the threaded IRQ handler. On allocation failure,
queue the lost_events_header (so userspace learns of the drop) and return
-ENOMEM so the caller learns of the kernel-side memory pressure.

This is intentionally distinct from the queue-overflow path, which also
queues the lost_events_header but returns 0: a full queue is an expected
userspace-pacing condition rather than a kernel error.

A subsequent change will cap the upper bound of the veventq_depth.

Fixes: e36ba5ab808e ("iommufd: Add IOMMUFD_OBJ_VEVENTQ and 
IOMMUFD_CMD_VEVENTQ_ALLOC")
Cc: [email protected]
Reviewed-by: Jason Gunthorpe <[email protected]>
Signed-off-by: Nicolin Chen <[email protected]>
---
 drivers/iommu/iommufd/driver.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 61e6b02601d1a..3b8067976eac0 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -149,15 +149,18 @@ int iommufd_viommu_report_event(struct iommufd_viommu 
*viommu,
                goto out_unlock_veventqs;
        }
 
-       spin_lock(&veventq->common.lock);
-       if (veventq->num_events == veventq->depth) {
+       /* Pre-allocate to avoid GFP_ATOMIC; use GFP_NOWAIT to avoid sleeping */
+       vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_NOWAIT);
+       if (!vevent) {
+               spin_lock(&veventq->common.lock);
                vevent = &veventq->lost_events_header;
+               rc = -ENOMEM;
                goto out_set_header;
        }
 
-       vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_ATOMIC);
-       if (!vevent) {
-               rc = -ENOMEM;
+       spin_lock(&veventq->common.lock);
+       if (veventq->num_events == veventq->depth) {
+               kfree(vevent);
                vevent = &veventq->lost_events_header;
                goto out_set_header;
        }
-- 
2.43.0


Reply via email to