[PATCH] sanitize handling of shared descriptor tables in failing execve()
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 22 Apr 2008 09:11:59 +0000 (05:11 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 25 Apr 2008 13:23:53 +0000 (09:23 -0400)
* 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>
fs/binfmt_elf.c
fs/binfmt_misc.c
fs/binfmt_som.c
fs/exec.c
kernel/exit.c
kernel/fork.c

index 5e1a4fb5cacb2a3715ad3552bd8e412fdae9b00c..9924581df6f6ccbc35520237cbdf8c9c079559f8 100644 (file)
@@ -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 @@ out_free_interp:
        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;
index b53c7e5f41bbcffd8f936642117ecfce5f258a21..dbf0ac0523de14d49edcc58b43ab7c62a407e406 100644 (file)
@@ -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 @@ _error:
                sys_close(fd_binary);
        bprm->interp_flags = 0;
        bprm->interp_data = 0;
-_unshare:
-       if (files)
-               reset_files_struct(current, files);
        goto _ret;
 }
 
index 14c63527c762d8cdc8fc443dec512bd4f34e194f..fdc36bfd6a7beb8d1b63b91520ece0a7d2a347b7 100644 (file)
@@ -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;
index 54a0a557b6781ecf379015bbd60dc6f3fd37c176..475543002f13847ad7e2ef42eac0d1313c7a837a 100644 (file)
--- 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 @@ out_file:
 out_kfree:
        kfree(bprm);
 
+out_files:
+       if (files)
+               reset_files_struct(current, files);
 out_ret:
        return retval;
 }
index cece89f80ab4e0ed78d5c687a78b0ab1ced426b8..3d320003cc03f627e45d4328d53def9985333a77 100644 (file)
@@ -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)
 {
index 76f05a08062bab82c72037827b1189435e89503e..2fc11f2e2b21a7c5d19d347774de930d9e4bc0ee 100644 (file)
@@ -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;