aboutsummaryrefslogtreecommitdiffstats
path: root/fs/exec.c
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2014-01-23 18:55:31 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-23 19:37:01 -0500
commitabacd2fe3ca10b3ade57f3634053241a660002c2 (patch)
treee3d8d77e9b12d2e57a90a026a225313ccca9ddb1 /fs/exec.c
parentf3c73a99a1fac2db992b6879b8a78a3ae2fcc06e (diff)
coredump: set_dumpable: fix the theoretical race with itself
set_dumpable() updates MMF_DUMPABLE_MASK in a non-trivial way to ensure that get_dumpable() can't observe the intermediate state, but this all can't help if multiple threads call set_dumpable() at the same time. And in theory commit_creds()->set_dumpable(SUID_DUMP_ROOT) racing with sys_prctl()->set_dumpable(SUID_DUMP_DISABLE) can result in SUID_DUMP_USER. Change this code to update both bits atomically via cmpxchg(). Note: this assumes that it is safe to mix bitops and cmpxchg. IOW, if, say, an architecture implements cmpxchg() using the locking (like arch/parisc/lib/bitops.c does), then it should use the same locks for set_bit/etc. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Acked-by: Kees Cook <keescook@chromium.org> Cc: Alex Kelly <alex.page.kelly@gmail.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Josh Triplett <josh@joshtriplett.org> Cc: Petr Matousek <pmatouse@redhat.com> Cc: Vasily Kulikov <segoon@openwall.com> 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.c49
1 files changed, 15 insertions, 34 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 7ea097f6b341..f039386499db 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1614,43 +1614,24 @@ EXPORT_SYMBOL(set_binfmt);
1614 1614
1615/* 1615/*
1616 * set_dumpable converts traditional three-value dumpable to two flags and 1616 * set_dumpable converts traditional three-value dumpable to two flags and
1617 * stores them into mm->flags. It modifies lower two bits of mm->flags, but 1617 * stores them into mm->flags.
1618 * these bits are not changed atomically. So get_dumpable can observe the
1619 * intermediate state. To avoid doing unexpected behavior, get get_dumpable
1620 * return either old dumpable or new one by paying attention to the order of
1621 * modifying the bits.
1622 *
1623 * dumpable | mm->flags (binary)
1624 * old new | initial interim final
1625 * ---------+-----------------------
1626 * 0 1 | 00 01 01
1627 * 0 2 | 00 10(*) 11
1628 * 1 0 | 01 00 00
1629 * 1 2 | 01 11 11
1630 * 2 0 | 11 10(*) 00
1631 * 2 1 | 11 11 01
1632 *
1633 * (*) get_dumpable regards interim value of 10 as 11.
1634 */ 1618 */
1635void set_dumpable(struct mm_struct *mm, int value) 1619void set_dumpable(struct mm_struct *mm, int value)
1636{ 1620{
1637 switch (value) { 1621 unsigned long old, new;
1638 case SUID_DUMP_DISABLE: 1622
1639 clear_bit(MMF_DUMPABLE, &mm->flags); 1623 do {
1640 smp_wmb(); 1624 old = ACCESS_ONCE(mm->flags);
1641 clear_bit(MMF_DUMP_SECURELY, &mm->flags); 1625 new = old & ~MMF_DUMPABLE_MASK;
1642 break; 1626
1643 case SUID_DUMP_USER: 1627 switch (value) {
1644 set_bit(MMF_DUMPABLE, &mm->flags); 1628 case SUID_DUMP_ROOT:
1645 smp_wmb(); 1629 new |= (1 << MMF_DUMP_SECURELY);
1646 clear_bit(MMF_DUMP_SECURELY, &mm->flags); 1630 case SUID_DUMP_USER:
1647 break; 1631 new |= (1<< MMF_DUMPABLE);
1648 case SUID_DUMP_ROOT: 1632 }
1649 set_bit(MMF_DUMP_SECURELY, &mm->flags); 1633
1650 smp_wmb(); 1634 } while (cmpxchg(&mm->flags, old, new) != old);
1651 set_bit(MMF_DUMPABLE, &mm->flags);
1652 break;
1653 }
1654} 1635}
1655 1636
1656int __get_dumpable(unsigned long mm_flags) 1637int __get_dumpable(unsigned long mm_flags)