diff options
Diffstat (limited to 'fs/exec.c')
| -rw-r--r-- | fs/exec.c | 114 | 
1 files changed, 101 insertions, 13 deletions
diff --git a/fs/exec.c b/fs/exec.c index 7302b75a9820..ad8798e26be9 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -277,6 +277,7 @@ static int __bprm_mm_init(struct linux_binprm *bprm)  		goto err;  	mm->stack_vm = mm->total_vm = 1; +	arch_bprm_mm_init(mm, vma);  	up_write(&mm->mmap_sem);  	bprm->p = vma->vm_end - sizeof(void *);  	return 0; @@ -747,18 +748,25 @@ EXPORT_SYMBOL(setup_arg_pages);  #endif /* CONFIG_MMU */ -static struct file *do_open_exec(struct filename *name) +static struct file *do_open_execat(int fd, struct filename *name, int flags)  {  	struct file *file;  	int err; -	static const struct open_flags open_exec_flags = { +	struct open_flags open_exec_flags = {  		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,  		.acc_mode = MAY_EXEC | MAY_OPEN,  		.intent = LOOKUP_OPEN,  		.lookup_flags = LOOKUP_FOLLOW,  	}; -	file = do_filp_open(AT_FDCWD, name, &open_exec_flags); +	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) +		return ERR_PTR(-EINVAL); +	if (flags & AT_SYMLINK_NOFOLLOW) +		open_exec_flags.lookup_flags &= ~LOOKUP_FOLLOW; +	if (flags & AT_EMPTY_PATH) +		open_exec_flags.lookup_flags |= LOOKUP_EMPTY; + +	file = do_filp_open(fd, name, &open_exec_flags);  	if (IS_ERR(file))  		goto out; @@ -769,12 +777,13 @@ static struct file *do_open_exec(struct filename *name)  	if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)  		goto exit; -	fsnotify_open(file); -  	err = deny_write_access(file);  	if (err)  		goto exit; +	if (name->name[0] != '\0') +		fsnotify_open(file); +  out:  	return file; @@ -786,7 +795,7 @@ exit:  struct file *open_exec(const char *name)  {  	struct filename tmp = { .name = name }; -	return do_open_exec(&tmp); +	return do_open_execat(AT_FDCWD, &tmp, 0);  }  EXPORT_SYMBOL(open_exec); @@ -1427,10 +1436,12 @@ static int exec_binprm(struct linux_binprm *bprm)  /*   * sys_execve() executes a new program.   */ -static int do_execve_common(struct filename *filename, -				struct user_arg_ptr argv, -				struct user_arg_ptr envp) +static int do_execveat_common(int fd, struct filename *filename, +			      struct user_arg_ptr argv, +			      struct user_arg_ptr envp, +			      int flags)  { +	char *pathbuf = NULL;  	struct linux_binprm *bprm;  	struct file *file;  	struct files_struct *displaced; @@ -1471,7 +1482,7 @@ static int do_execve_common(struct filename *filename,  	check_unsafe_exec(bprm);  	current->in_execve = 1; -	file = do_open_exec(filename); +	file = do_open_execat(fd, filename, flags);  	retval = PTR_ERR(file);  	if (IS_ERR(file))  		goto out_unmark; @@ -1479,7 +1490,28 @@ static int do_execve_common(struct filename *filename,  	sched_exec();  	bprm->file = file; -	bprm->filename = bprm->interp = filename->name; +	if (fd == AT_FDCWD || filename->name[0] == '/') { +		bprm->filename = filename->name; +	} else { +		if (filename->name[0] == '\0') +			pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d", fd); +		else +			pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d/%s", +					    fd, filename->name); +		if (!pathbuf) { +			retval = -ENOMEM; +			goto out_unmark; +		} +		/* +		 * Record that a name derived from an O_CLOEXEC fd will be +		 * inaccessible after exec. Relies on having exclusive access to +		 * current->files (due to unshare_files above). +		 */ +		if (close_on_exec(fd, rcu_dereference_raw(current->files->fdt))) +			bprm->interp_flags |= BINPRM_FLAGS_PATH_INACCESSIBLE; +		bprm->filename = pathbuf; +	} +	bprm->interp = bprm->filename;  	retval = bprm_mm_init(bprm);  	if (retval) @@ -1520,6 +1552,7 @@ static int do_execve_common(struct filename *filename,  	acct_update_integrals(current);  	task_numa_free(current);  	free_bprm(bprm); +	kfree(pathbuf);  	putname(filename);  	if (displaced)  		put_files_struct(displaced); @@ -1537,6 +1570,7 @@ out_unmark:  out_free:  	free_bprm(bprm); +	kfree(pathbuf);  out_files:  	if (displaced) @@ -1552,7 +1586,18 @@ int do_execve(struct filename *filename,  {  	struct user_arg_ptr argv = { .ptr.native = __argv };  	struct user_arg_ptr envp = { .ptr.native = __envp }; -	return do_execve_common(filename, argv, envp); +	return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); +} + +int do_execveat(int fd, struct filename *filename, +		const char __user *const __user *__argv, +		const char __user *const __user *__envp, +		int flags) +{ +	struct user_arg_ptr argv = { .ptr.native = __argv }; +	struct user_arg_ptr envp = { .ptr.native = __envp }; + +	return do_execveat_common(fd, filename, argv, envp, flags);  }  #ifdef CONFIG_COMPAT @@ -1568,7 +1613,23 @@ static int compat_do_execve(struct filename *filename,  		.is_compat = true,  		.ptr.compat = __envp,  	}; -	return do_execve_common(filename, argv, envp); +	return do_execveat_common(AT_FDCWD, filename, argv, envp, 0); +} + +static int compat_do_execveat(int fd, struct filename *filename, +			      const compat_uptr_t __user *__argv, +			      const compat_uptr_t __user *__envp, +			      int flags) +{ +	struct user_arg_ptr argv = { +		.is_compat = true, +		.ptr.compat = __argv, +	}; +	struct user_arg_ptr envp = { +		.is_compat = true, +		.ptr.compat = __envp, +	}; +	return do_execveat_common(fd, filename, argv, envp, flags);  }  #endif @@ -1608,6 +1669,20 @@ SYSCALL_DEFINE3(execve,  {  	return do_execve(getname(filename), argv, envp);  } + +SYSCALL_DEFINE5(execveat, +		int, fd, const char __user *, filename, +		const char __user *const __user *, argv, +		const char __user *const __user *, envp, +		int, flags) +{ +	int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + +	return do_execveat(fd, +			   getname_flags(filename, lookup_flags, NULL), +			   argv, envp, flags); +} +  #ifdef CONFIG_COMPAT  COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename,  	const compat_uptr_t __user *, argv, @@ -1615,4 +1690,17 @@ COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename,  {  	return compat_do_execve(getname(filename), argv, envp);  } + +COMPAT_SYSCALL_DEFINE5(execveat, int, fd, +		       const char __user *, filename, +		       const compat_uptr_t __user *, argv, +		       const compat_uptr_t __user *, envp, +		       int,  flags) +{ +	int lookup_flags = (flags & AT_EMPTY_PATH) ? LOOKUP_EMPTY : 0; + +	return compat_do_execveat(fd, +				  getname_flags(filename, lookup_flags, NULL), +				  argv, envp, flags); +}  #endif  |