* [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows
@ 2026-07-01 12:28 Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Edward Srouji
` (7 more replies)
0 siblings, 8 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
The resource-tracking (restrack) database is the back-end for the netlink
"rdma resource show" interface which pins objects with
rdma_restrack_get().
QP/CQ/SRQ/PD/DMAH/counter destroy paths currently call
rdma_restrack_del() at the very end of ib_destroy_*_user() (or its
equivalent), i.e. after device->ops.destroy_*() has already freed the
vendor object. A concurrent netlink dump can therefore look the entry
up, take a reference via rdma_restrack_get(), and then dereference
already freed memory - most visibly through ib_query_qp() during
fill_res_qp_entry_query(), which produced the crash logged in patch 2.
Fix this by splitting the delete into a begin/commit/abort sequence:
begin_del() parks the entry as XA_ZERO_ENTRY (so lookups return NULL),
drops the birth reference and waits for in-flight readers to drain,
while keeping the index reserved. The destroy paths run begin_del()
first, then commit_del() on success or abort_del() on error.
abort_del() re-inserts into the reserved slot, so it needs no allocation
and cannot fail.
An earlier submission [1] carried the two preparatory cleanups that
removed broken restrack tracking for DCT and raw-packet RSS QPs (whose
IDs are never actually installed in the xarray) and those have been
picked up. This series is a standalone resend of the remaining fixes,
rebased on top of rdma-next and reworked according to Jason's feedback.
There is a small race between rdma_restrack_begin_del() and
rdma_restrack_add() for a new QP with the same ID, whereas if
rdma_restrack_begin() was called, then the provider destroys that QP,
and recreates a new one with the same ID and calls rdma_restrack_add()
before rdma_restrack_commit_del() can be called, the addition of the new
QP to restrack would fail, the new QP creation would succeed but it
simply wont be tracked.
This wasn't fixed as the race-window is really small and the worst case
scenario is an untracked QP and fixing that would needlessly clutter the
code.
[1] https://lore.kernel.org/linux-rdma/20260607-restrack-uaf-fix-v1-0-d72e45eb76c2@nvidia.com/
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
Patrisious Haddad (8):
RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations
RDMA/core: Fix use after free in ib_query_qp()
RDMA/core: Fix potential use after free in ib_destroy_cq_user()
RDMA/core: Fix potential use after free in ib_destroy_srq_user()
RDMA/core: Fix potential use after free in counter_release()
RDMA/core: Fix potential use after free in ib_free_cq()
RDMA/core: Fix potential use after free in uverbs_free_dmah()
RDMA/core: Fix potential use after free in ib_dealloc_pd_user()
drivers/infiniband/core/counters.c | 5 +-
drivers/infiniband/core/cq.c | 2 +-
drivers/infiniband/core/restrack.c | 167 +++++++++++++++++++-----
drivers/infiniband/core/restrack.h | 3 +
drivers/infiniband/core/uverbs_std_types_dmah.c | 7 +-
drivers/infiniband/core/verbs.c | 28 +++-
6 files changed, 167 insertions(+), 45 deletions(-)
---
base-commit: dc59e4fea9d83f03bad6bddf3fa2e52491777482
change-id: 20260701-restrack-uaf-fix-resub-6f434ec5844c
Best regards,
--
Edward Srouji <edwards@nvidia.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-02 17:46 ` Jason Gunthorpe
2026-07-01 12:28 ` [PATCH rdma-next 2/8] RDMA/core: Fix use after free in ib_query_qp() Edward Srouji
` (6 subsequent siblings)
7 siblings, 1 reply; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
Add rdma_restrack_abort_del(), rdma_restrack_begin_del() and
rdma_restrack_commit_del() functions to allow deleting a resource from
the xarray to effectively prevent future access to it and wait for all
current users to finish while preserving its index in the xarray to
allow to re-insert it if needed with guaranteed success.
This is a preparatory change for subsequent patches in the series
which will use these functions to fix the cleanup flow.
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/restrack.c | 167 +++++++++++++++++++++++++++++--------
drivers/infiniband/core/restrack.h | 3 +
2 files changed, 137 insertions(+), 33 deletions(-)
diff --git a/drivers/infiniband/core/restrack.c b/drivers/infiniband/core/restrack.c
index cfee2071586c16e3dfc4e21e5d58f8488dfb277f..30d433ee79a042fe270c9ed2f47b0e2d4a388ff9 100644
--- a/drivers/infiniband/core/restrack.c
+++ b/drivers/infiniband/core/restrack.c
@@ -129,6 +129,40 @@ static void rdma_restrack_attach_task(struct rdma_restrack_entry *res,
res->user = true;
}
+static struct rdma_restrack_root *res_to_rt(struct rdma_restrack_entry *res)
+{
+ struct ib_device *dev = res_to_dev(res);
+
+ if (WARN_ON(!dev))
+ return NULL;
+
+ return &dev->res[res->type];
+}
+
+static void restrack_drain_res(struct rdma_restrack_root *rt,
+ struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_entry *old;
+
+ old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL);
+ WARN_ON(old != res);
+
+ rdma_restrack_put(res);
+ wait_for_completion(&res->comp);
+}
+
+static void restrack_restore_res(struct rdma_restrack_root *rt,
+ struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_entry *old;
+
+ reinit_completion(&res->comp);
+ kref_init(&res->kref);
+
+ old = xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, GFP_KERNEL);
+ WARN_ON(old);
+}
+
/**
* rdma_restrack_set_name() - set the task for this resource
* @res: resource entry
@@ -177,22 +211,23 @@ void rdma_restrack_new(struct rdma_restrack_entry *res,
EXPORT_SYMBOL(rdma_restrack_new);
/**
- * rdma_restrack_add() - add object to the resource tracking database
+ * rdma_restrack_add() - add object to the resource tracking database.
+ * If this resource reuses an ID of a resource that was already destroyed
+ * after calling rdma_restrack_begin() but didn't yet call
+ * rdma_restrack_commit_del() it can result in an untracked QP.
* @res: resource entry
*/
void rdma_restrack_add(struct rdma_restrack_entry *res)
{
- struct ib_device *dev = res_to_dev(res);
struct rdma_restrack_root *rt;
int ret = 0;
- if (!dev)
- return;
-
if (res->no_track)
goto out;
- rt = &dev->res[res->type];
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
if (res->type == RDMA_RESTRACK_QP) {
/* Special case to ensure that LQPN points to right QP */
@@ -229,6 +264,32 @@ void rdma_restrack_add(struct rdma_restrack_entry *res)
}
EXPORT_SYMBOL(rdma_restrack_add);
+/**
+ * rdma_restrack_abort_del() - re-add object to the resource tracking database
+ * it can only be used after rdma_restrack_begin_del().
+ * @res: resource entry
+ */
+void rdma_restrack_abort_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt;
+
+ if (!res->valid)
+ return;
+
+ if (res->no_track) {
+ reinit_completion(&res->comp);
+ kref_init(&res->kref);
+ return;
+ }
+
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+
+ restrack_restore_res(rt, res);
+}
+EXPORT_SYMBOL(rdma_restrack_abort_del);
+
int __must_check rdma_restrack_get(struct rdma_restrack_entry *res)
{
return kref_get_unless_zero(&res->kref);
@@ -265,7 +326,7 @@ static void restrack_release(struct kref *kref)
struct rdma_restrack_entry *res;
res = container_of(kref, struct rdma_restrack_entry, kref);
- if (res->task) {
+ if (res->task && !res->valid) {
put_task_struct(res->task);
res->task = NULL;
}
@@ -291,37 +352,20 @@ EXPORT_SYMBOL(rdma_restrack_put);
*/
void rdma_restrack_sync(struct rdma_restrack_entry *res)
{
- struct rdma_restrack_entry *old;
struct rdma_restrack_root *rt;
- struct task_struct *task;
- struct ib_device *dev;
if (!res->valid || res->no_track)
return;
- dev = res_to_dev(res);
- if (WARN_ON(!dev))
+ rt = res_to_rt(res);
+ if (!rt)
return;
- rt = &dev->res[res->type];
if (WARN_ON(xa_get_mark(&rt->xa, res->id, RESTRACK_DD)))
return;
- old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL);
- if (WARN_ON(old != res))
- return;
-
- task = res->task;
- if (task)
- get_task_struct(task);
- rdma_restrack_put(res);
- wait_for_completion(&res->comp);
- reinit_completion(&res->comp);
- if (task)
- res->task = task;
- kref_init(&res->kref);
-
- xa_cmpxchg(&rt->xa, res->id, XA_ZERO_ENTRY, res, GFP_KERNEL);
+ restrack_drain_res(rt, res);
+ restrack_restore_res(rt, res);
}
EXPORT_SYMBOL(rdma_restrack_sync);
@@ -333,7 +377,6 @@ void rdma_restrack_del(struct rdma_restrack_entry *res)
{
struct rdma_restrack_entry *old;
struct rdma_restrack_root *rt;
- struct ib_device *dev;
if (!res->valid) {
if (res->task) {
@@ -346,12 +389,10 @@ void rdma_restrack_del(struct rdma_restrack_entry *res)
if (res->no_track)
goto out;
- dev = res_to_dev(res);
- if (WARN_ON(!dev))
+ rt = res_to_rt(res);
+ if (!rt)
return;
- rt = &dev->res[res->type];
-
old = xa_erase(&rt->xa, res->id);
WARN_ON(old != res);
@@ -359,5 +400,65 @@ void rdma_restrack_del(struct rdma_restrack_entry *res)
res->valid = false;
rdma_restrack_put(res);
wait_for_completion(&res->comp);
+ if (res->task) {
+ put_task_struct(res->task);
+ res->task = NULL;
+ }
}
EXPORT_SYMBOL(rdma_restrack_del);
+
+/**
+ * rdma_restrack_begin_del() - invalidate the object from the resource tracking
+ * database but preserve its index in the array.
+ * Since this preserves the index in the array until rdma_restrack_commit_del()
+ * is called, if rdma_restrack_add() is called in between with an old QP ID it
+ * can result in an untracked QP.
+ * @res: resource entry
+ */
+void rdma_restrack_begin_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt;
+
+ if (!res->valid)
+ return;
+
+ if (res->no_track) {
+ rdma_restrack_put(res);
+ wait_for_completion(&res->comp);
+ return;
+ }
+
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+
+ restrack_drain_res(rt, res);
+}
+EXPORT_SYMBOL(rdma_restrack_begin_del);
+
+/**
+ * rdma_restrack_commit_del() - delete object from the resource tracking
+ * database and free the task.
+ * @res: resource entry
+ */
+void rdma_restrack_commit_del(struct rdma_restrack_entry *res)
+{
+ struct rdma_restrack_root *rt;
+
+ if (!res->valid || res->no_track)
+ goto out;
+
+ rt = res_to_rt(res);
+ if (!rt)
+ return;
+
+ xa_erase(&rt->xa, res->id);
+
+out:
+ res->valid = false;
+ if (res->task) {
+ put_task_struct(res->task);
+ res->task = NULL;
+ }
+}
+EXPORT_SYMBOL(rdma_restrack_commit_del);
diff --git a/drivers/infiniband/core/restrack.h b/drivers/infiniband/core/restrack.h
index 75b8d1005a984b21896e296ac6ace1415a90905f..2df78e084e107c517a2468982add74ce3562e5f2 100644
--- a/drivers/infiniband/core/restrack.h
+++ b/drivers/infiniband/core/restrack.h
@@ -26,8 +26,11 @@ struct rdma_restrack_root {
int rdma_restrack_init(struct ib_device *dev);
void rdma_restrack_clean(struct ib_device *dev);
void rdma_restrack_add(struct rdma_restrack_entry *res);
+void rdma_restrack_abort_del(struct rdma_restrack_entry *res);
void rdma_restrack_del(struct rdma_restrack_entry *res);
void rdma_restrack_sync(struct rdma_restrack_entry *res);
+void rdma_restrack_begin_del(struct rdma_restrack_entry *res);
+void rdma_restrack_commit_del(struct rdma_restrack_entry *res);
void rdma_restrack_new(struct rdma_restrack_entry *res,
enum rdma_restrack_type type);
void rdma_restrack_set_name(struct rdma_restrack_entry *res,
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 2/8] RDMA/core: Fix use after free in ib_query_qp()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 3/8] RDMA/core: Fix potential use after free in ib_destroy_cq_user() Edward Srouji
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When querying a QP via the netlink flow the only synchronization
mechanism for the said QP is rdma_restrack_get(), meanwhile during the
QP destroy path rdma_restrack_del() is called at the end of the
ib_destroy_qp_user() function which is too late, since by then the
vendor specific resources for said QP would already be destroyed, and
till the rdma_restrack_del() is called this QP can still be accessed,
which could cause the use after free below.
Fix this by moving the rdma_restrack_begin_del() to the start of the
ib_destroy_qp_user(), which in turn waits for all usages of the QP to be
done then removes it from the database to prevent access to it while it
is being destroyed.
RIP: 0010:ib_query_qp+0x15/0x50 [ib_core]
Code: 48 83 05 5d 8e b9 ff 01 eb b5 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 c7 46 40 00 00 00 00 48 c7 46 78 00 00 00 00 <48> 8b 07 48 8b 80 88 01 00 00 48 85 c0 74 1a 48 83 05 54 91 b9 ff
RSP: 0018:ff11000108a8f2f0 EFLAGS: 00010202
RAX: 0000000000000000 RBX: ff11000108a8f370 RCX: ff11000108a8f370
RDX: 0000000000000000 RSI: ff11000108a8f3d8 RDI: 0000000000000000
RBP: ff1100010de5a000 R08: 0000000000000e80 R09: 0000000000000004
R10: ff110001057a604c R11: 0000000000000000 R12: ff11000108a8f370
R13: ff110001090e8000 R14: 0000000000000000 R15: ff110001057a602c
FS: 00007f2ffd8db6c0(0000) GS:ff110008dc90b000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000010b9a7004 CR4: 0000000000373eb0
Call Trace:
<TASK>
mlx5_ib_gsi_query_qp+0x21/0x50 [mlx5_ib]
mlx5_ib_query_qp+0x689/0x9d0 [mlx5_ib]
ib_query_qp+0x35/0x50 [ib_core]
fill_res_qp_entry_query.isra.0+0x47/0x280 [ib_core]
? __wake_up+0x40/0x50
? netlink_broadcast_filtered+0x15a/0x550
? kobject_uevent_env+0x562/0x710
? ep_poll_callback+0x242/0x270
? __nla_put+0xc/0x20
? nla_put+0x28/0x40
? nla_put_string+0x2e/0x40 [ib_core]
fill_res_qp_entry+0x138/0x190 [ib_core]
res_get_common_dumpit+0x4a5/0x800 [ib_core]
? fill_res_qp_entry_query.isra.0+0x280/0x280 [ib_core]
nldev_res_get_qp_dumpit+0x1e/0x30 [ib_core]
netlink_dump+0x16f/0x450
__netlink_dump_start+0x1ce/0x2e0
rdma_nl_rcv_msg+0x1d3/0x330 [ib_core]
? nldev_res_get_qp_raw_dumpit+0x30/0x30 [ib_core]
rdma_nl_rcv_skb.constprop.0.isra.0+0x108/0x180 [ib_core]
rdma_nl_rcv+0x12/0x20 [ib_core]
netlink_unicast+0x255/0x380
? __alloc_skb+0xfa/0x1e0
netlink_sendmsg+0x1f3/0x420
__sock_sendmsg+0x38/0x60
____sys_sendmsg+0x1e8/0x230
? copy_msghdr_from_user+0xea/0x170
___sys_sendmsg+0x7c/0xb0
? __futex_wait+0x95/0xf0
? __futex_wake_mark+0x40/0x40
? futex_wait+0x67/0x100
? futex_wake+0xac/0x1b0
__sys_sendmsg+0x5f/0xb0
do_syscall_64+0x55/0xb90
entry_SYSCALL_64_after_hwframe+0x4b/0x53
Fixes: 514aee660df4 ("RDMA: Globally allocate and release QP memory")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/verbs.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 3b613b57e269781e94e9d63ea75c7dcc46b1dacb..7abb89a4e6a019b965d36446d64609ed2c33d1c0 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -2154,6 +2154,8 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (qp->real_qp != qp)
return __ib_destroy_shared_qp(qp);
+ rdma_restrack_begin_del(&qp->res);
+
sec = qp->qp_sec;
if (sec)
ib_destroy_qp_security_begin(sec);
@@ -2166,6 +2168,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (ret) {
if (sec)
ib_destroy_qp_security_abort(sec);
+ rdma_restrack_abort_del(&qp->res);
return ret;
}
@@ -2178,7 +2181,7 @@ int ib_destroy_qp_user(struct ib_qp *qp, struct ib_udata *udata)
if (sec)
ib_destroy_qp_security_end(sec);
- rdma_restrack_del(&qp->res);
+ rdma_restrack_commit_del(&qp->res);
kfree(qp);
return ret;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 3/8] RDMA/core: Fix potential use after free in ib_destroy_cq_user()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 2/8] RDMA/core: Fix use after free in ib_query_qp() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 4/8] RDMA/core: Fix potential use after free in ib_destroy_srq_user() Edward Srouji
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a CQ via the netlink path the only synchronization
mechanism for the said CQ is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
ib_destroy_cq_user(), which is too late, since by that point
vendor-specific resources associated with the CQ might already be
freed. This can leave a short window where the CQ remains accessible
through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_begin_del() call to the start of
ib_destroy_cq_user(), ensuring that the CQ is removed from restrack
before its internal resources are released. This guarantees that no new
users hold references to a CQ that is in the process of destruction.
In addition, this change preserves the intended inverted order
between create and destroy routines: resources are added to
restrack at the end of successful creation, and hence shall be removed
from the restrack first thing during the destruction flow, which keeps
the lifecycle management consistent and predictable.
Fixes: 08f294a1524b ("RDMA/core: Add resource tracking for create and destroy CQs")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/verbs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 7abb89a4e6a019b965d36446d64609ed2c33d1c0..568cb71da726a61997149e92aa570e0bcb76926c 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -2247,11 +2247,15 @@ int ib_destroy_cq_user(struct ib_cq *cq, struct ib_udata *udata)
if (atomic_read(&cq->usecnt))
return -EBUSY;
+ rdma_restrack_begin_del(&cq->res);
+
ret = cq->device->ops.destroy_cq(cq, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&cq->res);
return ret;
+ }
- rdma_restrack_del(&cq->res);
+ rdma_restrack_commit_del(&cq->res);
kfree(cq);
return ret;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 4/8] RDMA/core: Fix potential use after free in ib_destroy_srq_user()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
` (2 preceding siblings ...)
2026-07-01 12:28 ` [PATCH rdma-next 3/8] RDMA/core: Fix potential use after free in ib_destroy_cq_user() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 5/8] RDMA/core: Fix potential use after free in counter_release() Edward Srouji
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a SRQ via the netlink path the only synchronization
mechanism for the said SRQ is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
ib_destroy_srq_user(), which is too late, since by that point
vendor-specific resources associated with the SRQ might already be
freed. This can leave a short window where the SRQ remains accessible
through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_begin_del() call to the start of
ib_destroy_srq_user(), ensuring that the SRQ is removed from restrack
before its internal resources are released. This guarantees that no new
users hold references to a SRQ that is in the process of destruction.
In addition, this change preserves the intended inverted order
between create and destroy routines: resources are added to
restrack at the end of successful creation, and hence shall be removed
from the restrack first thing during the destruction flow, which keeps
the lifecycle management consistent and predictable.
Fixes: 48f8a70e899f ("RDMA/restrack: Add support to get resource tracking for SRQ")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/verbs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 568cb71da726a61997149e92aa570e0bcb76926c..bfbc25dee95d031f91ffef5389e81faa08170719 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -1140,16 +1140,20 @@ int ib_destroy_srq_user(struct ib_srq *srq, struct ib_udata *udata)
if (atomic_read(&srq->usecnt))
return -EBUSY;
+ rdma_restrack_begin_del(&srq->res);
+
ret = srq->device->ops.destroy_srq(srq, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&srq->res);
return ret;
+ }
atomic_dec(&srq->pd->usecnt);
if (srq->srq_type == IB_SRQT_XRC && srq->ext.xrc.xrcd)
atomic_dec(&srq->ext.xrc.xrcd->usecnt);
if (ib_srq_has_cq(srq->srq_type))
atomic_dec(&srq->ext.cq->usecnt);
- rdma_restrack_del(&srq->res);
+ rdma_restrack_commit_del(&srq->res);
kfree(srq);
return ret;
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 5/8] RDMA/core: Fix potential use after free in counter_release()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
` (3 preceding siblings ...)
2026-07-01 12:28 ` [PATCH rdma-next 4/8] RDMA/core: Fix potential use after free in ib_destroy_srq_user() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 6/8] RDMA/core: Fix potential use after free in ib_free_cq() Edward Srouji
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a counter via the netlink path the only synchronization
mechanism for the said counter is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
counter_release(), which is too late, since by that point
vendor-specific resources associated with the counter might already be
freed. This can leave a short window where the counter remains
accessible through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_del() call to be before the
freeing of the vendor-specific resources, ensuring that the counter is
removed from restrack before its internal resources are released.
This guarantees that no new users hold references to a counter that is
in the process of destruction.
Fixes: 99fa331dc862 ("RDMA/counter: Add "auto" configuration mode support")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/counters.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/counters.c b/drivers/infiniband/core/counters.c
index a9e189194c1300a37479502959ddc1d1687bd40e..a2c85840c501a74752973bcb31452da9aa2e84fa 100644
--- a/drivers/infiniband/core/counters.c
+++ b/drivers/infiniband/core/counters.c
@@ -234,7 +234,6 @@ static void rdma_counter_free(struct rdma_counter *counter)
mutex_unlock(&port_counter->lock);
- rdma_restrack_del(&counter->res);
rdma_free_hw_stats_struct(counter->stats);
kfree(counter);
}
@@ -329,6 +328,7 @@ static void counter_release(struct kref *kref)
counter = container_of(kref, struct rdma_counter, kref);
counter_history_stat_update(counter);
+ rdma_restrack_del(&counter->res);
counter->device->ops.counter_dealloc(counter);
rdma_counter_free(counter);
}
@@ -490,7 +490,8 @@ static struct rdma_counter *rdma_get_counter_by_id(struct ib_device *dev,
return NULL;
counter = container_of(res, struct rdma_counter, res);
- kref_get(&counter->kref);
+ if (!kref_get_unless_zero(&counter->kref))
+ counter = NULL;
rdma_restrack_put(res);
return counter;
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 6/8] RDMA/core: Fix potential use after free in ib_free_cq()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
` (4 preceding siblings ...)
2026-07-01 12:28 ` [PATCH rdma-next 5/8] RDMA/core: Fix potential use after free in counter_release() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 7/8] RDMA/core: Fix potential use after free in uverbs_free_dmah() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 8/8] RDMA/core: Fix potential use after free in ib_dealloc_pd_user() Edward Srouji
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a CQ via the netlink path the only synchronization
mechanism for the said CQ is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
ib_free_cq(), which is too late, since by that point
vendor-specific resources associated with the CQ might already be
freed. This can leave a short window where the CQ remains accessible
through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_del() call to be before the freeing
of the vendor-specific resources ensuring that the CQ is removed from
restrack before its internal resources are released.
This guarantees that no new users hold references to a CQ that is in
the process of destruction.
Fixes: 43d781b9fa56 ("RDMA: Allow fail of destroy CQ")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/cq.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/infiniband/core/cq.c b/drivers/infiniband/core/cq.c
index 3d7b6cddd131c4ed07082719fc9e3b4bfdb51459..1379808e1404096654e8dc73a11f90d939910e54 100644
--- a/drivers/infiniband/core/cq.c
+++ b/drivers/infiniband/core/cq.c
@@ -327,6 +327,7 @@ void ib_free_cq(struct ib_cq *cq)
if (WARN_ON_ONCE(cq->cqe_used))
return;
+ rdma_restrack_del(&cq->res);
if (cq->device->ops.pre_destroy_cq) {
ret = cq->device->ops.pre_destroy_cq(cq);
WARN_ONCE(ret, "Disable of kernel CQ shouldn't fail");
@@ -353,7 +354,6 @@ void ib_free_cq(struct ib_cq *cq)
else
ret = cq->device->ops.destroy_cq(cq, NULL);
WARN_ONCE(ret, "Destroy of kernel CQ shouldn't fail");
- rdma_restrack_del(&cq->res);
kfree(cq->wc);
kfree(cq);
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 7/8] RDMA/core: Fix potential use after free in uverbs_free_dmah()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
` (5 preceding siblings ...)
2026-07-01 12:28 ` [PATCH rdma-next 6/8] RDMA/core: Fix potential use after free in ib_free_cq() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 8/8] RDMA/core: Fix potential use after free in ib_dealloc_pd_user() Edward Srouji
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a dmah via the netlink path the only synchronization
mechanism for the said dmah is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
uverbs_free_dmah(), which is too late, since by that point
vendor-specific resources associated with the dmah might already be
freed. This can leave a short window where the dmah remains accessible
through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_begin_del() call to the start of
uverbs_free_dmah(), ensuring that the dmah is removed from restrack
before its internal resources are released. This guarantees that no new
users hold references to a dmah that is in the process of destruction.
In addition, this change preserves the intended inverted order
between create and destroy routines: resources are added to
restrack at the end of successful creation, and hence shall be removed
from the restrack first thing during the destruction flow, which keeps
the lifecycle management consistent and predictable.
Fixes: d83edab562a4 ("RDMA/core: Introduce a DMAH object and its alloc/free APIs")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/uverbs_std_types_dmah.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/uverbs_std_types_dmah.c b/drivers/infiniband/core/uverbs_std_types_dmah.c
index 97101e0938263d114d6c1e398895bb8335915202..9873ab49a60132ce32891b96909c1dbfc383fe60 100644
--- a/drivers/infiniband/core/uverbs_std_types_dmah.c
+++ b/drivers/infiniband/core/uverbs_std_types_dmah.c
@@ -18,11 +18,14 @@ static int uverbs_free_dmah(struct ib_uobject *uobject,
if (atomic_read(&dmah->usecnt))
return -EBUSY;
+ rdma_restrack_begin_del(&dmah->res);
ret = dmah->device->ops.dealloc_dmah(dmah, attrs);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&dmah->res);
return ret;
+ }
- rdma_restrack_del(&dmah->res);
+ rdma_restrack_commit_del(&dmah->res);
kfree(dmah);
return 0;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH rdma-next 8/8] RDMA/core: Fix potential use after free in ib_dealloc_pd_user()
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
` (6 preceding siblings ...)
2026-07-01 12:28 ` [PATCH rdma-next 7/8] RDMA/core: Fix potential use after free in uverbs_free_dmah() Edward Srouji
@ 2026-07-01 12:28 ` Edward Srouji
7 siblings, 0 replies; 10+ messages in thread
From: Edward Srouji @ 2026-07-01 12:28 UTC (permalink / raw)
To: Jason Gunthorpe, Leon Romanovsky, Dennis Dalessandro,
Gal Pressman, Steve Wise, Mark Bloch, Neta Ostrovsky, Mark Zhang,
Mark Zhang, Majd Dibbiny, Yishai Hadas
Cc: linux-rdma, linux-kernel, Edward Srouji, Patrisious Haddad,
Michael Guralnik
From: Patrisious Haddad <phaddad@nvidia.com>
When accessing a PD via the netlink path the only synchronization
mechanism for the said PD is rdma_restrack_get().
Currently, rdma_restrack_del() is invoked at the end of
ib_dealloc_pd_user(), which is too late, since by that point
vendor-specific resources associated with the PD might already be
freed. This can leave a short window where the PD remains accessible
through restrack, leading to a potential use-after-free.
Fix this by moving the rdma_restrack_begin_del() call to the start of
ib_dealloc_pd_user(), ensuring that the PD is removed from restrack
before its internal resources are released. This guarantees that no new
users hold references to a PD that is in the process of destruction.
In addition, this change preserves the intended inverted order
between create and destroy routines: resources are added to
restrack at the end of successful creation, and hence shall be removed
from the restrack first thing during the destruction flow, which keeps
the lifecycle management consistent and predictable.
Fixes: 91a7c58fce06 ("RDMA: Restore ability to fail on PD deallocate")
Signed-off-by: Patrisious Haddad <phaddad@nvidia.com>
Reviewed-by: Michael Guralnik <michaelgur@nvidia.com>
Signed-off-by: Edward Srouji <edwards@nvidia.com>
---
drivers/infiniband/core/verbs.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index bfbc25dee95d031f91ffef5389e81faa08170719..f86e6f30b1df08396c151000fa7184574a034e0f 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -392,6 +392,7 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata)
{
int ret;
+ rdma_restrack_begin_del(&pd->res);
if (pd->__internal_mr) {
ret = pd->device->ops.dereg_mr(pd->__internal_mr, NULL);
WARN_ON(ret);
@@ -399,10 +400,12 @@ int ib_dealloc_pd_user(struct ib_pd *pd, struct ib_udata *udata)
}
ret = pd->device->ops.dealloc_pd(pd, udata);
- if (ret)
+ if (ret) {
+ rdma_restrack_abort_del(&pd->res);
return ret;
+ }
- rdma_restrack_del(&pd->res);
+ rdma_restrack_commit_del(&pd->res);
kfree(pd);
return ret;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations
2026-07-01 12:28 ` [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Edward Srouji
@ 2026-07-02 17:46 ` Jason Gunthorpe
0 siblings, 0 replies; 10+ messages in thread
From: Jason Gunthorpe @ 2026-07-02 17:46 UTC (permalink / raw)
To: Edward Srouji
Cc: Leon Romanovsky, Dennis Dalessandro, Gal Pressman, Steve Wise,
Mark Bloch, Neta Ostrovsky, Mark Zhang, Mark Zhang, Majd Dibbiny,
Yishai Hadas, linux-rdma, linux-kernel, Patrisious Haddad,
Michael Guralnik
On Wed, Jul 01, 2026 at 03:28:15PM +0300, Edward Srouji wrote:
> +static void restrack_drain_res(struct rdma_restrack_root *rt,
> + struct rdma_restrack_entry *res)
> +{
> + struct rdma_restrack_entry *old;
> +
> + old = xa_cmpxchg(&rt->xa, res->id, res, XA_ZERO_ENTRY, GFP_KERNEL);
> + WARN_ON(old != res);
> +
> + rdma_restrack_put(res);
> + wait_for_completion(&res->comp);
> +}
> + */
> +void rdma_restrack_begin_del(struct rdma_restrack_entry *res)
> +{
> + struct rdma_restrack_root *rt;
> +
> + if (!res->valid)
> + return;
> +
> + if (res->no_track) {
> + rdma_restrack_put(res);
> + wait_for_completion(&res->comp);
> + return;
> + }
> +
> + rt = res_to_rt(res);
> + if (!rt)
> + return;
> +
> + restrack_drain_res(rt, res);
Why the duplicatin of the put and wait? Maybe
restrack_drain_res(NULL, res)
should just skip the xa part? Same for the other functions.
Jason
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-07-02 17:46 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 12:28 [PATCH rdma-next 0/8] RDMA/core: Fix restrack UAF in destroy flows Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 1/8] RDMA/core: Add rdma_restrack_begin/abort/commit_del() operations Edward Srouji
2026-07-02 17:46 ` Jason Gunthorpe
2026-07-01 12:28 ` [PATCH rdma-next 2/8] RDMA/core: Fix use after free in ib_query_qp() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 3/8] RDMA/core: Fix potential use after free in ib_destroy_cq_user() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 4/8] RDMA/core: Fix potential use after free in ib_destroy_srq_user() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 5/8] RDMA/core: Fix potential use after free in counter_release() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 6/8] RDMA/core: Fix potential use after free in ib_free_cq() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 7/8] RDMA/core: Fix potential use after free in uverbs_free_dmah() Edward Srouji
2026-07-01 12:28 ` [PATCH rdma-next 8/8] RDMA/core: Fix potential use after free in ib_dealloc_pd_user() Edward Srouji
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox