mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
nsproxy: Fix cred leak in the error path of prepare_nsset()
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: 303cc571d1 ("nsproxy: attach to namespaces via pidfds")
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
Link: https://patch.msgid.link/20261017033538.793280-1-nickolai@csail.mit.edu
Reviewed-by: Serge Hallyn <serge@hallyn.com>
Reviewed-by: Bradley Morgan <brads@mainlining.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
4bd3a0e8e5
commit
e08601d3a7
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user