Patch "unshare: Unsharing a thread does not require unsharing a vm" has been added to the 4.2-stable tree
Subsystems (4)
This is a note to let you know that I've just added the patch titled
unshare: Unsharing a thread does not require unsharing a vm
to the 4.2-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
unshare-unsharing-a-thread-does-not-require-unsharing-a-vm.patch
and it can be found in the queue-4.2 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
12c641ab8270f787dfcce08b5f20ce8b65008096Mon Sep 17 00:00:00 2001
From: "Eric W. Biederman" <[email protected]>
Date: Mon, 10 Aug 2015 17:35:07 -0500
Subject: unshare: Unsharing a thread does not require unsharing a vm
From: "Eric W. Biederman" <[email protected]>
commit 12c641ab8270f787dfcce08b5f20ce8b65008096 upstream.
In 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.
--- kernel/fork.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-)
kernel/fork.c +18 -10
@@ -1871,13 +1871,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 ; }
@@ -1946,16 +1954,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 stable-queue which might be from [email protected] are
queue-4.2/unshare-unsharing-a-thread-does-not-require-unsharing-a-vm.patch