CVE-2026-46053
Description
In the Linux kernel, the following vulnerability has been resolved:
net: rds: fix MR cleanup on copy error
__rds_rdma_map() hands sg/pages ownership to the transport after get_mr() succeeds. If copying the generated cookie back to user space fails after that point, the error path must not free those resources again before dropping the MR reference.
Remove the duplicate unpin/free from the put_user() failure branch so that MR teardown is handled only through the existing final cleanup path.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A double-free in Linux kernel RDS RDMA on copy_to_user() failure could corrupt memory; fixed by removing redundant cleanup.
Vulnerability
The Linux kernel's __rds_rdma_map() function, introduced in commit b3cb8cae530b, contains a double-free vulnerability. When get_mr() succeeds, ownership of scatter/gather pages is transferred to the transport. If the subsequent put_user() call copying the MR cookie back to userspace fails, the error path incorrectly calls unpin/free on those pages before dropping the MR reference. This leads to a double-free when the final MR cleanup path runs. The vulnerability affects kernel builds with RDS enabled (CONFIG_RDS) and requires local access with AF_RDS socket privileges. [1]
Exploitation
An attacker must have the ability to create an AF_RDS socket and trigger RDMA memory registration via rdma_map on a system exposed to RDS. The exploit requires a race window or a crafted put_user() failure (e.g., by using set_fs() tricks or memory pressure) to cause the cookie copy to fail after a successful get_mr(). The exact sequence involves: calling RDS_GET_MR ioctl, the kernel successfully pins pages and calls transport’s get_mr(), then the kernel fails to copy the MR handle to user space, hitting the erroneous error path that frees pages not owned by the kernel. [1]
Impact
Successful exploitation results in a double-free of kernel memory pages, which can lead to memory corruption, a system crash (denial of service), or potentially arbitrary code execution (kernel compromise) depending on heap layout. The attacker gains no direct data access but can destabilize the system or possibly escalate privileges. The CVE notes no active exploitation in the wild as of the patch date. [1]
Mitigation
The fix is included in Linux kernel stable updates published on 2026-05-27. Patched versions must contain commit b3cb8cae530b2727d8245684148bb49425f6765c. Users should update their kernels to the latest stable release that includes this commit. No workaround is available without patching. The vulnerability is not known to be listed on CISA’s Known Exploited Vulnerabilities catalog. [1]
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
108fdbb6262a4anet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
b3cb8cae530bnet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
d95cea9298benet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
033370ffb3c9net: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
8141a2dc7008net: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
033370ffb3c9net: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
d95cea9298benet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
8141a2dc7008net: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
8fdbb6262a4anet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index 00dbcd4d28e680..34d9333e4229f3 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
b3cb8cae530bnet: rds: fix MR cleanup on copy error
2 files changed · +0 −10
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
net/rds/rdma.c+0 −5 modifieddiff --git a/net/rds/rdma.c b/net/rds/rdma.c index aa6465dc742c2d..61fb6e45281bf1 100644 --- a/net/rds/rdma.c +++ b/net/rds/rdma.c @@ -326,10 +326,6 @@ static int __rds_rdma_map(struct rds_sock *rs, struct rds_get_mr_args *args, if (args->cookie_addr && put_user(cookie, (u64 __user *)(unsigned long)args->cookie_addr)) { - if (!need_odp) { - unpin_user_pages(pages, nr_pages); - kfree(sg); - } ret = -EFAULT; goto out; } -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Double-free of pages and scatter-gather list in __rds_rdma_map() when put_user() fails after get_mr() has transferred ownership to the transport."
Attack vector
An attacker with access to the RDS RDMA interface can trigger a use-after-free / double-free by invoking `__rds_rdma_map()` and arranging for the `put_user()` call that copies the MR cookie back to userspace to fail (e.g., by passing an invalid `cookie_addr` pointer). After `get_mr()` succeeds, ownership of the scatter-gather list and pages has been transferred to the transport. The old error path then freed those resources a second time, while the transport still held references, leading to memory corruption.
Affected code
The vulnerability is in the `__rds_rdma_map()` function in `net/rds/rdma.c` [patch_id=2660099]. The faulty code path is the `put_user()` failure branch at line 326, which unconditionally calls `unpin_user_pages(pages, nr_pages)` and `kfree(sg)` when `need_odp` is false.
What the fix does
The patch removes the three lines in the `put_user()` failure branch that called `unpin_user_pages(pages, nr_pages)` and `kfree(sg)` when `need_odp` was false [patch_id=2660099]. After `get_mr()` succeeds, the transport owns the pages and sg list, so the error path must not unpin or free them. Instead, the code now simply sets `ret = -EFAULT` and jumps to the `out` label, where the existing final cleanup path handles MR teardown correctly through `rds_rdma_drop_mr_refs()` and related logic.
Preconditions
- inputAttacker must be able to invoke the RDS RDMA mapping ioctl (__rds_rdma_map) with a cookie_addr that causes put_user() to fail
- configThe system must have the RDS protocol module loaded and RDMA transport configured
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/033370ffb3c9c0264d19f8ba9ef769523266589anvd
- git.kernel.org/stable/c/8141a2dc70080eda1aedc0389ed2db2b292af5bdnvd
- git.kernel.org/stable/c/8fdbb6262a4a3ed44a0830a7793903b54bb27bdcnvd
- git.kernel.org/stable/c/b3cb8cae530b2727d8245684148bb49425f6765cnvd
- git.kernel.org/stable/c/d95cea9298be1ba8876e3f156be96d3a492085canvd
News mentions
0No linked articles in our index yet.