diff options
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 120 | 
1 files changed, 25 insertions, 95 deletions
diff --git a/fs/exec.c b/fs/exec.c index 7ea097f6b341..e1529b4c79b1 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -62,7 +62,6 @@  #include <trace/events/task.h>  #include "internal.h" -#include "coredump.h"  #include <trace/events/sched.h> @@ -843,7 +842,6 @@ static int exec_mmap(struct mm_struct *mm)  	tsk->active_mm = mm;  	activate_mm(active_mm, mm);  	task_unlock(tsk); -	arch_pick_mmap_layout(mm);  	if (old_mm) {  		up_read(&old_mm->mmap_sem);  		BUG_ON(active_mm != old_mm); @@ -1088,8 +1086,8 @@ int flush_old_exec(struct linux_binprm * bprm)  	bprm->mm = NULL;		/* We're using it now */  	set_fs(USER_DS); -	current->flags &= -		~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD | PF_NOFREEZE); +	current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD | +					PF_NOFREEZE | PF_NO_SETAFFINITY);  	flush_thread();  	current->personality &= ~bprm->per_clear; @@ -1139,9 +1137,7 @@ void setup_new_exec(struct linux_binprm * bprm)  	/* An exec changes our domain. We are no longer part of the thread  	   group */ -  	current->self_exec_id++; -			  	flush_signal_handlers(current, 0);  	do_close_on_exec(current->files);  } @@ -1173,6 +1169,10 @@ void free_bprm(struct linux_binprm *bprm)  		mutex_unlock(¤t->signal->cred_guard_mutex);  		abort_creds(bprm->cred);  	} +	if (bprm->file) { +		allow_write_access(bprm->file); +		fput(bprm->file); +	}  	/* If a binfmt changed the interp, free it. */  	if (bprm->interp != bprm->filename)  		kfree(bprm->interp); @@ -1224,11 +1224,10 @@ EXPORT_SYMBOL(install_exec_creds);   * - the caller must hold ->cred_guard_mutex to protect against   *   PTRACE_ATTACH   */ -static int check_unsafe_exec(struct linux_binprm *bprm) +static void check_unsafe_exec(struct linux_binprm *bprm)  {  	struct task_struct *p = current, *t;  	unsigned n_fs; -	int res = 0;  	if (p->ptrace) {  		if (p->ptrace & PT_PTRACE_CAP) @@ -1244,31 +1243,25 @@ static int check_unsafe_exec(struct linux_binprm *bprm)  	if (current->no_new_privs)  		bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; +	t = p;  	n_fs = 1;  	spin_lock(&p->fs->lock);  	rcu_read_lock(); -	for (t = next_thread(p); t != p; t = next_thread(t)) { +	while_each_thread(p, t) {  		if (t->fs == p->fs)  			n_fs++;  	}  	rcu_read_unlock(); -	if (p->fs->users > n_fs) { +	if (p->fs->users > n_fs)  		bprm->unsafe |= LSM_UNSAFE_SHARE; -	} else { -		res = -EAGAIN; -		if (!p->fs->in_exec) { -			p->fs->in_exec = 1; -			res = 1; -		} -	} +	else +		p->fs->in_exec = 1;  	spin_unlock(&p->fs->lock); - -	return res;  } -/*  - * Fill the binprm structure from the inode.  +/* + * Fill the binprm structure from the inode.   * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes   *   * This may be called multiple times for binary chains (scripts for example). @@ -1430,14 +1423,7 @@ static int exec_binprm(struct linux_binprm *bprm)  		audit_bprm(bprm);  		trace_sched_process_exec(current, old_pid, bprm);  		ptrace_event(PTRACE_EVENT_EXEC, old_vpid); -		current->did_exec = 1;  		proc_exec_connector(current); - -		if (bprm->file) { -			allow_write_access(bprm->file); -			fput(bprm->file); -			bprm->file = NULL; /* to catch use-after-free */ -		}  	}  	return ret; @@ -1453,7 +1439,6 @@ static int do_execve_common(const char *filename,  	struct linux_binprm *bprm;  	struct file *file;  	struct files_struct *displaced; -	bool clear_in_exec;  	int retval;  	/* @@ -1485,10 +1470,7 @@ static int do_execve_common(const char *filename,  	if (retval)  		goto out_free; -	retval = check_unsafe_exec(bprm); -	if (retval < 0) -		goto out_free; -	clear_in_exec = retval; +	check_unsafe_exec(bprm);  	current->in_execve = 1;  	file = open_exec(filename); @@ -1504,7 +1486,7 @@ static int do_execve_common(const char *filename,  	retval = bprm_mm_init(bprm);  	if (retval) -		goto out_file; +		goto out_unmark;  	bprm->argc = count(argv, MAX_ARG_STRINGS);  	if ((retval = bprm->argc) < 0) @@ -1551,15 +1533,8 @@ out:  		mmput(bprm->mm);  	} -out_file: -	if (bprm->file) { -		allow_write_access(bprm->file); -		fput(bprm->file); -	} -  out_unmark: -	if (clear_in_exec) -		current->fs->in_exec = 0; +	current->fs->in_exec = 0;  	current->in_execve = 0;  out_free: @@ -1609,67 +1584,22 @@ void set_binfmt(struct linux_binfmt *new)  	if (new)  		__module_get(new->module);  } -  EXPORT_SYMBOL(set_binfmt);  /* - * 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. + * set_dumpable stores three-value SUID_DUMP_* into mm->flags.   */  void set_dumpable(struct mm_struct *mm, int value)  { -	switch (value) { -	case SUID_DUMP_DISABLE: -		clear_bit(MMF_DUMPABLE, &mm->flags); -		smp_wmb(); -		clear_bit(MMF_DUMP_SECURELY, &mm->flags); -		break; -	case SUID_DUMP_USER: -		set_bit(MMF_DUMPABLE, &mm->flags); -		smp_wmb(); -		clear_bit(MMF_DUMP_SECURELY, &mm->flags); -		break; -	case SUID_DUMP_ROOT: -		set_bit(MMF_DUMP_SECURELY, &mm->flags); -		smp_wmb(); -		set_bit(MMF_DUMPABLE, &mm->flags); -		break; -	} -} - -int __get_dumpable(unsigned long mm_flags) -{ -	int ret; +	unsigned long old, new; -	ret = mm_flags & MMF_DUMPABLE_MASK; -	return (ret > SUID_DUMP_USER) ? SUID_DUMP_ROOT : ret; -} +	if (WARN_ON((unsigned)value > SUID_DUMP_ROOT)) +		return; -/* - * This returns the actual value of the suid_dumpable flag. For things - * that are using this for checking for privilege transitions, it must - * test against SUID_DUMP_USER rather than treating it as a boolean - * value. - */ -int get_dumpable(struct mm_struct *mm) -{ -	return __get_dumpable(mm->flags); +	do { +		old = ACCESS_ONCE(mm->flags); +		new = (old & ~MMF_DUMPABLE_MASK) | value; +	} while (cmpxchg(&mm->flags, old, new) != old);  }  SYSCALL_DEFINE3(execve,  |