Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c5d5238 authored by Kawai, Hidehiro's avatar Kawai, Hidehiro Committed by Linus Torvalds
Browse files

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: default avatarHidehiro 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 76fdbb25
Loading
Loading
Loading
Loading
+56 −6
Original line number Original line Diff line number Diff line
@@ -1058,9 +1058,9 @@ int flush_old_exec(struct linux_binprm * bprm)
	current->sas_ss_sp = current->sas_ss_size = 0;
	current->sas_ss_sp = current->sas_ss_size = 0;


	if (current->euid == current->uid && current->egid == current->gid)
	if (current->euid == current->uid && current->egid == current->gid)
		current->mm->dumpable = 1;
		set_dumpable(current->mm, 1);
	else
	else
		current->mm->dumpable = suid_dumpable;
		set_dumpable(current->mm, suid_dumpable);


	name = bprm->filename;
	name = bprm->filename;


@@ -1088,7 +1088,7 @@ int flush_old_exec(struct linux_binprm * bprm)
	    file_permission(bprm->file, MAY_READ) ||
	    file_permission(bprm->file, MAY_READ) ||
	    (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
	    (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
		suid_keys(current);
		suid_keys(current);
		current->mm->dumpable = suid_dumpable;
		set_dumpable(current->mm, suid_dumpable);
	}
	}


	/* An exec changes our domain. We are no longer part of the thread
	/* An exec changes our domain. We are no longer part of the thread
@@ -1665,6 +1665,56 @@ fail:
	return core_waiters;
	return core_waiters;
}
}


/*
 * set_dumpable converts traditional three-value dumpable to two flags and
 * stores them into mm->flags.  It modifies lower two bits of mm->flags, but
 * these bits are not changed atomically.  So get_dumpable can observe the
 * intermediate state.  To avoid doing unexpected behavior, get get_dumpable
 * return either old dumpable or new one by paying attention to the order of
 * modifying the bits.
 *
 * dumpable |   mm->flags (binary)
 * old  new | initial interim  final
 * ---------+-----------------------
 *  0    1  |   00      01      01
 *  0    2  |   00      10(*)   11
 *  1    0  |   01      00      00
 *  1    2  |   01      11      11
 *  2    0  |   11      10(*)   00
 *  2    1  |   11      11      01
 *
 * (*) get_dumpable regards interim value of 10 as 11.
 */
void set_dumpable(struct mm_struct *mm, int value)
{
	switch (value) {
	case 0:
		clear_bit(MMF_DUMPABLE, &mm->flags);
		smp_wmb();
		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
		break;
	case 1:
		set_bit(MMF_DUMPABLE, &mm->flags);
		smp_wmb();
		clear_bit(MMF_DUMP_SECURELY, &mm->flags);
		break;
	case 2:
		set_bit(MMF_DUMP_SECURELY, &mm->flags);
		smp_wmb();
		set_bit(MMF_DUMPABLE, &mm->flags);
		break;
	}
}
EXPORT_SYMBOL_GPL(set_dumpable);

int get_dumpable(struct mm_struct *mm)
{
	int ret;

	ret = mm->flags & 0x3;
	return (ret >= 2) ? 2 : ret;
}

int do_coredump(long signr, int exit_code, struct pt_regs * regs)
int do_coredump(long signr, int exit_code, struct pt_regs * regs)
{
{
	char corename[CORENAME_MAX_SIZE + 1];
	char corename[CORENAME_MAX_SIZE + 1];
@@ -1683,7 +1733,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
	if (!binfmt || !binfmt->core_dump)
	if (!binfmt || !binfmt->core_dump)
		goto fail;
		goto fail;
	down_write(&mm->mmap_sem);
	down_write(&mm->mmap_sem);
	if (!mm->dumpable) {
	if (!get_dumpable(mm)) {
		up_write(&mm->mmap_sem);
		up_write(&mm->mmap_sem);
		goto fail;
		goto fail;
	}
	}
@@ -1693,11 +1743,11 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
	 *	process nor do we know its entire history. We only know it
	 *	process nor do we know its entire history. We only know it
	 *	was tainted so we dump it as root in mode 2.
	 *	was tainted so we dump it as root in mode 2.
	 */
	 */
	if (mm->dumpable == 2) {	/* Setuid core dump mode */
	if (get_dumpable(mm) == 2) {	/* Setuid core dump mode */
		flag = O_EXCL;		/* Stop rewrite attacks */
		flag = O_EXCL;		/* Stop rewrite attacks */
		current->fsuid = 0;	/* Dump root private */
		current->fsuid = 0;	/* Dump root private */
	}
	}
	mm->dumpable = 0;
	set_dumpable(mm, 0);


	retval = coredump_wait(exit_code);
	retval = coredump_wait(exit_code);
	if (retval < 0)
	if (retval < 0)
+1 −1
Original line number Original line Diff line number Diff line
@@ -1014,7 +1014,7 @@ static int task_dumpable(struct task_struct *task)
	task_lock(task);
	task_lock(task);
	mm = task->mm;
	mm = task->mm;
	if (mm)
	if (mm)
		dumpable = mm->dumpable;
		dumpable = get_dumpable(mm);
	task_unlock(task);
	task_unlock(task);
	if(dumpable == 1)
	if(dumpable == 1)
		return 1;
		return 1;
+2 −2
Original line number Original line Diff line number Diff line
@@ -295,9 +295,9 @@ struct thread_struct {
	regs->ar_bspstore = current->thread.rbs_bot;						\
	regs->ar_bspstore = current->thread.rbs_bot;						\
	regs->ar_fpsr = FPSR_DEFAULT;								\
	regs->ar_fpsr = FPSR_DEFAULT;								\
	regs->loadrs = 0;									\
	regs->loadrs = 0;									\
	regs->r8 = current->mm->dumpable;	/* set "don't zap registers" flag */		\
	regs->r8 = get_dumpable(current->mm);	/* set "don't zap registers" flag */		\
	regs->r12 = new_sp - 16;	/* allocate 16 byte scratch area */			\
	regs->r12 = new_sp - 16;	/* allocate 16 byte scratch area */			\
	if (unlikely(!current->mm->dumpable)) {							\
	if (unlikely(!get_dumpable(current->mm))) {							\
		/*										\
		/*										\
		 * Zap scratch regs to avoid leaking bits between processes with different	\
		 * Zap scratch regs to avoid leaking bits between processes with different	\
		 * uid/privileges.								\
		 * uid/privileges.								\
+8 −1
Original line number Original line Diff line number Diff line
@@ -345,6 +345,13 @@ typedef unsigned long mm_counter_t;
		(mm)->hiwater_vm = (mm)->total_vm;	\
		(mm)->hiwater_vm = (mm)->total_vm;	\
} while (0)
} while (0)


extern void set_dumpable(struct mm_struct *mm, int value);
extern int get_dumpable(struct mm_struct *mm);

/* mm flags */
#define MMF_DUMPABLE      0  /* core dump is permitted */
#define MMF_DUMP_SECURELY 1  /* core file is readable only by root */

struct mm_struct {
struct mm_struct {
	struct vm_area_struct * mmap;		/* list of VMAs */
	struct vm_area_struct * mmap;		/* list of VMAs */
	struct rb_root mm_rb;
	struct rb_root mm_rb;
@@ -402,7 +409,7 @@ struct mm_struct {
	unsigned int token_priority;
	unsigned int token_priority;
	unsigned int last_interval;
	unsigned int last_interval;


	unsigned char dumpable:2;
	unsigned long flags; /* Must use atomic bitops to access the bits */


	/* coredumping support */
	/* coredumping support */
	int core_waiters;
	int core_waiters;
+1 −1
Original line number Original line Diff line number Diff line
@@ -142,7 +142,7 @@ static int may_attach(struct task_struct *task)
		return -EPERM;
		return -EPERM;
	smp_rmb();
	smp_rmb();
	if (task->mm)
	if (task->mm)
		dumpable = task->mm->dumpable;
		dumpable = get_dumpable(task->mm);
	if (!dumpable && !capable(CAP_SYS_PTRACE))
	if (!dumpable && !capable(CAP_SYS_PTRACE))
		return -EPERM;
		return -EPERM;


Loading