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. Treat the failure the same
as a queue-overflow and return -ENOMEM to notify the caller.

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]
Signed-off-by: Nicolin Chen <[email protected]>
---
 drivers/iommu/iommufd/driver.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 61e6b02601d1a..b0dec089aeee1 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -149,15 +149,28 @@ int iommufd_viommu_report_event(struct iommufd_viommu 
*viommu,
                goto out_unlock_veventqs;
        }
 
-       spin_lock(&veventq->common.lock);
-       if (veventq->num_events == veventq->depth) {
+       /*
+        * Optimistic skip when clearly full. A concurrent reader may free a 
slot
+        * before the spinlock; the cost is recording one extra event as lost.
+        */
+       if (READ_ONCE(veventq->num_events) >= veventq->depth) {
+               spin_lock(&veventq->common.lock);
                vevent = &veventq->lost_events_header;
                goto out_set_header;
        }
 
-       vevent = kzalloc_flex(*vevent, event_data, data_len, GFP_ATOMIC);
+       /* 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;
+       }
+
+       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