From 6c5d523826dc639df709ed0f88c5d2ce25379652 Mon Sep 17 00:00:00 2001 From: "Kawai, Hidehiro" Date: Thu, 19 Jul 2007 01:48:27 -0700 Subject: coredump masking: reimplementation of dumpable using two flags This patch changes mm_struct.dumpable to a pair of bit flags. set_dumpable() converts three-value dumpable to two flags and stores it into lower two bits of mm_struct.flags instead of mm_struct.dumpable. get_dumpable() behaves in the opposite way. [akpm@linux-foundation.org: export set_dumpable] Signed-off-by: Hidehiro Kawai Cc: Alan Cox Cc: David Howells Cc: Hugh Dickins Cc: Nick Piggin Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/exec.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ fs/proc/base.c | 2 +- 2 files changed, 57 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/exec.c b/fs/exec.c index 498f2b3dca2..7bdea7937ee 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1058,9 +1058,9 @@ int flush_old_exec(struct linux_binprm * bprm) current->sas_ss_sp = current->sas_ss_size = 0; if (current->euid == current->uid && current->egid == current->gid) - current->mm->dumpable = 1; + set_dumpable(current->mm, 1); else - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); name = bprm->filename; @@ -1088,7 +1088,7 @@ int flush_old_exec(struct linux_binprm * bprm) file_permission(bprm->file, MAY_READ) || (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { suid_keys(current); - current->mm->dumpable = suid_dumpable; + set_dumpable(current->mm, suid_dumpable); } /* An exec changes our domain. We are no longer part of the thread @@ -1665,6 +1665,56 @@ fail: return core_waiters; } +/* + * 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. + */ +void set_dumpable(struct mm_struct *mm, int value) +{ + switch (value) { + case 0: + clear_bit(MMF_DUMPABLE, &mm->flags); + smp_wmb(); + clear_bit(MMF_DUMP_SECURELY, &mm->flags); + break; + case 1: + set_bit(MMF_DUMPABLE, &mm->flags); + smp_wmb(); + clear_bit(MMF_DUMP_SECURELY, &mm->flags); + break; + case 2: + set_bit(MMF_DUMP_SECURELY, &mm->flags); + smp_wmb(); + set_bit(MMF_DUMPABLE, &mm->flags); + break; + } +} +EXPORT_SYMBOL_GPL(set_dumpable); + +int get_dumpable(struct mm_struct *mm) +{ + int ret; + + ret = mm->flags & 0x3; + return (ret >= 2) ? 2 : ret; +} + int do_coredump(long signr, int exit_code, struct pt_regs * regs) { char corename[CORENAME_MAX_SIZE + 1]; @@ -1683,7 +1733,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!binfmt || !binfmt->core_dump) goto fail; down_write(&mm->mmap_sem); - if (!mm->dumpable) { + if (!get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } @@ -1693,11 +1743,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) * process nor do we know its entire history. We only know it * was tainted so we dump it as root in mode 2. */ - if (mm->dumpable == 2) { /* Setuid core dump mode */ + if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ flag = O_EXCL; /* Stop rewrite attacks */ current->fsuid = 0; /* Dump root private */ } - mm->dumpable = 0; + set_dumpable(mm, 0); retval = coredump_wait(exit_code); if (retval < 0) diff --git a/fs/proc/base.c b/fs/proc/base.c index 42cb4f5613b..49b3ab0175e 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1014,7 +1014,7 @@ static int task_dumpable(struct task_struct *task) task_lock(task); mm = task->mm; if (mm) - dumpable = mm->dumpable; + dumpable = get_dumpable(mm); task_unlock(task); if(dumpable == 1) return 1; -- cgit v1.2.3