mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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
  2026-10-17  9:46 ` Bradley Morgan
  0 siblings, 2 replies; 3+ 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] 3+ 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
  1 sibling, 0 replies; 3+ 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] 3+ 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
  1 sibling, 0 replies; 3+ 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] 3+ messages in thread

end of thread, other threads:[~2026-10-17  9:47 UTC | newest]

Thread overview: 3+ 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

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®