The patch titled Subject: unshare: unsharing a thread does not require unsharing a vm has been removed from the -mm tree. Its filename was unshare-unsharing-a-thread-does-not-require-unsharing-a-vm.patch This patch was dropped because it was merged into mainline or a subsystem tree ------------------------------------------------------ From: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> Subject: unshare: unsharing a thread does not require unsharing a vm Checking mm_users > 1 does not mean a process is multithreaded. For example, reading /proc/PID/maps temporarily increments mm_users, allowing other processes to (accidentally) interfere with unshare() calls. This fixes observed failures of unshare(CLONE_NEWUSER) incorrectly returning EINVAL if another processes happened to be simultaneously reading the maps file. The logic in the initial commit of unshare made creating a new thread group for a process contingent upon creating a new memory address space for that process. That is wrong. Two separate processes in different thread groups can share a memory address space and clone allows creation of such proceses. This is significant because it was observed that mm_users > 1 does not mean that a process is multi-threaded, as reading /proc/PID/maps temporarily increments mm_users, which allows other processes to (accidentally) interfere with unshare() calls. Correct the check in check_unshare_flags() to test for !thread_group_empty() for CLONE_THREAD, CLONE_SIGHAND, and CLONE_VM. For sighand->count > 1 for CLONE_SIGHAND and CLONE_VM. For !current_is_single_threaded instead of mm_users > 1 for CLONE_VM. By using the correct checks in unshare this removes the possibility of an accidental denial of service attack. Additionally using the correct checks in unshare ensures that only an explicit unshare(CLONE_VM) can possibly trigger the slow path of current_is_single_threaded(). As an explict unshare(CLONE_VM) is pointless it is not expected there are many applications that make that call. Fixes: b2e0d98705e60e45 ("userns: Implement unshare of the user namespace") Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> Reported-by: Ricky Zhou <rickyz@xxxxxxxxxxxx> Reported-by: Kees Cook <keescook@xxxxxxxxxxxx> Reviewed-by: Kees Cook <keescook@xxxxxxxxxxxx> Cc: Oleg Nesterov <oleg@xxxxxxxxxx> Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx> Cc: Ingo Molnar <mingo@xxxxxxxxxx> Cc: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx> Cc: Vladimir Davydov <vdavydov@xxxxxxxxxxxxx> Cc: Julien Tinnes <jln@xxxxxxxxxx> Cc: <stable@xxxxxxxxxxxxxxx> [3.9+] Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> --- kernel/fork.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff -puN kernel/fork.c~unshare-unsharing-a-thread-does-not-require-unsharing-a-vm kernel/fork.c --- a/kernel/fork.c~unshare-unsharing-a-thread-does-not-require-unsharing-a-vm +++ a/kernel/fork.c @@ -1873,13 +1873,21 @@ static int check_unshare_flags(unsigned CLONE_NEWUSER|CLONE_NEWPID)) return -EINVAL; /* - * Not implemented, but pretend it works if there is nothing to - * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND - * needs to unshare vm. + * Not implemented, but pretend it works if there is nothing + * to unshare. Note that unsharing the address space or the + * signal handlers also need to unshare the signal queues (aka + * CLONE_THREAD). */ if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) { - /* FIXME: get_task_mm() increments ->mm_users */ - if (atomic_read(¤t->mm->mm_users) > 1) + if (!thread_group_empty(current)) + return -EINVAL; + } + if (unshare_flags & (CLONE_SIGHAND | CLONE_VM)) { + if (atomic_read(¤t->sighand->count) > 1) + return -EINVAL; + } + if (unshare_flags & CLONE_VM) { + if (!current_is_single_threaded()) return -EINVAL; } @@ -1948,16 +1956,16 @@ SYSCALL_DEFINE1(unshare, unsigned long, if (unshare_flags & CLONE_NEWUSER) unshare_flags |= CLONE_THREAD | CLONE_FS; /* - * If unsharing a thread from a thread group, must also unshare vm. - */ - if (unshare_flags & CLONE_THREAD) - unshare_flags |= CLONE_VM; - /* * If unsharing vm, must also unshare signal handlers. */ if (unshare_flags & CLONE_VM) unshare_flags |= CLONE_SIGHAND; /* + * If unsharing a signal handlers, must also unshare the signal queues. + */ + if (unshare_flags & CLONE_SIGHAND) + unshare_flags |= CLONE_THREAD; + /* * If unsharing namespace, must also unshare filesystem information. */ if (unshare_flags & CLONE_NEWNS) _ Patches currently in -mm which might be from ebiederm@xxxxxxxxxxxx are proc-change-proc_subdir_lock-to-a-rwlock.patch kexec-split-kexec_file-syscall-code-to-kexec_filec.patch kexec-split-kexec_load-syscall-from-kexec-core-code.patch align-crash_notes-allocation-to-make-it-be-inside-one-physical-page.patch kdump-vmcoreinfo-report-actual-value-of-phys_base.patch sysctl-fix-int-unsigned-long-assignments-in-int_min-case.patch linux-next.patch -- To unsubscribe from this list: send the line "unsubscribe stable" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html