diff options
Diffstat (limited to '')
| -rw-r--r-- | fs/binfmt_elf.c | 2 | ||||
| -rw-r--r-- | fs/binfmt_elf_fdpic.c | 5 | ||||
| -rw-r--r-- | fs/binfmt_misc.c | 7 | ||||
| -rw-r--r-- | fs/exec.c | 23 | ||||
| -rw-r--r-- | kernel/fork.c | 26 | 
5 files changed, 49 insertions, 14 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3039a6b7aba4..106f0e8af177 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -1257,6 +1257,7 @@ out_free_interp:  		}  		reloc_func_desc = interp_load_addr; +		allow_write_access(interpreter);  		fput(interpreter);  		kfree(interp_elf_ex); @@ -1353,6 +1354,7 @@ out_free_dentry:  	kfree(interp_elf_ex);  	kfree(interp_elf_phdata);  out_free_file: +	allow_write_access(interpreter);  	if (interpreter)  		fput(interpreter);  out_free_ph: diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 31d253bd3961..f1a7c4875c4a 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -394,6 +394,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)  			goto error;  		} +		allow_write_access(interpreter);  		fput(interpreter);  		interpreter = NULL;  	} @@ -465,8 +466,10 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)  	retval = 0;  error: -	if (interpreter) +	if (interpreter) { +		allow_write_access(interpreter);  		fput(interpreter); +	}  	kfree(interpreter_name);  	kfree(exec_params.phdrs);  	kfree(exec_params.loadmap); diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 31660d8cc2c6..6a3a16f91051 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -247,10 +247,13 @@ static int load_misc_binary(struct linux_binprm *bprm)  	if (retval < 0)  		goto ret; -	if (fmt->flags & MISC_FMT_OPEN_FILE) +	if (fmt->flags & MISC_FMT_OPEN_FILE) {  		interp_file = file_clone_open(fmt->interp_file); -	else +		if (!IS_ERR(interp_file)) +			deny_write_access(interp_file); +	} else {  		interp_file = open_exec(fmt->interpreter); +	}  	retval = PTR_ERR(interp_file);  	if (IS_ERR(interp_file))  		goto ret; diff --git a/fs/exec.c b/fs/exec.c index da51ca70489a..98cb7ba9983c 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -883,7 +883,8 @@ EXPORT_SYMBOL(transfer_args_to_stack);   */  static struct file *do_open_execat(int fd, struct filename *name, int flags)  { -	struct file *file; +	int err; +	struct file *file __free(fput) = NULL;  	struct open_flags open_exec_flags = {  		.open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,  		.acc_mode = MAY_EXEC, @@ -908,12 +909,14 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)  	 * an invariant that all non-regular files error out before we get here.  	 */  	if (WARN_ON_ONCE(!S_ISREG(file_inode(file)->i_mode)) || -	    path_noexec(&file->f_path)) { -		fput(file); +	    path_noexec(&file->f_path))  		return ERR_PTR(-EACCES); -	} -	return file; +	err = deny_write_access(file); +	if (err) +		return ERR_PTR(err); + +	return no_free_ptr(file);  }  /** @@ -923,7 +926,8 @@ static struct file *do_open_execat(int fd, struct filename *name, int flags)   *   * Returns ERR_PTR on failure or allocated struct file on success.   * - * As this is a wrapper for the internal do_open_execat(). Also see + * As this is a wrapper for the internal do_open_execat(), callers + * must call allow_write_access() before fput() on release. Also see   * do_close_execat().   */  struct file *open_exec(const char *name) @@ -1465,8 +1469,10 @@ static int prepare_bprm_creds(struct linux_binprm *bprm)  /* Matches do_open_execat() */  static void do_close_execat(struct file *file)  { -	if (file) -		fput(file); +	if (!file) +		return; +	allow_write_access(file); +	fput(file);  }  static void free_bprm(struct linux_binprm *bprm) @@ -1791,6 +1797,7 @@ static int exec_binprm(struct linux_binprm *bprm)  		bprm->file = bprm->interpreter;  		bprm->interpreter = NULL; +		allow_write_access(exec);  		if (unlikely(bprm->have_execfd)) {  			if (bprm->executable) {  				fput(exec); diff --git a/kernel/fork.c b/kernel/fork.c index f253e81d0c28..1450b461d196 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -621,6 +621,12 @@ static void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm)  	exe_file = get_mm_exe_file(oldmm);  	RCU_INIT_POINTER(mm->exe_file, exe_file); +	/* +	 * We depend on the oldmm having properly denied write access to the +	 * exe_file already. +	 */ +	if (exe_file && deny_write_access(exe_file)) +		pr_warn_once("deny_write_access() failed in %s\n", __func__);  }  #ifdef CONFIG_MMU @@ -1413,11 +1419,20 @@ int set_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)  	 */  	old_exe_file = rcu_dereference_raw(mm->exe_file); -	if (new_exe_file) +	if (new_exe_file) { +		/* +		 * We expect the caller (i.e., sys_execve) to already denied +		 * write access, so this is unlikely to fail. +		 */ +		if (unlikely(deny_write_access(new_exe_file))) +			return -EACCES;  		get_file(new_exe_file); +	}  	rcu_assign_pointer(mm->exe_file, new_exe_file); -	if (old_exe_file) +	if (old_exe_file) { +		allow_write_access(old_exe_file);  		fput(old_exe_file); +	}  	return 0;  } @@ -1456,6 +1471,9 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)  			return ret;  	} +	ret = deny_write_access(new_exe_file); +	if (ret) +		return -EACCES;  	get_file(new_exe_file);  	/* set the new file */ @@ -1464,8 +1482,10 @@ int replace_mm_exe_file(struct mm_struct *mm, struct file *new_exe_file)  	rcu_assign_pointer(mm->exe_file, new_exe_file);  	mmap_write_unlock(mm); -	if (old_exe_file) +	if (old_exe_file) { +		allow_write_access(old_exe_file);  		fput(old_exe_file); +	}  	return 0;  }  | 
