aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2013-11-12 18:11:17 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-11-12 22:09:33 -0500
commitd049f74f2dbe71354d43d393ac3a188947811348 (patch)
tree7d3b6d88824a0584ae3c8d86782d69bf331d6cef
parent1c3fc3e5cc8a81d21b199cb739d5d9c51f3688f4 (diff)
exec/ptrace: fix get_dumpable() incorrect tests
The get_dumpable() return value is not boolean. Most users of the function actually want to be testing for non-SUID_DUMP_USER(1) rather than SUID_DUMP_DISABLE(0). The SUID_DUMP_ROOT(2) is also considered a protected state. Almost all places did this correctly, excepting the two places fixed in this patch. Wrong logic: if (dumpable == SUID_DUMP_DISABLE) { /* be protective */ } or if (dumpable == 0) { /* be protective */ } or if (!dumpable) { /* be protective */ } Correct logic: if (dumpable != SUID_DUMP_USER) { /* be protective */ } or if (dumpable != 1) { /* be protective */ } Without this patch, if the system had set the sysctl fs/suid_dumpable=2, a user was able to ptrace attach to processes that had dropped privileges to that user. (This may have been partially mitigated if Yama was enabled.) The macros have been moved into the file that declares get/set_dumpable(), which means things like the ia64 code can see them too. CVE-2013-2929 Reported-by: Vasily Kulikov <segoon@openwall.com> Signed-off-by: Kees Cook <keescook@chromium.org> Cc: "Luck, Tony" <tony.luck@intel.com> Cc: Oleg Nesterov <oleg@redhat.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/ia64/include/asm/processor.h2
-rw-r--r--fs/exec.c6
-rw-r--r--include/linux/binfmts.h3
-rw-r--r--include/linux/sched.h4
-rw-r--r--kernel/ptrace.c3
5 files changed, 13 insertions, 5 deletions
diff --git a/arch/ia64/include/asm/processor.h b/arch/ia64/include/asm/processor.h
index e0a899a1a8a6..5a84b3a50741 100644
--- a/arch/ia64/include/asm/processor.h
+++ b/arch/ia64/include/asm/processor.h
@@ -319,7 +319,7 @@ struct thread_struct {
319 regs->loadrs = 0; \ 319 regs->loadrs = 0; \
320 regs->r8 = get_dumpable(current->mm); /* set "don't zap registers" flag */ \ 320 regs->r8 = get_dumpable(current->mm); /* set "don't zap registers" flag */ \
321 regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \ 321 regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \
322 if (unlikely(!get_dumpable(current->mm))) { \ 322 if (unlikely(get_dumpable(current->mm) != SUID_DUMP_USER)) { \
323 /* \ 323 /* \
324 * Zap scratch regs to avoid leaking bits between processes with different \ 324 * Zap scratch regs to avoid leaking bits between processes with different \
325 * uid/privileges. \ 325 * uid/privileges. \
diff --git a/fs/exec.c b/fs/exec.c
index 2ea437e5acf4..12120620f040 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1669,6 +1669,12 @@ int __get_dumpable(unsigned long mm_flags)
1669 return (ret > SUID_DUMP_USER) ? SUID_DUMP_ROOT : ret; 1669 return (ret > SUID_DUMP_USER) ? SUID_DUMP_ROOT : ret;
1670} 1670}
1671 1671
1672/*
1673 * This returns the actual value of the suid_dumpable flag. For things
1674 * that are using this for checking for privilege transitions, it must
1675 * test against SUID_DUMP_USER rather than treating it as a boolean
1676 * value.
1677 */
1672int get_dumpable(struct mm_struct *mm) 1678int get_dumpable(struct mm_struct *mm)
1673{ 1679{
1674 return __get_dumpable(mm->flags); 1680 return __get_dumpable(mm->flags);
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index e8112ae50531..7554fd410bcc 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -99,9 +99,6 @@ extern void setup_new_exec(struct linux_binprm * bprm);
99extern void would_dump(struct linux_binprm *, struct file *); 99extern void would_dump(struct linux_binprm *, struct file *);
100 100
101extern int suid_dumpable; 101extern int suid_dumpable;
102#define SUID_DUMP_DISABLE 0 /* No setuid dumping */
103#define SUID_DUMP_USER 1 /* Dump as user of process */
104#define SUID_DUMP_ROOT 2 /* Dump as root */
105 102
106/* Stack area protections */ 103/* Stack area protections */
107#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */ 104#define EXSTACK_DEFAULT 0 /* Whatever the arch defaults to */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 5e226fe3e512..f7efc8604652 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -323,6 +323,10 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
323extern void set_dumpable(struct mm_struct *mm, int value); 323extern void set_dumpable(struct mm_struct *mm, int value);
324extern int get_dumpable(struct mm_struct *mm); 324extern int get_dumpable(struct mm_struct *mm);
325 325
326#define SUID_DUMP_DISABLE 0 /* No setuid dumping */
327#define SUID_DUMP_USER 1 /* Dump as user of process */
328#define SUID_DUMP_ROOT 2 /* Dump as root */
329
326/* mm flags */ 330/* mm flags */
327/* dumpable bits */ 331/* dumpable bits */
328#define MMF_DUMPABLE 0 /* core dump is permitted */ 332#define MMF_DUMPABLE 0 /* core dump is permitted */
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index dd562e9aa2c8..1f4bcb3cc21c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -257,7 +257,8 @@ ok:
257 if (task->mm) 257 if (task->mm)
258 dumpable = get_dumpable(task->mm); 258 dumpable = get_dumpable(task->mm);
259 rcu_read_lock(); 259 rcu_read_lock();
260 if (!dumpable && !ptrace_has_cap(__task_cred(task)->user_ns, mode)) { 260 if (dumpable != SUID_DUMP_USER &&
261 !ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
261 rcu_read_unlock(); 262 rcu_read_unlock();
262 return -EPERM; 263 return -EPERM;
263 } 264 }