* [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset()
@ 2026-10-17 3:35 Nickolai Zeldovich
2026-10-17 3:59 ` Serge E. Hallyn
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Nickolai Zeldovich @ 2026-10-17 3:35 UTC (permalink / raw)
To: Christian Brauner
Cc: Jeff Layton, Mickaël Salaün, Serge Hallyn,
linux-kernel, Nickolai Zeldovich
A task that calls setns() with CLONE_NEWUSER together with CLONE_NEWNS
and at least one more namespace flag leaks a struct cred (and the
user_namespace, ucounts and group_info references it holds) whenever the
call fails at the fs_struct copy. On a kernel with CONFIG_USER_NS the
leak is one cred per failed setns(); under memory pressure, or driven by
a fault-injecting attacker, it accumulates and pins user namespaces that
can then never be freed.
prepare_nsset() sets nsset->flags only on the success path:
if (flags & CLONE_NEWUSER)
nsset->cred = prepare_creds();
...
} else if (flags & CLONE_NEWNS) {
nsset->fs = copy_fs_struct(me->fs);
if (!nsset->fs)
goto out; /* flags still 0 here */
}
nsset->flags = flags;
return 0;
out:
put_nsset(nsset);
put_nsset() decides what to release from nsset->flags:
if (flags & CLONE_NEWUSER)
put_cred(nsset_cred(nsset));
On the copy_fs_struct() failure arm prepare_creds() has already
allocated the cred, but nsset->flags is still the zero from the caller's
"struct nsset nsset = {}", so put_nsset() does not see CLONE_NEWUSER and
never drops it. The caller (__do_sys_setns) returns the error without
touching nsset, so the cred is lost.
Set nsset->flags at the start of prepare_nsset(), before anything can
fail, so every error exit through put_nsset() releases what was taken.
commit_nsset(), which clears nsset->cred on success, is unaffected.
Reproduced on x86_64 under QEMU with failslab aimed at the fs_struct
cache and kmemleak enabled: 500 calls of
setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) each fail with
-ENOMEM and leave 851 unreferenced cred objects allocated from
prepare_creds() via setns() before the fix, and none after (the program
is in the notes below).
Found by formal verification of kernel/nsproxy.c.
Fixes: 303cc571d107 ("nsproxy: attach to namespaces via pidfds")
Assisted-by: LLM
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
---
Notes:
The leak is reachable from userspace on a CONFIG_USER_NS kernel:
setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) where
copy_fs_struct() returns NULL. copy_fs_struct() allocates from fs_cachep
("fs_cache"), so CONFIG_FAILSLAB with that cache's per-slab failslab knob
forces the error arm deterministically; otherwise it needs real memory
pressure. The leaked object is the cred from prepare_creds() and the
user_namespace, ucounts and group_info references it holds; CONFIG_USER_NS
LSM blobs hung off that cred (security_prepare_creds) leak with it.
Reproduced under QEMU (x86_64 defconfig + CONFIG_USER_NS, CONFIG_FAILSLAB,
CONFIG_FAULT_INJECTION_DEBUG_FS, CONFIG_SLUB_DEBUG, CONFIG_DEBUG_KMEMLEAK;
booted with slab_nomerge so fs_cache keeps refcount 1 for the per-cache
knob). A task marks fs_cache for failslab, marks itself (make-it-fail), and
calls setns() 500 times; every call returns -ENOMEM. On 70dd4ee72eb2 the
"cred" slab grows (active 120 -> 660) and kmemleak reports the leaked creds:
unreferenced object 0xff4bc4614195c000 (size 184):
comm "test", ...
backtrace:
kmem_cache_alloc_noprof+0x188/0x3c0
prepare_creds+0x22/0x280
__se_sys_setns+0x131/0x710 (prepare_nsset is inlined here)
do_syscall_64+0x109/0x330
kmemleak: before 851 unreferenced cred objects via prepare_creds<-setns,
RESULT FAIL; after 0, RESULT PASS. (The SLUB /proc/slabinfo active count is
approximate, so kmemleak is the signal.)
Reproducer (needs CONFIG_FAILSLAB + CONFIG_DEBUG_KMEMLEAK, boot slab_nomerge,
run as root):
cc -O2 -Wall -static -o setns_cred_leak setns_cred_leak.c
./setns_cred_leak # before: RESULT: FAIL; after: RESULT: PASS
// SPDX-License-Identifier: GPL-2.0
/* setns() prepare_nsset() cred leak: fail copy_fs_struct() with failslab. */
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/syscall.h>
#define N 500
static void w(const char *path, const char *val)
{
int fd = open(path, O_WRONLY);
if (fd >= 0) {
if (write(fd, val, strlen(val)) < 0)
perror(path);
close(fd);
}
}
int main(void)
{
char line[512];
int pidfd, i, enomem = 0, leaks = 0;
FILE *f;
mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
w("/sys/kernel/slab/fs_cache/failslab", "1"); /* needs slab_nomerge */
w("/sys/kernel/debug/failslab/cache-filter", "1");
w("/sys/kernel/debug/failslab/task-filter", "1");
w("/sys/kernel/debug/failslab/ignore-gfp-wait", "0"); /* fail GFP_KERNEL */
w("/sys/kernel/debug/failslab/probability", "100");
w("/sys/kernel/debug/failslab/interval", "1");
w("/sys/kernel/debug/failslab/times", "-1");
pidfd = syscall(SYS_pidfd_open, getpid(), 0);
w("/proc/self/make-it-fail", "1");
for (i = 0; i < N; i++)
if (setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) &&
errno == ENOMEM)
enomem++;
w("/proc/self/make-it-fail", "0");
w("/sys/kernel/slab/fs_cache/failslab", "0");
w("/sys/kernel/debug/failslab/times", "0");
w("/sys/kernel/debug/kmemleak", "scan");
sleep(5);
w("/sys/kernel/debug/kmemleak", "scan");
sleep(1);
f = fopen("/sys/kernel/debug/kmemleak", "r");
while (f && fgets(line, sizeof(line), f))
if (strstr(line, "prepare_creds")) /* one per leaked cred */
leaks++;
if (f)
fclose(f);
printf("setns x%d: ENOMEM=%d; kmemleak cred leaks=%d\n", N, enomem, leaks);
printf("RESULT: %s\n", leaks > N / 10 ? "FAIL" : "PASS");
return leaks > N / 10;
}
kernel/nsproxy.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 3fb1595a4a59..028019f3e387 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -355,6 +355,7 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
{
struct task_struct *me = current;
+ nsset->flags = flags;
nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs);
if (IS_ERR(nsset->nsproxy))
return PTR_ERR(nsset->nsproxy);
@@ -375,7 +376,6 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
goto out;
}
- nsset->flags = flags;
return 0;
out:
base-commit: 70dd4ee72eb2d5793c62e71c14e3aba78e5a4e56
--
2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset()
2026-10-17 3:35 [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset() Nickolai Zeldovich
@ 2026-10-17 3:59 ` Serge E. Hallyn
2026-10-17 9:46 ` Bradley Morgan
2026-10-21 14:02 ` Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Serge E. Hallyn @ 2026-10-17 3:59 UTC (permalink / raw)
To: Nickolai Zeldovich
Cc: Christian Brauner, Jeff Layton, Mickaël Salaün, linux-kernel
On Fri, Oct 16, 2026 at 11:35:38PM -0400, Nickolai Zeldovich wrote:
> A task that calls setns() with CLONE_NEWUSER together with CLONE_NEWNS
> and at least one more namespace flag leaks a struct cred (and the
> user_namespace, ucounts and group_info references it holds) whenever the
> call fails at the fs_struct copy. On a kernel with CONFIG_USER_NS the
> leak is one cred per failed setns(); under memory pressure, or driven by
> a fault-injecting attacker, it accumulates and pins user namespaces that
> can then never be freed.
>
> prepare_nsset() sets nsset->flags only on the success path:
>
> if (flags & CLONE_NEWUSER)
> nsset->cred = prepare_creds();
> ...
> } else if (flags & CLONE_NEWNS) {
> nsset->fs = copy_fs_struct(me->fs);
> if (!nsset->fs)
> goto out; /* flags still 0 here */
> }
> nsset->flags = flags;
> return 0;
> out:
> put_nsset(nsset);
>
> put_nsset() decides what to release from nsset->flags:
>
> if (flags & CLONE_NEWUSER)
> put_cred(nsset_cred(nsset));
>
> On the copy_fs_struct() failure arm prepare_creds() has already
> allocated the cred, but nsset->flags is still the zero from the caller's
> "struct nsset nsset = {}", so put_nsset() does not see CLONE_NEWUSER and
> never drops it. The caller (__do_sys_setns) returns the error without
> touching nsset, so the cred is lost.
>
> Set nsset->flags at the start of prepare_nsset(), before anything can
> fail, so every error exit through put_nsset() releases what was taken.
> commit_nsset(), which clears nsset->cred on success, is unaffected.
>
> Reproduced on x86_64 under QEMU with failslab aimed at the fs_struct
> cache and kmemleak enabled: 500 calls of
> setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) each fail with
> -ENOMEM and leave 851 unreferenced cred objects allocated from
> prepare_creds() via setns() before the fix, and none after (the program
> is in the notes below).
>
> Found by formal verification of kernel/nsproxy.c.
>
> Fixes: 303cc571d107 ("nsproxy: attach to namespaces via pidfds")
> Assisted-by: LLM
> Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
Reviewed-by: Serge Hallyn <serge@hallyn.com>
thanks,
-serge
> ---
>
> Notes:
> The leak is reachable from userspace on a CONFIG_USER_NS kernel:
> setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) where
> copy_fs_struct() returns NULL. copy_fs_struct() allocates from fs_cachep
> ("fs_cache"), so CONFIG_FAILSLAB with that cache's per-slab failslab knob
> forces the error arm deterministically; otherwise it needs real memory
> pressure. The leaked object is the cred from prepare_creds() and the
> user_namespace, ucounts and group_info references it holds; CONFIG_USER_NS
> LSM blobs hung off that cred (security_prepare_creds) leak with it.
>
> Reproduced under QEMU (x86_64 defconfig + CONFIG_USER_NS, CONFIG_FAILSLAB,
> CONFIG_FAULT_INJECTION_DEBUG_FS, CONFIG_SLUB_DEBUG, CONFIG_DEBUG_KMEMLEAK;
> booted with slab_nomerge so fs_cache keeps refcount 1 for the per-cache
> knob). A task marks fs_cache for failslab, marks itself (make-it-fail), and
> calls setns() 500 times; every call returns -ENOMEM. On 70dd4ee72eb2 the
> "cred" slab grows (active 120 -> 660) and kmemleak reports the leaked creds:
>
> unreferenced object 0xff4bc4614195c000 (size 184):
> comm "test", ...
> backtrace:
> kmem_cache_alloc_noprof+0x188/0x3c0
> prepare_creds+0x22/0x280
> __se_sys_setns+0x131/0x710 (prepare_nsset is inlined here)
> do_syscall_64+0x109/0x330
>
> kmemleak: before 851 unreferenced cred objects via prepare_creds<-setns,
> RESULT FAIL; after 0, RESULT PASS. (The SLUB /proc/slabinfo active count is
> approximate, so kmemleak is the signal.)
>
> Reproducer (needs CONFIG_FAILSLAB + CONFIG_DEBUG_KMEMLEAK, boot slab_nomerge,
> run as root):
> cc -O2 -Wall -static -o setns_cred_leak setns_cred_leak.c
> ./setns_cred_leak # before: RESULT: FAIL; after: RESULT: PASS
>
> // SPDX-License-Identifier: GPL-2.0
> /* setns() prepare_nsset() cred leak: fail copy_fs_struct() with failslab. */
> #define _GNU_SOURCE
> #include <sched.h>
> #include <stdio.h>
> #include <string.h>
> #include <unistd.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <sys/mount.h>
> #include <sys/syscall.h>
>
> #define N 500
>
> static void w(const char *path, const char *val)
> {
> int fd = open(path, O_WRONLY);
> if (fd >= 0) {
> if (write(fd, val, strlen(val)) < 0)
> perror(path);
> close(fd);
> }
> }
>
> int main(void)
> {
> char line[512];
> int pidfd, i, enomem = 0, leaks = 0;
> FILE *f;
>
> mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
> w("/sys/kernel/slab/fs_cache/failslab", "1"); /* needs slab_nomerge */
> w("/sys/kernel/debug/failslab/cache-filter", "1");
> w("/sys/kernel/debug/failslab/task-filter", "1");
> w("/sys/kernel/debug/failslab/ignore-gfp-wait", "0"); /* fail GFP_KERNEL */
> w("/sys/kernel/debug/failslab/probability", "100");
> w("/sys/kernel/debug/failslab/interval", "1");
> w("/sys/kernel/debug/failslab/times", "-1");
>
> pidfd = syscall(SYS_pidfd_open, getpid(), 0);
> w("/proc/self/make-it-fail", "1");
> for (i = 0; i < N; i++)
> if (setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) &&
> errno == ENOMEM)
> enomem++;
> w("/proc/self/make-it-fail", "0");
> w("/sys/kernel/slab/fs_cache/failslab", "0");
> w("/sys/kernel/debug/failslab/times", "0");
>
> w("/sys/kernel/debug/kmemleak", "scan");
> sleep(5);
> w("/sys/kernel/debug/kmemleak", "scan");
> sleep(1);
> f = fopen("/sys/kernel/debug/kmemleak", "r");
> while (f && fgets(line, sizeof(line), f))
> if (strstr(line, "prepare_creds")) /* one per leaked cred */
> leaks++;
> if (f)
> fclose(f);
>
> printf("setns x%d: ENOMEM=%d; kmemleak cred leaks=%d\n", N, enomem, leaks);
> printf("RESULT: %s\n", leaks > N / 10 ? "FAIL" : "PASS");
> return leaks > N / 10;
> }
>
> kernel/nsproxy.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> index 3fb1595a4a59..028019f3e387 100644
> --- a/kernel/nsproxy.c
> +++ b/kernel/nsproxy.c
> @@ -355,6 +355,7 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> {
> struct task_struct *me = current;
>
> + nsset->flags = flags;
> nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs);
> if (IS_ERR(nsset->nsproxy))
> return PTR_ERR(nsset->nsproxy);
> @@ -375,7 +376,6 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> goto out;
> }
>
> - nsset->flags = flags;
> return 0;
>
> out:
>
> base-commit: 70dd4ee72eb2d5793c62e71c14e3aba78e5a4e56
> --
> 2.53.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset()
2026-10-17 3:35 [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset() Nickolai Zeldovich
2026-10-17 3:59 ` Serge E. Hallyn
@ 2026-10-17 9:46 ` Bradley Morgan
2026-10-21 14:02 ` Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Bradley Morgan @ 2026-10-17 9:46 UTC (permalink / raw)
To: nickolai; +Cc: brauner, jlayton, linux-kernel, mic, serge
On 17 October 2026 04:35:38 BST, Nickolai Zeldovich
<nickolai@csail.mit.edu> wrote:
>A task that calls setns() with CLONE_NEWUSER together with CLONE_NEWNS
>and at least one more namespace flag leaks a struct cred (and the
>user_namespace, ucounts and group_info references it holds) whenever the
>call fails at the fs_struct copy. On a kernel with CONFIG_USER_NS the
>leak is one cred per failed setns(); under memory pressure, or driven by
>a fault-injecting attacker, it accumulates and pins user namespaces that
>can then never be freed.
>
>prepare_nsset() sets nsset->flags only on the success path:
>
> if (flags & CLONE_NEWUSER)
> nsset->cred = prepare_creds();
> ...
> } else if (flags & CLONE_NEWNS) {
> nsset->fs = copy_fs_struct(me->fs);
> if (!nsset->fs)
> goto out; /* flags still 0 here */
> }
> nsset->flags = flags;
> return 0;
>out:
> put_nsset(nsset);
>
>put_nsset() decides what to release from nsset->flags:
>
> if (flags & CLONE_NEWUSER)
> put_cred(nsset_cred(nsset));
>
>On the copy_fs_struct() failure arm prepare_creds() has already
>allocated the cred, but nsset->flags is still the zero from the caller's
>"struct nsset nsset = {}", so put_nsset() does not see CLONE_NEWUSER and
>never drops it. The caller (__do_sys_setns) returns the error without
>touching nsset, so the cred is lost.
>
>Set nsset->flags at the start of prepare_nsset(), before anything can
>fail, so every error exit through put_nsset() releases what was taken.
>commit_nsset(), which clears nsset->cred on success, is unaffected.
>
>Reproduced on x86_64 under QEMU with failslab aimed at the fs_struct
>cache and kmemleak enabled: 500 calls of
>setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) each fail with
>-ENOMEM and leave 851 unreferenced cred objects allocated from
>prepare_creds() via setns() before the fix, and none after (the program
>is in the notes below).
>
>Found by formal verification of kernel/nsproxy.c.
>
>Fixes: 303cc571d107 ("nsproxy: attach to namespaces via pidfds")
>Assisted-by: LLM
Reviewed-by: Bradley Morgan <brads@mainlining.org>
>Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
>---
>
>Notes:
> The leak is reachable from userspace on a CONFIG_USER_NS kernel:
> setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) where
> copy_fs_struct() returns NULL. copy_fs_struct() allocates from fs_cachep
> ("fs_cache"), so CONFIG_FAILSLAB with that cache's per-slab failslab knob
> forces the error arm deterministically; otherwise it needs real memory
> pressure. The leaked object is the cred from prepare_creds() and the
> user_namespace, ucounts and group_info references it holds; CONFIG_USER_NS
> LSM blobs hung off that cred (security_prepare_creds) leak with it.
>
> Reproduced under QEMU (x86_64 defconfig + CONFIG_USER_NS, CONFIG_FAILSLAB,
> CONFIG_FAULT_INJECTION_DEBUG_FS, CONFIG_SLUB_DEBUG, CONFIG_DEBUG_KMEMLEAK;
> booted with slab_nomerge so fs_cache keeps refcount 1 for the per-cache
> knob). A task marks fs_cache for failslab, marks itself (make-it-fail), and
> calls setns() 500 times; every call returns -ENOMEM. On 70dd4ee72eb2 the
> "cred" slab grows (active 120 -> 660) and kmemleak reports the leaked creds:
>
> unreferenced object 0xff4bc4614195c000 (size 184):
> comm "test", ...
> backtrace:
> kmem_cache_alloc_noprof+0x188/0x3c0
> prepare_creds+0x22/0x280
> __se_sys_setns+0x131/0x710 (prepare_nsset is inlined here)
> do_syscall_64+0x109/0x330
>
> kmemleak: before 851 unreferenced cred objects via prepare_creds<-setns,
> RESULT FAIL; after 0, RESULT PASS. (The SLUB /proc/slabinfo active count is
> approximate, so kmemleak is the signal.)
>
> Reproducer (needs CONFIG_FAILSLAB + CONFIG_DEBUG_KMEMLEAK, boot slab_nomerge,
> run as root):
> cc -O2 -Wall -static -o setns_cred_leak setns_cred_leak.c
> ./setns_cred_leak # before: RESULT: FAIL; after: RESULT: PASS
>
> // SPDX-License-Identifier: GPL-2.0
> /* setns() prepare_nsset() cred leak: fail copy_fs_struct() with failslab. */
> #define _GNU_SOURCE
> #include <sched.h>
> #include <stdio.h>
> #include <string.h>
> #include <unistd.h>
> #include <errno.h>
> #include <fcntl.h>
> #include <sys/mount.h>
> #include <sys/syscall.h>
>
> #define N 500
>
> static void w(const char *path, const char *val)
> {
> int fd = open(path, O_WRONLY);
> if (fd >= 0) {
> if (write(fd, val, strlen(val)) < 0)
> perror(path);
> close(fd);
> }
> }
>
> int main(void)
> {
> char line[512];
> int pidfd, i, enomem = 0, leaks = 0;
> FILE *f;
>
> mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL);
> w("/sys/kernel/slab/fs_cache/failslab", "1"); /* needs slab_nomerge */
> w("/sys/kernel/debug/failslab/cache-filter", "1");
> w("/sys/kernel/debug/failslab/task-filter", "1");
> w("/sys/kernel/debug/failslab/ignore-gfp-wait", "0"); /* fail GFP_KERNEL */
> w("/sys/kernel/debug/failslab/probability", "100");
> w("/sys/kernel/debug/failslab/interval", "1");
> w("/sys/kernel/debug/failslab/times", "-1");
>
> pidfd = syscall(SYS_pidfd_open, getpid(), 0);
> w("/proc/self/make-it-fail", "1");
> for (i = 0; i < N; i++)
> if (setns(pidfd, CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWUTS) &&
> errno == ENOMEM)
> enomem++;
> w("/proc/self/make-it-fail", "0");
> w("/sys/kernel/slab/fs_cache/failslab", "0");
> w("/sys/kernel/debug/failslab/times", "0");
>
> w("/sys/kernel/debug/kmemleak", "scan");
> sleep(5);
> w("/sys/kernel/debug/kmemleak", "scan");
> sleep(1);
> f = fopen("/sys/kernel/debug/kmemleak", "r");
> while (f && fgets(line, sizeof(line), f))
> if (strstr(line, "prepare_creds")) /* one per leaked cred */
> leaks++;
> if (f)
> fclose(f);
>
> printf("setns x%d: ENOMEM=%d; kmemleak cred leaks=%d\n", N, enomem, leaks);
> printf("RESULT: %s\n", leaks > N / 10 ? "FAIL" : "PASS");
> return leaks > N / 10;
> }
>
> kernel/nsproxy.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
>index 3fb1595a4a59..028019f3e387 100644
>--- a/kernel/nsproxy.c
>+++ b/kernel/nsproxy.c
>@@ -355,6 +355,7 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> {
> struct task_struct *me = current;
>
>+ nsset->flags = flags;
> nsset->nsproxy = create_new_namespaces(0, me, current_user_ns(), me->fs);
> if (IS_ERR(nsset->nsproxy))
> return PTR_ERR(nsset->nsproxy);
>@@ -375,7 +376,6 @@ static int prepare_nsset(unsigned flags, struct nsset *nsset)
> goto out;
> }
>
>- nsset->flags = flags;
> return 0;
>
> out:
>
>base-commit: 70dd4ee72eb2d5793c62e71c14e3aba78e5a4e56
>
--- Thanks!
"I'm not a very positive person" - Linus torvalds
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset()
2026-10-17 3:35 [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset() Nickolai Zeldovich
2026-10-17 3:59 ` Serge E. Hallyn
2026-10-17 9:46 ` Bradley Morgan
@ 2026-10-21 14:02 ` Christian Brauner
2 siblings, 0 replies; 4+ messages in thread
From: Christian Brauner @ 2026-10-21 14:02 UTC (permalink / raw)
To: Nickolai Zeldovich
Cc: Jeff Layton, Mickaël Salaün, Serge Hallyn, linux-kernel
On Fri, 16 Oct 2026 23:35:38 -0400, Nickolai Zeldovich wrote:
> A task that calls setns() with CLONE_NEWUSER together with CLONE_NEWNS
> and at least one more namespace flag leaks a struct cred (and the
> user_namespace, ucounts and group_info references it holds) whenever the
> call fails at the fs_struct copy. On a kernel with CONFIG_USER_NS the
> leak is one cred per failed setns(); under memory pressure, or driven by
> a fault-injecting attacker, it accumulates and pins user namespaces that
> can then never be freed.
>
> [...]
Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.
Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.
It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.
Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.
tree: https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes
[1/1] nsproxy: Fix cred leak in the error path of prepare_nsset()
https://git.kernel.org/vfs/vfs/c/486e02e391e6
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-21 14:02 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-17 3:35 [PATCH v1] nsproxy: Fix cred leak in the error path of prepare_nsset() Nickolai Zeldovich
2026-10-17 3:59 ` Serge E. Hallyn
2026-10-17 9:46 ` Bradley Morgan
2026-10-21 14:02 ` Christian Brauner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®