aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/exec.c62
-rw-r--r--fs/proc/base.c2
-rw-r--r--include/asm-ia64/processor.h4
-rw-r--r--include/linux/sched.h9
-rw-r--r--kernel/ptrace.c2
-rw-r--r--kernel/sys.c24
-rw-r--r--security/commoncap.c2
-rw-r--r--security/dummy.c2
8 files changed, 82 insertions, 25 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 498f2b3dca20..7bdea7937ee8 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -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 */
1688void 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}
1708EXPORT_SYMBOL_GPL(set_dumpable);
1709
1710int get_dumpable(struct mm_struct *mm)
1711{
1712 int ret;
1713
1714 ret = mm->flags & 0x3;
1715 return (ret >= 2) ? 2 : ret;
1716}
1717
1668int do_coredump(long signr, int exit_code, struct pt_regs * regs) 1718int 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;
diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h
index db81ba406cef..6251c76437d2 100644
--- a/include/asm-ia64/processor.h
+++ b/include/asm-ia64/processor.h
@@ -295,9 +295,9 @@ struct thread_struct {
295 regs->ar_bspstore = current->thread.rbs_bot; \ 295 regs->ar_bspstore = current->thread.rbs_bot; \
296 regs->ar_fpsr = FPSR_DEFAULT; \ 296 regs->ar_fpsr = FPSR_DEFAULT; \
297 regs->loadrs = 0; \ 297 regs->loadrs = 0; \
298 regs->r8 = current->mm->dumpable; /* set "don't zap registers" flag */ \ 298 regs->r8 = get_dumpable(current->mm); /* set "don't zap registers" flag */ \
299 regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \ 299 regs->r12 = new_sp - 16; /* allocate 16 byte scratch area */ \
300 if (unlikely(!current->mm->dumpable)) { \ 300 if (unlikely(!get_dumpable(current->mm))) { \
301 /* \ 301 /* \
302 * Zap scratch regs to avoid leaking bits between processes with different \ 302 * Zap scratch regs to avoid leaking bits between processes with different \
303 * uid/privileges. \ 303 * uid/privileges. \
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 731edaca8ffd..8dbd08366400 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -345,6 +345,13 @@ typedef unsigned long mm_counter_t;
345 (mm)->hiwater_vm = (mm)->total_vm; \ 345 (mm)->hiwater_vm = (mm)->total_vm; \
346} while (0) 346} while (0)
347 347
348extern void set_dumpable(struct mm_struct *mm, int value);
349extern int get_dumpable(struct mm_struct *mm);
350
351/* mm flags */
352#define MMF_DUMPABLE 0 /* core dump is permitted */
353#define MMF_DUMP_SECURELY 1 /* core file is readable only by root */
354
348struct mm_struct { 355struct mm_struct {
349 struct vm_area_struct * mmap; /* list of VMAs */ 356 struct vm_area_struct * mmap; /* list of VMAs */
350 struct rb_root mm_rb; 357 struct rb_root mm_rb;
@@ -402,7 +409,7 @@ struct mm_struct {
402 unsigned int token_priority; 409 unsigned int token_priority;
403 unsigned int last_interval; 410 unsigned int last_interval;
404 411
405 unsigned char dumpable:2; 412 unsigned long flags; /* Must use atomic bitops to access the bits */
406 413
407 /* coredumping support */ 414 /* coredumping support */
408 int core_waiters; 415 int core_waiters;
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 4a1745f1dadf..82a558b655da 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -142,7 +142,7 @@ static int may_attach(struct task_struct *task)
142 return -EPERM; 142 return -EPERM;
143 smp_rmb(); 143 smp_rmb();
144 if (task->mm) 144 if (task->mm)
145 dumpable = task->mm->dumpable; 145 dumpable = get_dumpable(task->mm);
146 if (!dumpable && !capable(CAP_SYS_PTRACE)) 146 if (!dumpable && !capable(CAP_SYS_PTRACE))
147 return -EPERM; 147 return -EPERM;
148 148
diff --git a/kernel/sys.c b/kernel/sys.c
index d40e40a9446c..08562f419768 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1036,7 +1036,7 @@ asmlinkage long sys_setregid(gid_t rgid, gid_t egid)
1036 return -EPERM; 1036 return -EPERM;
1037 } 1037 }
1038 if (new_egid != old_egid) { 1038 if (new_egid != old_egid) {
1039 current->mm->dumpable = suid_dumpable; 1039 set_dumpable(current->mm, suid_dumpable);
1040 smp_wmb(); 1040 smp_wmb();
1041 } 1041 }
1042 if (rgid != (gid_t) -1 || 1042 if (rgid != (gid_t) -1 ||
@@ -1066,13 +1066,13 @@ asmlinkage long sys_setgid(gid_t gid)
1066 1066
1067 if (capable(CAP_SETGID)) { 1067 if (capable(CAP_SETGID)) {
1068 if (old_egid != gid) { 1068 if (old_egid != gid) {
1069 current->mm->dumpable = suid_dumpable; 1069 set_dumpable(current->mm, suid_dumpable);
1070 smp_wmb(); 1070 smp_wmb();
1071 } 1071 }
1072 current->gid = current->egid = current->sgid = current->fsgid = gid; 1072 current->gid = current->egid = current->sgid = current->fsgid = gid;
1073 } else if ((gid == current->gid) || (gid == current->sgid)) { 1073 } else if ((gid == current->gid) || (gid == current->sgid)) {
1074 if (old_egid != gid) { 1074 if (old_egid != gid) {
1075 current->mm->dumpable = suid_dumpable; 1075 set_dumpable(current->mm, suid_dumpable);
1076 smp_wmb(); 1076 smp_wmb();
1077 } 1077 }
1078 current->egid = current->fsgid = gid; 1078 current->egid = current->fsgid = gid;
@@ -1103,7 +1103,7 @@ static int set_user(uid_t new_ruid, int dumpclear)
1103 switch_uid(new_user); 1103 switch_uid(new_user);
1104 1104
1105 if (dumpclear) { 1105 if (dumpclear) {
1106 current->mm->dumpable = suid_dumpable; 1106 set_dumpable(current->mm, suid_dumpable);
1107 smp_wmb(); 1107 smp_wmb();
1108 } 1108 }
1109 current->uid = new_ruid; 1109 current->uid = new_ruid;
@@ -1159,7 +1159,7 @@ asmlinkage long sys_setreuid(uid_t ruid, uid_t euid)
1159 return -EAGAIN; 1159 return -EAGAIN;
1160 1160
1161 if (new_euid != old_euid) { 1161 if (new_euid != old_euid) {
1162 current->mm->dumpable = suid_dumpable; 1162 set_dumpable(current->mm, suid_dumpable);
1163 smp_wmb(); 1163 smp_wmb();
1164 } 1164 }
1165 current->fsuid = current->euid = new_euid; 1165 current->fsuid = current->euid = new_euid;
@@ -1209,7 +1209,7 @@ asmlinkage long sys_setuid(uid_t uid)
1209 return -EPERM; 1209 return -EPERM;
1210 1210
1211 if (old_euid != uid) { 1211 if (old_euid != uid) {
1212 current->mm->dumpable = suid_dumpable; 1212 set_dumpable(current->mm, suid_dumpable);
1213 smp_wmb(); 1213 smp_wmb();
1214 } 1214 }
1215 current->fsuid = current->euid = uid; 1215 current->fsuid = current->euid = uid;
@@ -1254,7 +1254,7 @@ asmlinkage long sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
1254 } 1254 }
1255 if (euid != (uid_t) -1) { 1255 if (euid != (uid_t) -1) {
1256 if (euid != current->euid) { 1256 if (euid != current->euid) {
1257 current->mm->dumpable = suid_dumpable; 1257 set_dumpable(current->mm, suid_dumpable);
1258 smp_wmb(); 1258 smp_wmb();
1259 } 1259 }
1260 current->euid = euid; 1260 current->euid = euid;
@@ -1304,7 +1304,7 @@ asmlinkage long sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
1304 } 1304 }
1305 if (egid != (gid_t) -1) { 1305 if (egid != (gid_t) -1) {
1306 if (egid != current->egid) { 1306 if (egid != current->egid) {
1307 current->mm->dumpable = suid_dumpable; 1307 set_dumpable(current->mm, suid_dumpable);
1308 smp_wmb(); 1308 smp_wmb();
1309 } 1309 }
1310 current->egid = egid; 1310 current->egid = egid;
@@ -1350,7 +1350,7 @@ asmlinkage long sys_setfsuid(uid_t uid)
1350 uid == current->suid || uid == current->fsuid || 1350 uid == current->suid || uid == current->fsuid ||
1351 capable(CAP_SETUID)) { 1351 capable(CAP_SETUID)) {
1352 if (uid != old_fsuid) { 1352 if (uid != old_fsuid) {
1353 current->mm->dumpable = suid_dumpable; 1353 set_dumpable(current->mm, suid_dumpable);
1354 smp_wmb(); 1354 smp_wmb();
1355 } 1355 }
1356 current->fsuid = uid; 1356 current->fsuid = uid;
@@ -1379,7 +1379,7 @@ asmlinkage long sys_setfsgid(gid_t gid)
1379 gid == current->sgid || gid == current->fsgid || 1379 gid == current->sgid || gid == current->fsgid ||
1380 capable(CAP_SETGID)) { 1380 capable(CAP_SETGID)) {
1381 if (gid != old_fsgid) { 1381 if (gid != old_fsgid) {
1382 current->mm->dumpable = suid_dumpable; 1382 set_dumpable(current->mm, suid_dumpable);
1383 smp_wmb(); 1383 smp_wmb();
1384 } 1384 }
1385 current->fsgid = gid; 1385 current->fsgid = gid;
@@ -2176,14 +2176,14 @@ asmlinkage long sys_prctl(int option, unsigned long arg2, unsigned long arg3,
2176 error = put_user(current->pdeath_signal, (int __user *)arg2); 2176 error = put_user(current->pdeath_signal, (int __user *)arg2);
2177 break; 2177 break;
2178 case PR_GET_DUMPABLE: 2178 case PR_GET_DUMPABLE:
2179 error = current->mm->dumpable; 2179 error = get_dumpable(current->mm);
2180 break; 2180 break;
2181 case PR_SET_DUMPABLE: 2181 case PR_SET_DUMPABLE:
2182 if (arg2 < 0 || arg2 > 1) { 2182 if (arg2 < 0 || arg2 > 1) {
2183 error = -EINVAL; 2183 error = -EINVAL;
2184 break; 2184 break;
2185 } 2185 }
2186 current->mm->dumpable = arg2; 2186 set_dumpable(current->mm, arg2);
2187 break; 2187 break;
2188 2188
2189 case PR_SET_UNALIGN: 2189 case PR_SET_UNALIGN:
diff --git a/security/commoncap.c b/security/commoncap.c
index 384379ede4fd..338606eb7238 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -148,7 +148,7 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
148 148
149 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid || 149 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
150 !cap_issubset (new_permitted, current->cap_permitted)) { 150 !cap_issubset (new_permitted, current->cap_permitted)) {
151 current->mm->dumpable = suid_dumpable; 151 set_dumpable(current->mm, suid_dumpable);
152 152
153 if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) { 153 if (unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
154 if (!capable(CAP_SETUID)) { 154 if (!capable(CAP_SETUID)) {
diff --git a/security/dummy.c b/security/dummy.c
index d6a112ce2975..19d813d5e083 100644
--- a/security/dummy.c
+++ b/security/dummy.c
@@ -130,7 +130,7 @@ static void dummy_bprm_free_security (struct linux_binprm *bprm)
130static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) 130static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
131{ 131{
132 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) { 132 if (bprm->e_uid != current->uid || bprm->e_gid != current->gid) {
133 current->mm->dumpable = suid_dumpable; 133 set_dumpable(current->mm, suid_dumpable);
134 134
135 if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) { 135 if ((unsafe & ~LSM_UNSAFE_PTRACE_CAP) && !capable(CAP_SETUID)) {
136 bprm->e_uid = current->uid; 136 bprm->e_uid = current->uid;