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 | |
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>
-rw-r--r-- | fs/exec.c | 62 | ||||
-rw-r--r-- | fs/proc/base.c | 2 | ||||
-rw-r--r-- | include/asm-ia64/processor.h | 4 | ||||
-rw-r--r-- | include/linux/sched.h | 9 | ||||
-rw-r--r-- | kernel/ptrace.c | 2 | ||||
-rw-r--r-- | kernel/sys.c | 24 | ||||
-rw-r--r-- | security/commoncap.c | 2 | ||||
-rw-r--r-- | security/dummy.c | 2 |
8 files changed, 82 insertions, 25 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; |
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 | ||
348 | extern void set_dumpable(struct mm_struct *mm, int value); | ||
349 | extern 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 | |||
348 | struct mm_struct { | 355 | struct 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) | |||
130 | static void dummy_bprm_apply_creds (struct linux_binprm *bprm, int unsafe) | 130 | static 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; |