diff options
author | Kawai, Hidehiro <hidehiro.kawai.ez@hitachi.com> | 2007-07-19 04:48:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-19 13:04:46 -0400 |
commit | 6c5d523826dc639df709ed0f88c5d2ce25379652 (patch) | |
tree | ef2fa8cb30266b3a9b047902794e78c583b099da /fs/exec.c | |
parent | 76fdbb25f963de5dc1e308325f0578a2f92b1c2d (diff) |
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 <hidehiro.kawai.ez@hitachi.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: David Howells <dhowells@redhat.com>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/exec.c')
-rw-r--r-- | fs/exec.c | 62 |
1 files changed, 56 insertions, 6 deletions
@@ -1058,9 +1058,9 @@ int flush_old_exec(struct linux_binprm * bprm) | |||
1058 | current->sas_ss_sp = current->sas_ss_size = 0; | 1058 | current->sas_ss_sp = current->sas_ss_size = 0; |
1059 | 1059 | ||
1060 | if (current->euid == current->uid && current->egid == current->gid) | 1060 | if (current->euid == current->uid && current->egid == current->gid) |
1061 | current->mm->dumpable = 1; | 1061 | set_dumpable(current->mm, 1); |
1062 | else | 1062 | else |
1063 | current->mm->dumpable = suid_dumpable; | 1063 | set_dumpable(current->mm, suid_dumpable); |
1064 | 1064 | ||
1065 | name = bprm->filename; | 1065 | name = bprm->filename; |
1066 | 1066 | ||
@@ -1088,7 +1088,7 @@ int flush_old_exec(struct linux_binprm * bprm) | |||
1088 | file_permission(bprm->file, MAY_READ) || | 1088 | file_permission(bprm->file, MAY_READ) || |
1089 | (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { | 1089 | (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) { |
1090 | suid_keys(current); | 1090 | suid_keys(current); |
1091 | current->mm->dumpable = suid_dumpable; | 1091 | set_dumpable(current->mm, suid_dumpable); |
1092 | } | 1092 | } |
1093 | 1093 | ||
1094 | /* An exec changes our domain. We are no longer part of the thread | 1094 | /* An exec changes our domain. We are no longer part of the thread |
@@ -1665,6 +1665,56 @@ fail: | |||
1665 | return core_waiters; | 1665 | return core_waiters; |
1666 | } | 1666 | } |
1667 | 1667 | ||
1668 | /* | ||
1669 | * set_dumpable converts traditional three-value dumpable to two flags and | ||
1670 | * stores them into mm->flags. It modifies lower two bits of mm->flags, but | ||
1671 | * these bits are not changed atomically. So get_dumpable can observe the | ||
1672 | * intermediate state. To avoid doing unexpected behavior, get get_dumpable | ||
1673 | * return either old dumpable or new one by paying attention to the order of | ||
1674 | * modifying the bits. | ||
1675 | * | ||
1676 | * dumpable | mm->flags (binary) | ||
1677 | * old new | initial interim final | ||
1678 | * ---------+----------------------- | ||
1679 | * 0 1 | 00 01 01 | ||
1680 | * 0 2 | 00 10(*) 11 | ||
1681 | * 1 0 | 01 00 00 | ||
1682 | * 1 2 | 01 11 11 | ||
1683 | * 2 0 | 11 10(*) 00 | ||
1684 | * 2 1 | 11 11 01 | ||
1685 | * | ||
1686 | * (*) get_dumpable regards interim value of 10 as 11. | ||
1687 | */ | ||
1688 | void set_dumpable(struct mm_struct *mm, int value) | ||
1689 | { | ||
1690 | switch (value) { | ||
1691 | case 0: | ||
1692 | clear_bit(MMF_DUMPABLE, &mm->flags); | ||
1693 | smp_wmb(); | ||
1694 | clear_bit(MMF_DUMP_SECURELY, &mm->flags); | ||
1695 | break; | ||
1696 | case 1: | ||
1697 | set_bit(MMF_DUMPABLE, &mm->flags); | ||
1698 | smp_wmb(); | ||
1699 | clear_bit(MMF_DUMP_SECURELY, &mm->flags); | ||
1700 | break; | ||
1701 | case 2: | ||
1702 | set_bit(MMF_DUMP_SECURELY, &mm->flags); | ||
1703 | smp_wmb(); | ||
1704 | set_bit(MMF_DUMPABLE, &mm->flags); | ||
1705 | break; | ||
1706 | } | ||
1707 | } | ||
1708 | EXPORT_SYMBOL_GPL(set_dumpable); | ||
1709 | |||
1710 | int get_dumpable(struct mm_struct *mm) | ||
1711 | { | ||
1712 | int ret; | ||
1713 | |||
1714 | ret = mm->flags & 0x3; | ||
1715 | return (ret >= 2) ? 2 : ret; | ||
1716 | } | ||
1717 | |||
1668 | int do_coredump(long signr, int exit_code, struct pt_regs * regs) | 1718 | int do_coredump(long signr, int exit_code, struct pt_regs * regs) |
1669 | { | 1719 | { |
1670 | char corename[CORENAME_MAX_SIZE + 1]; | 1720 | char corename[CORENAME_MAX_SIZE + 1]; |
@@ -1683,7 +1733,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) | |||
1683 | if (!binfmt || !binfmt->core_dump) | 1733 | if (!binfmt || !binfmt->core_dump) |
1684 | goto fail; | 1734 | goto fail; |
1685 | down_write(&mm->mmap_sem); | 1735 | down_write(&mm->mmap_sem); |
1686 | if (!mm->dumpable) { | 1736 | if (!get_dumpable(mm)) { |
1687 | up_write(&mm->mmap_sem); | 1737 | up_write(&mm->mmap_sem); |
1688 | goto fail; | 1738 | goto fail; |
1689 | } | 1739 | } |
@@ -1693,11 +1743,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) | |||
1693 | * process nor do we know its entire history. We only know it | 1743 | * process nor do we know its entire history. We only know it |
1694 | * was tainted so we dump it as root in mode 2. | 1744 | * was tainted so we dump it as root in mode 2. |
1695 | */ | 1745 | */ |
1696 | if (mm->dumpable == 2) { /* Setuid core dump mode */ | 1746 | if (get_dumpable(mm) == 2) { /* Setuid core dump mode */ |
1697 | flag = O_EXCL; /* Stop rewrite attacks */ | 1747 | flag = O_EXCL; /* Stop rewrite attacks */ |
1698 | current->fsuid = 0; /* Dump root private */ | 1748 | current->fsuid = 0; /* Dump root private */ |
1699 | } | 1749 | } |
1700 | mm->dumpable = 0; | 1750 | set_dumpable(mm, 0); |
1701 | 1751 | ||
1702 | retval = coredump_wait(exit_code); | 1752 | retval = coredump_wait(exit_code); |
1703 | if (retval < 0) | 1753 | if (retval < 0) |