From fd8328be874f4190a811c58cd4778ec2c74d2c05 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 22 Apr 2008 05:11:59 -0400 Subject: [PATCH] [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 --- fs/binfmt_elf.c | 23 +---------------------- fs/binfmt_misc.c | 18 +----------------- fs/binfmt_som.c | 10 ---------- fs/exec.c | 34 ++++++++++++++++++---------------- kernel/exit.c | 3 --- kernel/fork.c | 2 -- 6 files changed, 20 insertions(+), 70 deletions(-) diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5e1a4fb5cacb2a..9924581df6f6cc 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -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 start_code, end_code, start_data, end_data; unsigned long reloc_func_desc = 0; - struct files_struct *files; int executable_stack = EXSTACK_DEFAULT; unsigned long def_flags = 0; struct { @@ -593,20 +592,9 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) 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(); if (retval < 0) - goto out_free_fh; + goto out_free_ph; get_file(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) 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 */ current->flags &= ~PF_FORKNOEXEC; 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); out_free_file: sys_close(elf_exec_fileno); -out_free_fh: - if (files) - reset_files_struct(current, files); out_free_ph: kfree(elf_phdata); goto out; diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index b53c7e5f41bbcf..dbf0ac0523de14 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -110,7 +110,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) char *iname_addr = iname; int retval; int fd_binary = -1; - struct files_struct *files = NULL; retval = -ENOEXEC; 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) { - 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 * interpreter than keep it open and assign descriptor * to it */ fd_binary = get_unused_fd(); if (fd_binary < 0) { retval = fd_binary; - goto _unshare; + goto _ret; } 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) goto _error; - if (files) { - put_files_struct(files); - files = NULL; - } _ret: return retval; _error: @@ -216,9 +203,6 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) sys_close(fd_binary); bprm->interp_flags = 0; bprm->interp_data = 0; -_unshare: - if (files) - reset_files_struct(current, files); goto _ret; } diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c index 14c63527c762d8..fdc36bfd6a7beb 100644 --- a/fs/binfmt_som.c +++ b/fs/binfmt_som.c @@ -194,7 +194,6 @@ load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned long som_entry; struct som_hdr *som_ex; struct som_exec_auxhdr *hpuxhdr; - struct files_struct *files; /* Get the exec-header */ 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; } - 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(); if (retval < 0) goto out_free; diff --git a/fs/exec.c b/fs/exec.c index 54a0a557b6781e..475543002f1384 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -953,7 +953,6 @@ int flush_old_exec(struct linux_binprm * bprm) { char * name; int i, ch, retval; - struct files_struct *files; char tcomm[sizeof(current->comm)]; /* @@ -964,27 +963,16 @@ int flush_old_exec(struct linux_binprm * bprm) if (retval) 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 */ retval = exec_mmap(bprm->mm); if (retval) - goto mmap_failed; + goto out; bprm->mm = NULL; /* We're using it now */ /* This is the point of no return */ - put_files_struct(files); - current->sas_ss_sp = current->sas_ss_size = 0; if (current->euid == current->uid && current->egid == current->gid) @@ -1034,8 +1022,6 @@ int flush_old_exec(struct linux_binprm * bprm) return 0; -mmap_failed: - reset_files_struct(current, files); out: return retval; } @@ -1283,12 +1269,23 @@ int do_execve(char * filename, struct linux_binprm *bprm; struct file *file; unsigned long env_p; + struct files_struct *files; 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; bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); if (!bprm) - goto out_ret; + goto out_files; file = open_exec(filename); retval = PTR_ERR(file); @@ -1343,6 +1340,8 @@ int do_execve(char * filename, security_bprm_free(bprm); acct_update_integrals(current); kfree(bprm); + if (files) + put_files_struct(files); return retval; } @@ -1363,6 +1362,9 @@ int do_execve(char * filename, out_kfree: kfree(bprm); +out_files: + if (files) + reset_files_struct(current, files); out_ret: return retval; } diff --git a/kernel/exit.c b/kernel/exit.c index cece89f80ab4e0..3d320003cc03f6 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -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) { struct files_struct *old; @@ -519,7 +517,6 @@ void reset_files_struct(struct task_struct *tsk, struct files_struct *files) task_unlock(tsk); put_files_struct(old); } -EXPORT_SYMBOL(reset_files_struct); void exit_files(struct task_struct *tsk) { diff --git a/kernel/fork.c b/kernel/fork.c index 76f05a08062bab..2fc11f2e2b21a7 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -870,8 +870,6 @@ int unshare_files(void) return error; } -EXPORT_SYMBOL(unshare_files); - static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) { struct sighand_struct *sig;