linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] get/set_dumpable() cleanups and theoretical fix
@ 2013-11-19 14:43 Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself Oleg Nesterov
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Oleg Nesterov @ 2013-11-19 14:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Alex Kelly, Eric W. Biederman, Josh Triplett, Kees Cook,
	Petr Matousek, Vasily Kulikov, linux-arch, linux-kernel

Hello.

The only change is WARN_ON() in 2/3 to catch the insane callers,
I preserved the acks from Kees.

Oleg.

 fs/coredump.c         |    1 -
 fs/coredump.h         |    6 ----
 fs/exec.c             |   62 ++++++------------------------------------------
 include/linux/sched.h |   25 ++++++++++++++-----
 4 files changed, 26 insertions(+), 68 deletions(-)


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself
  2013-11-19 14:43 [PATCH v2 0/3] get/set_dumpable() cleanups and theoretical fix Oleg Nesterov
@ 2013-11-19 14:43 ` Oleg Nesterov
  2013-11-19 22:20   ` Andrew Morton
  2013-11-19 14:43 ` [PATCH v2 2/3] kill MMF_DUMPABLE and MMF_DUMP_SECURELY Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 3/3] make __get_dumpable/get_dumpable inline, kill fs/coredump.h Oleg Nesterov
  2 siblings, 1 reply; 5+ messages in thread
From: Oleg Nesterov @ 2013-11-19 14:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Alex Kelly, Eric W. Biederman, Josh Triplett, Kees Cook,
	Petr Matousek, Vasily Kulikov, linux-arch, linux-kernel

set_dumpable() updates MMF_DUMPABLE_MASK in a non-trivial way to
ensure that get_dumpable() can't observe the intermediate state,
but this all can't help if multiple threads call set_dumpable()
at the same time.

And in theory commit_creds()->set_dumpable(SUID_DUMP_ROOT) racing
with sys_prctl()->set_dumpable(SUID_DUMP_DISABLE) can result in
SUID_DUMP_USER.

Change this code to update both bits atomically via cmpxchg().

Note: this assumes that it is safe to mix bitops and cmpxchg. IOW,
if, say, an architecture implements cmpxchg() using the locking
(like arch/parisc/lib/bitops.c does), then it should use the same
locks for set_bit/etc.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Kees Cook <keescook@chromium.org>
---
 fs/exec.c |   49 +++++++++++++++----------------------------------
 1 files changed, 15 insertions(+), 34 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index bb8afc1..613c9dc 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1621,43 +1621,24 @@ EXPORT_SYMBOL(set_binfmt);
 
 /*
  * set_dumpable converts traditional three-value dumpable to two flags and
- * stores them into mm->flags.  It modifies lower two bits of mm->flags, but
- * these bits are not changed atomically.  So get_dumpable can observe the
- * intermediate state.  To avoid doing unexpected behavior, get get_dumpable
- * return either old dumpable or new one by paying attention to the order of
- * modifying the bits.
- *
- * dumpable |   mm->flags (binary)
- * old  new | initial interim  final
- * ---------+-----------------------
- *  0    1  |   00      01      01
- *  0    2  |   00      10(*)   11
- *  1    0  |   01      00      00
- *  1    2  |   01      11      11
- *  2    0  |   11      10(*)   00
- *  2    1  |   11      11      01
- *
- * (*) get_dumpable regards interim value of 10 as 11.
+ * stores them into mm->flags.
  */
 void set_dumpable(struct mm_struct *mm, int value)
 {
-	switch (value) {
-	case SUID_DUMP_DISABLE:
-		clear_bit(MMF_DUMPABLE, &mm->flags);
-		smp_wmb();
-		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
-		break;
-	case SUID_DUMP_USER:
-		set_bit(MMF_DUMPABLE, &mm->flags);
-		smp_wmb();
-		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
-		break;
-	case SUID_DUMP_ROOT:
-		set_bit(MMF_DUMP_SECURELY, &mm->flags);
-		smp_wmb();
-		set_bit(MMF_DUMPABLE, &mm->flags);
-		break;
-	}
+	unsigned long old, new;
+
+	do {
+		old = ACCESS_ONCE(mm->flags);
+		new = old & ~MMF_DUMPABLE_MASK;
+
+		switch (value) {
+		case SUID_DUMP_ROOT:
+			new |= (1 << MMF_DUMP_SECURELY);
+		case SUID_DUMP_USER:
+			new |= (1<< MMF_DUMPABLE);
+		}
+
+	} while (cmpxchg(&mm->flags, old, new) != old);
 }
 
 int __get_dumpable(unsigned long mm_flags)
-- 
1.5.5.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 2/3] kill MMF_DUMPABLE and MMF_DUMP_SECURELY
  2013-11-19 14:43 [PATCH v2 0/3] get/set_dumpable() cleanups and theoretical fix Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself Oleg Nesterov
@ 2013-11-19 14:43 ` Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 3/3] make __get_dumpable/get_dumpable inline, kill fs/coredump.h Oleg Nesterov
  2 siblings, 0 replies; 5+ messages in thread
From: Oleg Nesterov @ 2013-11-19 14:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Alex Kelly, Eric W. Biederman, Josh Triplett, Kees Cook,
	Petr Matousek, Vasily Kulikov, linux-arch, linux-kernel

Nobody actually needs MMF_DUMPABLE/MMF_DUMP_SECURELY, they
are only used to enforce the encoding of SUID_DUMP_* enum
in mm->flags & MMF_DUMPABLE_MASK.

Now that set_dumpable() updates both bits atomically we can
kill them and simply store the value "as is" in 2 lower bits.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Kees Cook <keescook@chromium.org>
---
 fs/exec.c             |   21 ++++++---------------
 include/linux/sched.h |    4 +---
 2 files changed, 7 insertions(+), 18 deletions(-)

diff --git a/fs/exec.c b/fs/exec.c
index 613c9dc..6ce1f86 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1620,33 +1620,24 @@ void set_binfmt(struct linux_binfmt *new)
 EXPORT_SYMBOL(set_binfmt);
 
 /*
- * set_dumpable converts traditional three-value dumpable to two flags and
- * stores them into mm->flags.
+ * set_dumpable stores three-value SUID_DUMP_* into mm->flags.
  */
 void set_dumpable(struct mm_struct *mm, int value)
 {
 	unsigned long old, new;
 
+	if (WARN_ON((unsigned)value > SUID_DUMP_ROOT))
+		return;
+
 	do {
 		old = ACCESS_ONCE(mm->flags);
-		new = old & ~MMF_DUMPABLE_MASK;
-
-		switch (value) {
-		case SUID_DUMP_ROOT:
-			new |= (1 << MMF_DUMP_SECURELY);
-		case SUID_DUMP_USER:
-			new |= (1<< MMF_DUMPABLE);
-		}
-
+		new = (old & ~MMF_DUMPABLE_MASK) | value;
 	} while (cmpxchg(&mm->flags, old, new) != old);
 }
 
 int __get_dumpable(unsigned long mm_flags)
 {
-	int ret;
-
-	ret = mm_flags & MMF_DUMPABLE_MASK;
-	return (ret > SUID_DUMP_USER) ? SUID_DUMP_ROOT : ret;
+	return mm_flags & MMF_DUMPABLE_MASK;
 }
 
 /*
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 838a3d9..828c00d 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -326,10 +326,8 @@ extern int get_dumpable(struct mm_struct *mm);
 #define SUID_DUMP_ROOT		2	/* Dump as root */
 
 /* mm flags */
-/* dumpable bits */
-#define MMF_DUMPABLE      0  /* core dump is permitted */
-#define MMF_DUMP_SECURELY 1  /* core file is readable only by root */
 
+/* for SUID_DUMP_* above */
 #define MMF_DUMPABLE_BITS 2
 #define MMF_DUMPABLE_MASK ((1 << MMF_DUMPABLE_BITS) - 1)
 
-- 
1.5.5.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH v2 3/3] make __get_dumpable/get_dumpable inline, kill fs/coredump.h
  2013-11-19 14:43 [PATCH v2 0/3] get/set_dumpable() cleanups and theoretical fix Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself Oleg Nesterov
  2013-11-19 14:43 ` [PATCH v2 2/3] kill MMF_DUMPABLE and MMF_DUMP_SECURELY Oleg Nesterov
@ 2013-11-19 14:43 ` Oleg Nesterov
  2 siblings, 0 replies; 5+ messages in thread
From: Oleg Nesterov @ 2013-11-19 14:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Alex Kelly, Eric W. Biederman, Josh Triplett, Kees Cook,
	Petr Matousek, Vasily Kulikov, linux-arch, linux-kernel

1. Remove fs/coredump.h. It is not clear why do we need it,
   it only declares __get_dumpable(), signal.c includes it
   for no reason.

2. Now that get_dumpable() and __get_dumpable() are really
   trivial make them inline in linux/sched.h.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Kees Cook <keescook@chromium.org>
---
 fs/coredump.c         |    1 -
 fs/coredump.h         |    6 ------
 fs/exec.c             |   18 ------------------
 include/linux/sched.h |   21 +++++++++++++++++----
 4 files changed, 17 insertions(+), 29 deletions(-)
 delete mode 100644 fs/coredump.h

diff --git a/fs/coredump.c b/fs/coredump.c
index 9bdeca1..4bc92c7 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -40,7 +40,6 @@
 
 #include <trace/events/task.h>
 #include "internal.h"
-#include "coredump.h"
 
 #include <trace/events/sched.h>
 
diff --git a/fs/coredump.h b/fs/coredump.h
deleted file mode 100644
index e39ff07..0000000
--- a/fs/coredump.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _FS_COREDUMP_H
-#define _FS_COREDUMP_H
-
-extern int __get_dumpable(unsigned long mm_flags);
-
-#endif
diff --git a/fs/exec.c b/fs/exec.c
index 6ce1f86..1dee8ef 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -62,7 +62,6 @@
 
 #include <trace/events/task.h>
 #include "internal.h"
-#include "coredump.h"
 
 #include <trace/events/sched.h>
 
@@ -1616,7 +1615,6 @@ void set_binfmt(struct linux_binfmt *new)
 	if (new)
 		__module_get(new->module);
 }
-
 EXPORT_SYMBOL(set_binfmt);
 
 /*
@@ -1635,22 +1633,6 @@ void set_dumpable(struct mm_struct *mm, int value)
 	} while (cmpxchg(&mm->flags, old, new) != old);
 }
 
-int __get_dumpable(unsigned long mm_flags)
-{
-	return mm_flags & MMF_DUMPABLE_MASK;
-}
-
-/*
- * This returns the actual value of the suid_dumpable flag. For things
- * that are using this for checking for privilege transitions, it must
- * test against SUID_DUMP_USER rather than treating it as a boolean
- * value.
- */
-int get_dumpable(struct mm_struct *mm)
-{
-	return __get_dumpable(mm->flags);
-}
-
 SYSCALL_DEFINE3(execve,
 		const char __user *, filename,
 		const char __user *const __user *, argv,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 828c00d..34c1903 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -317,10 +317,6 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
 static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
 #endif
 
-
-extern void set_dumpable(struct mm_struct *mm, int value);
-extern int get_dumpable(struct mm_struct *mm);
-
 #define SUID_DUMP_DISABLE	0	/* No setuid dumping */
 #define SUID_DUMP_USER		1	/* Dump as user of process */
 #define SUID_DUMP_ROOT		2	/* Dump as root */
@@ -331,6 +327,23 @@ extern int get_dumpable(struct mm_struct *mm);
 #define MMF_DUMPABLE_BITS 2
 #define MMF_DUMPABLE_MASK ((1 << MMF_DUMPABLE_BITS) - 1)
 
+extern void set_dumpable(struct mm_struct *mm, int value);
+/*
+ * This returns the actual value of the suid_dumpable flag. For things
+ * that are using this for checking for privilege transitions, it must
+ * test against SUID_DUMP_USER rather than treating it as a boolean
+ * value.
+ */
+static inline int __get_dumpable(unsigned long mm_flags)
+{
+	return mm_flags & MMF_DUMPABLE_MASK;
+}
+
+static inline int get_dumpable(struct mm_struct *mm)
+{
+	return __get_dumpable(mm->flags);
+}
+
 /* coredump filter bits */
 #define MMF_DUMP_ANON_PRIVATE	2
 #define MMF_DUMP_ANON_SHARED	3
-- 
1.5.5.1


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself
  2013-11-19 14:43 ` [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself Oleg Nesterov
@ 2013-11-19 22:20   ` Andrew Morton
  0 siblings, 0 replies; 5+ messages in thread
From: Andrew Morton @ 2013-11-19 22:20 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Alex Kelly, Eric W. Biederman, Josh Triplett, Kees Cook,
	Petr Matousek, Vasily Kulikov, linux-arch, linux-kernel

On Tue, 19 Nov 2013 15:43:15 +0100 Oleg Nesterov <oleg@redhat.com> wrote:

> set_dumpable() updates MMF_DUMPABLE_MASK in a non-trivial way to
> ensure that get_dumpable() can't observe the intermediate state,
> but this all can't help if multiple threads call set_dumpable()
> at the same time.
> 
> And in theory commit_creds()->set_dumpable(SUID_DUMP_ROOT) racing
> with sys_prctl()->set_dumpable(SUID_DUMP_DISABLE) can result in
> SUID_DUMP_USER.
> 
> Change this code to update both bits atomically via cmpxchg().
> 
> Note: this assumes that it is safe to mix bitops and cmpxchg. IOW,
> if, say, an architecture implements cmpxchg() using the locking
> (like arch/parisc/lib/bitops.c does), then it should use the same
> locks for set_bit/etc.

I suppose that is reasonable - atomic operations on memory should be
atomic wrt other classes of atomic operations on the same memory.  So
set_bit() has to "know" about concurrent cmpxchg().  It *has* to be
this way to fully emulate the hardware-based RMW operations.

parisc got that right.  I wonder if all other architectures did, and
how on earth we can communicate this to future arch developers..


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2013-11-19 22:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-11-19 14:43 [PATCH v2 0/3] get/set_dumpable() cleanups and theoretical fix Oleg Nesterov
2013-11-19 14:43 ` [PATCH v2 1/3] set_dumpable: fix the theoretical race with itself Oleg Nesterov
2013-11-19 22:20   ` Andrew Morton
2013-11-19 14:43 ` [PATCH v2 2/3] kill MMF_DUMPABLE and MMF_DUMP_SECURELY Oleg Nesterov
2013-11-19 14:43 ` [PATCH v2 3/3] make __get_dumpable/get_dumpable inline, kill fs/coredump.h Oleg Nesterov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).