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