forked from mirrors/linux
		
	[PATCH] sanitize handling of shared descriptor tables in failing execve()
* unshare_files() can fail; doing it after irreversible actions is wrong and de_thread() is certainly irreversible. * since we do it unconditionally anyway, we might as well do it in do_execve() and save ourselves the PITA in binfmt handlers, etc. * while we are at it, binfmt_som actually leaked files_struct on failure. As a side benefit, unshare_files(), put_files_struct() and reset_files_struct() become unexported. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									6b335d9c80
								
							
						
					
					
						commit
						fd8328be87
					
				
					 6 changed files with 20 additions and 70 deletions
				
			
		| 
						 | 
					@ -543,7 +543,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
	unsigned long interp_load_addr = 0;
 | 
						unsigned long interp_load_addr = 0;
 | 
				
			||||||
	unsigned long start_code, end_code, start_data, end_data;
 | 
						unsigned long start_code, end_code, start_data, end_data;
 | 
				
			||||||
	unsigned long reloc_func_desc = 0;
 | 
						unsigned long reloc_func_desc = 0;
 | 
				
			||||||
	struct files_struct *files;
 | 
					 | 
				
			||||||
	int executable_stack = EXSTACK_DEFAULT;
 | 
						int executable_stack = EXSTACK_DEFAULT;
 | 
				
			||||||
	unsigned long def_flags = 0;
 | 
						unsigned long def_flags = 0;
 | 
				
			||||||
	struct {
 | 
						struct {
 | 
				
			||||||
| 
						 | 
					@ -593,20 +592,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
		goto out_free_ph;
 | 
							goto out_free_ph;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	files = current->files;	/* Refcounted so ok */
 | 
					 | 
				
			||||||
	retval = unshare_files();
 | 
					 | 
				
			||||||
	if (retval < 0)
 | 
					 | 
				
			||||||
		goto out_free_ph;
 | 
					 | 
				
			||||||
	if (files == current->files) {
 | 
					 | 
				
			||||||
		put_files_struct(files);
 | 
					 | 
				
			||||||
		files = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* exec will make our files private anyway, but for the a.out
 | 
					 | 
				
			||||||
	   loader stuff we need to do it earlier */
 | 
					 | 
				
			||||||
	retval = get_unused_fd();
 | 
						retval = get_unused_fd();
 | 
				
			||||||
	if (retval < 0)
 | 
						if (retval < 0)
 | 
				
			||||||
		goto out_free_fh;
 | 
							goto out_free_ph;
 | 
				
			||||||
	get_file(bprm->file);
 | 
						get_file(bprm->file);
 | 
				
			||||||
	fd_install(elf_exec_fileno = retval, bprm->file);
 | 
						fd_install(elf_exec_fileno = retval, bprm->file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -728,12 +716,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		goto out_free_dentry;
 | 
							goto out_free_dentry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Discard our unneeded old files struct */
 | 
					 | 
				
			||||||
	if (files) {
 | 
					 | 
				
			||||||
		put_files_struct(files);
 | 
					 | 
				
			||||||
		files = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* OK, This is the point of no return */
 | 
						/* OK, This is the point of no return */
 | 
				
			||||||
	current->flags &= ~PF_FORKNOEXEC;
 | 
						current->flags &= ~PF_FORKNOEXEC;
 | 
				
			||||||
	current->mm->def_flags = def_flags;
 | 
						current->mm->def_flags = def_flags;
 | 
				
			||||||
| 
						 | 
					@ -1016,9 +998,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
	kfree(elf_interpreter);
 | 
						kfree(elf_interpreter);
 | 
				
			||||||
out_free_file:
 | 
					out_free_file:
 | 
				
			||||||
	sys_close(elf_exec_fileno);
 | 
						sys_close(elf_exec_fileno);
 | 
				
			||||||
out_free_fh:
 | 
					 | 
				
			||||||
	if (files)
 | 
					 | 
				
			||||||
		reset_files_struct(current, files);
 | 
					 | 
				
			||||||
out_free_ph:
 | 
					out_free_ph:
 | 
				
			||||||
	kfree(elf_phdata);
 | 
						kfree(elf_phdata);
 | 
				
			||||||
	goto out;
 | 
						goto out;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
	char *iname_addr = iname;
 | 
						char *iname_addr = iname;
 | 
				
			||||||
	int retval;
 | 
						int retval;
 | 
				
			||||||
	int fd_binary = -1;
 | 
						int fd_binary = -1;
 | 
				
			||||||
	struct files_struct *files = NULL;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retval = -ENOEXEC;
 | 
						retval = -ENOEXEC;
 | 
				
			||||||
	if (!enabled)
 | 
						if (!enabled)
 | 
				
			||||||
| 
						 | 
					@ -133,21 +132,13 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (fmt->flags & MISC_FMT_OPEN_BINARY) {
 | 
						if (fmt->flags & MISC_FMT_OPEN_BINARY) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		files = current->files;
 | 
					 | 
				
			||||||
		retval = unshare_files();
 | 
					 | 
				
			||||||
		if (retval < 0)
 | 
					 | 
				
			||||||
			goto _ret;
 | 
					 | 
				
			||||||
		if (files == current->files) {
 | 
					 | 
				
			||||||
			put_files_struct(files);
 | 
					 | 
				
			||||||
			files = NULL;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		/* if the binary should be opened on behalf of the
 | 
							/* if the binary should be opened on behalf of the
 | 
				
			||||||
		 * interpreter than keep it open and assign descriptor
 | 
							 * interpreter than keep it open and assign descriptor
 | 
				
			||||||
		 * to it */
 | 
							 * to it */
 | 
				
			||||||
 		fd_binary = get_unused_fd();
 | 
					 		fd_binary = get_unused_fd();
 | 
				
			||||||
 		if (fd_binary < 0) {
 | 
					 		if (fd_binary < 0) {
 | 
				
			||||||
 			retval = fd_binary;
 | 
					 			retval = fd_binary;
 | 
				
			||||||
 			goto _unshare;
 | 
					 			goto _ret;
 | 
				
			||||||
 		}
 | 
					 		}
 | 
				
			||||||
 		fd_install(fd_binary, bprm->file);
 | 
					 		fd_install(fd_binary, bprm->file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -205,10 +196,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
	if (retval < 0)
 | 
						if (retval < 0)
 | 
				
			||||||
		goto _error;
 | 
							goto _error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (files) {
 | 
					 | 
				
			||||||
		put_files_struct(files);
 | 
					 | 
				
			||||||
		files = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
_ret:
 | 
					_ret:
 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
_error:
 | 
					_error:
 | 
				
			||||||
| 
						 | 
					@ -216,9 +203,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 | 
				
			||||||
		sys_close(fd_binary);
 | 
							sys_close(fd_binary);
 | 
				
			||||||
	bprm->interp_flags = 0;
 | 
						bprm->interp_flags = 0;
 | 
				
			||||||
	bprm->interp_data = 0;
 | 
						bprm->interp_data = 0;
 | 
				
			||||||
_unshare:
 | 
					 | 
				
			||||||
	if (files)
 | 
					 | 
				
			||||||
		reset_files_struct(current, files);
 | 
					 | 
				
			||||||
	goto _ret;
 | 
						goto _ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 | 
				
			||||||
	unsigned long som_entry;
 | 
						unsigned long som_entry;
 | 
				
			||||||
	struct som_hdr *som_ex;
 | 
						struct som_hdr *som_ex;
 | 
				
			||||||
	struct som_exec_auxhdr *hpuxhdr;
 | 
						struct som_exec_auxhdr *hpuxhdr;
 | 
				
			||||||
	struct files_struct *files;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Get the exec-header */
 | 
						/* Get the exec-header */
 | 
				
			||||||
	som_ex = (struct som_hdr *) bprm->buf;
 | 
						som_ex = (struct som_hdr *) bprm->buf;
 | 
				
			||||||
| 
						 | 
					@ -221,15 +220,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 | 
				
			||||||
		goto out_free;
 | 
							goto out_free;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	files = current->files; /* Refcounted so ok */
 | 
					 | 
				
			||||||
	retval = unshare_files();
 | 
					 | 
				
			||||||
	if (retval < 0)
 | 
					 | 
				
			||||||
		goto out_free;
 | 
					 | 
				
			||||||
	if (files == current->files) {
 | 
					 | 
				
			||||||
		put_files_struct(files);
 | 
					 | 
				
			||||||
		files = NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	retval = get_unused_fd();
 | 
						retval = get_unused_fd();
 | 
				
			||||||
	if (retval < 0)
 | 
						if (retval < 0)
 | 
				
			||||||
		goto out_free;
 | 
							goto out_free;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								fs/exec.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								fs/exec.c
									
									
									
									
									
								
							| 
						 | 
					@ -953,7 +953,6 @@ int flush_old_exec(struct linux_binprm * bprm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char * name;
 | 
						char * name;
 | 
				
			||||||
	int i, ch, retval;
 | 
						int i, ch, retval;
 | 
				
			||||||
	struct files_struct *files;
 | 
					 | 
				
			||||||
	char tcomm[sizeof(current->comm)];
 | 
						char tcomm[sizeof(current->comm)];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
| 
						 | 
					@ -964,27 +963,16 @@ int flush_old_exec(struct linux_binprm * bprm)
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		goto out;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Make sure we have private file handles. Ask the
 | 
					 | 
				
			||||||
	 * fork helper to do the work for us and the exit
 | 
					 | 
				
			||||||
	 * helper to do the cleanup of the old one.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	files = current->files;		/* refcounted so safe to hold */
 | 
					 | 
				
			||||||
	retval = unshare_files();
 | 
					 | 
				
			||||||
	if (retval)
 | 
					 | 
				
			||||||
		goto out;
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Release all of the old mmap stuff
 | 
						 * Release all of the old mmap stuff
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	retval = exec_mmap(bprm->mm);
 | 
						retval = exec_mmap(bprm->mm);
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		goto mmap_failed;
 | 
							goto out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bprm->mm = NULL;		/* We're using it now */
 | 
						bprm->mm = NULL;		/* We're using it now */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* This is the point of no return */
 | 
						/* This is the point of no return */
 | 
				
			||||||
	put_files_struct(files);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	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)
 | 
				
			||||||
| 
						 | 
					@ -1034,8 +1022,6 @@ int flush_old_exec(struct linux_binprm * bprm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mmap_failed:
 | 
					 | 
				
			||||||
	reset_files_struct(current, files);
 | 
					 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1283,12 +1269,23 @@ int do_execve(char * filename,
 | 
				
			||||||
	struct linux_binprm *bprm;
 | 
						struct linux_binprm *bprm;
 | 
				
			||||||
	struct file *file;
 | 
						struct file *file;
 | 
				
			||||||
	unsigned long env_p;
 | 
						unsigned long env_p;
 | 
				
			||||||
 | 
						struct files_struct *files;
 | 
				
			||||||
	int retval;
 | 
						int retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						files = current->files;
 | 
				
			||||||
 | 
						retval = unshare_files();
 | 
				
			||||||
 | 
						if (retval)
 | 
				
			||||||
 | 
							goto out_ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (files == current->files) {
 | 
				
			||||||
 | 
							put_files_struct(files);
 | 
				
			||||||
 | 
							files = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retval = -ENOMEM;
 | 
						retval = -ENOMEM;
 | 
				
			||||||
	bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 | 
						bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
 | 
				
			||||||
	if (!bprm)
 | 
						if (!bprm)
 | 
				
			||||||
		goto out_ret;
 | 
							goto out_files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	file = open_exec(filename);
 | 
						file = open_exec(filename);
 | 
				
			||||||
	retval = PTR_ERR(file);
 | 
						retval = PTR_ERR(file);
 | 
				
			||||||
| 
						 | 
					@ -1343,6 +1340,8 @@ int do_execve(char * filename,
 | 
				
			||||||
		security_bprm_free(bprm);
 | 
							security_bprm_free(bprm);
 | 
				
			||||||
		acct_update_integrals(current);
 | 
							acct_update_integrals(current);
 | 
				
			||||||
		kfree(bprm);
 | 
							kfree(bprm);
 | 
				
			||||||
 | 
							if (files)
 | 
				
			||||||
 | 
								put_files_struct(files);
 | 
				
			||||||
		return retval;
 | 
							return retval;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1363,6 +1362,9 @@ int do_execve(char * filename,
 | 
				
			||||||
out_kfree:
 | 
					out_kfree:
 | 
				
			||||||
	kfree(bprm);
 | 
						kfree(bprm);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_files:
 | 
				
			||||||
 | 
						if (files)
 | 
				
			||||||
 | 
							reset_files_struct(current, files);
 | 
				
			||||||
out_ret:
 | 
					out_ret:
 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -507,8 +507,6 @@ void put_files_struct(struct files_struct *files)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPORT_SYMBOL(put_files_struct);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
 | 
					void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct files_struct *old;
 | 
						struct files_struct *old;
 | 
				
			||||||
| 
						 | 
					@ -519,7 +517,6 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
 | 
				
			||||||
	task_unlock(tsk);
 | 
						task_unlock(tsk);
 | 
				
			||||||
	put_files_struct(old);
 | 
						put_files_struct(old);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(reset_files_struct);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void exit_files(struct task_struct *tsk)
 | 
					void exit_files(struct task_struct *tsk)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -870,8 +870,6 @@ int unshare_files(void)
 | 
				
			||||||
	return error;
 | 
						return error;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXPORT_SYMBOL(unshare_files);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
 | 
					static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct sighand_struct *sig;
 | 
						struct sighand_struct *sig;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue