diff options
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 245 | 
1 files changed, 80 insertions, 165 deletions
diff --git a/fs/exec.c b/fs/exec.c index 8875dd10ae7..a3d33fe592d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -26,6 +26,7 @@  #include <linux/file.h>  #include <linux/fdtable.h>  #include <linux/mm.h> +#include <linux/vmacache.h>  #include <linux/stat.h>  #include <linux/fcntl.h>  #include <linux/swap.h> @@ -62,7 +63,6 @@  #include <trace/events/task.h>  #include "internal.h" -#include "coredump.h"  #include <trace/events/sched.h> @@ -98,6 +98,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt)  	module_put(fmt->module);  } +#ifdef CONFIG_USELIB  /*   * Note that a shared library must be both readable and executable due to   * security reasons. @@ -106,6 +107,7 @@ static inline void put_binfmt(struct linux_binfmt * fmt)   */  SYSCALL_DEFINE1(uselib, const char __user *, library)  { +	struct linux_binfmt *fmt;  	struct file *file;  	struct filename *tmp = getname(library);  	int error = PTR_ERR(tmp); @@ -136,29 +138,27 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)  	fsnotify_open(file);  	error = -ENOEXEC; -	if(file->f_op) { -		struct linux_binfmt * fmt; -		read_lock(&binfmt_lock); -		list_for_each_entry(fmt, &formats, lh) { -			if (!fmt->load_shlib) -				continue; -			if (!try_module_get(fmt->module)) -				continue; -			read_unlock(&binfmt_lock); -			error = fmt->load_shlib(file); -			read_lock(&binfmt_lock); -			put_binfmt(fmt); -			if (error != -ENOEXEC) -				break; -		} +	read_lock(&binfmt_lock); +	list_for_each_entry(fmt, &formats, lh) { +		if (!fmt->load_shlib) +			continue; +		if (!try_module_get(fmt->module)) +			continue;  		read_unlock(&binfmt_lock); +		error = fmt->load_shlib(file); +		read_lock(&binfmt_lock); +		put_binfmt(fmt); +		if (error != -ENOEXEC) +			break;  	} +	read_unlock(&binfmt_lock);  exit:  	fput(file);  out:    	return error;  } +#endif /* #ifdef CONFIG_USELIB */  #ifdef CONFIG_MMU  /* @@ -657,10 +657,10 @@ int setup_arg_pages(struct linux_binprm *bprm,  	unsigned long rlim_stack;  #ifdef CONFIG_STACK_GROWSUP -	/* Limit stack size to 1GB */ +	/* Limit stack size */  	stack_base = rlimit_max(RLIMIT_STACK); -	if (stack_base > (1 << 30)) -		stack_base = 1 << 30; +	if (stack_base > STACK_SIZE_MAX) +		stack_base = STACK_SIZE_MAX;  	/* Make sure we didn't let the argument array grow too large. */  	if (vma->vm_end - vma->vm_start > stack_base) @@ -751,11 +751,10 @@ EXPORT_SYMBOL(setup_arg_pages);  #endif /* CONFIG_MMU */ -struct file *open_exec(const char *name) +static struct file *do_open_exec(struct filename *name)  {  	struct file *file;  	int err; -	struct filename tmp = { .name = name };  	static const struct open_flags open_exec_flags = {  		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,  		.acc_mode = MAY_EXEC | MAY_OPEN, @@ -763,7 +762,7 @@ struct file *open_exec(const char *name)  		.lookup_flags = LOOKUP_FOLLOW,  	}; -	file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags); +	file = do_filp_open(AT_FDCWD, name, &open_exec_flags);  	if (IS_ERR(file))  		goto out; @@ -787,6 +786,12 @@ exit:  	fput(file);  	return ERR_PTR(err);  } + +struct file *open_exec(const char *name) +{ +	struct filename tmp = { .name = name }; +	return do_open_exec(&tmp); +}  EXPORT_SYMBOL(open_exec);  int kernel_read(struct file *file, loff_t offset, @@ -808,7 +813,7 @@ EXPORT_SYMBOL(kernel_read);  ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len)  { -	ssize_t res = file->f_op->read(file, (void __user *)addr, len, &pos); +	ssize_t res = vfs_read(file, (void __user *)addr, len, &pos);  	if (res > 0)  		flush_icache_range(addr, addr + len);  	return res; @@ -818,7 +823,7 @@ EXPORT_SYMBOL(read_code);  static int exec_mmap(struct mm_struct *mm)  {  	struct task_struct *tsk; -	struct mm_struct * old_mm, *active_mm; +	struct mm_struct *old_mm, *active_mm;  	/* Notify parent that we're no longer interested in the old VM */  	tsk = current; @@ -844,8 +849,9 @@ static int exec_mmap(struct mm_struct *mm)  	tsk->mm = mm;  	tsk->active_mm = mm;  	activate_mm(active_mm, mm); +	tsk->mm->vmacache_seqnum = 0; +	vmacache_flush(tsk);  	task_unlock(tsk); -	arch_pick_mmap_layout(mm);  	if (old_mm) {  		up_read(&old_mm->mmap_sem);  		BUG_ON(active_mm != old_mm); @@ -1040,28 +1046,13 @@ EXPORT_SYMBOL_GPL(get_task_comm);   * so that a new one can be started   */ -void set_task_comm(struct task_struct *tsk, char *buf) +void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)  {  	task_lock(tsk);  	trace_task_rename(tsk, buf);  	strlcpy(tsk->comm, buf, sizeof(tsk->comm));  	task_unlock(tsk); -	perf_event_comm(tsk); -} - -static void filename_to_taskname(char *tcomm, const char *fn, unsigned int len) -{ -	int i, ch; - -	/* Copies the binary name from after last slash */ -	for (i = 0; (ch = *(fn++)) != '\0';) { -		if (ch == '/') -			i = 0; /* overwrite what we wrote */ -		else -			if (i < len - 1) -				tcomm[i++] = ch; -	} -	tcomm[i] = '\0'; +	perf_event_comm(tsk, exec);  }  int flush_old_exec(struct linux_binprm * bprm) @@ -1077,8 +1068,6 @@ int flush_old_exec(struct linux_binprm * bprm)  		goto out;  	set_mm_exe_file(bprm->mm, bprm->file); - -	filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm));  	/*  	 * Release all of the old mmap stuff  	 */ @@ -1090,8 +1079,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; @@ -1121,7 +1110,8 @@ void setup_new_exec(struct linux_binprm * bprm)  	else  		set_dumpable(current->mm, suid_dumpable); -	set_task_comm(current, bprm->tcomm); +	perf_event_exec(); +	__set_task_comm(current, kbasename(bprm->filename), true);  	/* Set the new mm task size. We have to do that late because it may  	 * depend on TIF_32BIT which is only updated in flush_thread() on @@ -1141,9 +1131,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);  } @@ -1168,13 +1156,17 @@ int prepare_bprm_creds(struct linux_binprm *bprm)  	return -ENOMEM;  } -void free_bprm(struct linux_binprm *bprm) +static void free_bprm(struct linux_binprm *bprm)  {  	free_arg_pages(bprm);  	if (bprm->cred) {  		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); @@ -1226,11 +1218,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) @@ -1246,44 +1237,35 @@ 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).   */  int prepare_binprm(struct linux_binprm *bprm)  { -	umode_t mode; -	struct inode * inode = file_inode(bprm->file); +	struct inode *inode = file_inode(bprm->file); +	umode_t mode = inode->i_mode;  	int retval; -	mode = inode->i_mode; -	if (bprm->file->f_op == NULL) -		return -EACCES;  	/* clear any previous set[ug]id data from a previous binary */  	bprm->cred->euid = current_euid(); @@ -1385,10 +1367,6 @@ int search_binary_handler(struct linux_binprm *bprm)  	if (retval)  		return retval; -	retval = audit_bprm(bprm); -	if (retval) -		return retval; -  	retval = -ENOENT;   retry:  	read_lock(&binfmt_lock); @@ -1436,16 +1414,10 @@ static int exec_binprm(struct linux_binprm *bprm)  	ret = search_binary_handler(bprm);  	if (ret >= 0) { +		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; @@ -1454,16 +1426,18 @@ static int exec_binprm(struct linux_binprm *bprm)  /*   * sys_execve() executes a new program.   */ -static int do_execve_common(const char *filename, +static int do_execve_common(struct filename *filename,  				struct user_arg_ptr argv,  				struct user_arg_ptr envp)  {  	struct linux_binprm *bprm;  	struct file *file;  	struct files_struct *displaced; -	bool clear_in_exec;  	int retval; +	if (IS_ERR(filename)) +		return PTR_ERR(filename); +  	/*  	 * We move the actual failure in case of RLIMIT_NPROC excess from  	 * set*uid() to execve() because too many poorly written programs @@ -1493,13 +1467,10 @@ 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); +	file = do_open_exec(filename);  	retval = PTR_ERR(file);  	if (IS_ERR(file))  		goto out_unmark; @@ -1507,12 +1478,11 @@ static int do_execve_common(const char *filename,  	sched_exec();  	bprm->file = file; -	bprm->filename = filename; -	bprm->interp = filename; +	bprm->filename = bprm->interp = filename->name;  	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) @@ -1547,7 +1517,9 @@ static int do_execve_common(const char *filename,  	current->fs->in_exec = 0;  	current->in_execve = 0;  	acct_update_integrals(current); +	task_numa_free(current);  	free_bprm(bprm); +	putname(filename);  	if (displaced)  		put_files_struct(displaced);  	return retval; @@ -1558,15 +1530,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: @@ -1576,10 +1541,11 @@ out_files:  	if (displaced)  		reset_files_struct(displaced);  out_ret: +	putname(filename);  	return retval;  } -int do_execve(const char *filename, +int do_execve(struct filename *filename,  	const char __user *const __user *__argv,  	const char __user *const __user *__envp)  { @@ -1589,7 +1555,7 @@ int do_execve(const char *filename,  }  #ifdef CONFIG_COMPAT -static int compat_do_execve(const char *filename, +static int compat_do_execve(struct filename *filename,  	const compat_uptr_t __user *__argv,  	const compat_uptr_t __user *__envp)  { @@ -1616,61 +1582,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; -	} -} +	unsigned long old, new; -int __get_dumpable(unsigned long mm_flags) -{ -	int ret; - -	ret = mm_flags & MMF_DUMPABLE_MASK; -	return (ret > SUID_DUMP_USER) ? SUID_DUMP_ROOT : ret; -} +	if (WARN_ON((unsigned)value > SUID_DUMP_ROOT)) +		return; -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, @@ -1678,25 +1605,13 @@ SYSCALL_DEFINE3(execve,  		const char __user *const __user *, argv,  		const char __user *const __user *, envp)  { -	struct filename *path = getname(filename); -	int error = PTR_ERR(path); -	if (!IS_ERR(path)) { -		error = do_execve(path->name, argv, envp); -		putname(path); -	} -	return error; +	return do_execve(getname(filename), argv, envp);  }  #ifdef CONFIG_COMPAT -asmlinkage long compat_sys_execve(const char __user * filename, -	const compat_uptr_t __user * argv, -	const compat_uptr_t __user * envp) +COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename, +	const compat_uptr_t __user *, argv, +	const compat_uptr_t __user *, envp)  { -	struct filename *path = getname(filename); -	int error = PTR_ERR(path); -	if (!IS_ERR(path)) { -		error = compat_do_execve(path->name, argv, envp); -		putname(path); -	} -	return error; +	return compat_do_execve(getname(filename), argv, envp);  }  #endif  | 
