diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/exec.c | 62 | ||||
-rw-r--r-- | fs/proc/base.c | 2 |
2 files changed, 57 insertions, 7 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) |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 42cb4f5613b6..49b3ab0175e0 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -1014,7 +1014,7 @@ static int task_dumpable(struct task_struct *task) | |||
1014 | task_lock(task); | 1014 | task_lock(task); |
1015 | mm = task->mm; | 1015 | mm = task->mm; |
1016 | if (mm) | 1016 | if (mm) |
1017 | dumpable = mm->dumpable; | 1017 | dumpable = get_dumpable(mm); |
1018 | task_unlock(task); | 1018 | task_unlock(task); |
1019 | if(dumpable == 1) | 1019 | if(dumpable == 1) |
1020 | return 1; | 1020 | return 1; |