[PATCH] sanitize unshare_files/reset_files_struct
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 22 Apr 2008 09:31:30 +0000 (05:31 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 25 Apr 2008 13:23:59 +0000 (09:23 -0400)
* let unshare_files() give caller the displaced files_struct
* don't bother with grabbing reference only to drop it in the
  caller if it hadn't been shared in the first place
* in that form unshare_files() is trivially implemented via
  unshare_fd(), so we eliminate the duplicate logics in fork.c
* reset_files_struct() is not just only called for current;
  it will break the system if somebody ever calls it for anything
  else (we can't modify ->files of somebody else).  Lose the
  task_struct * argument.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/exec.c
include/linux/file.h
include/linux/fs.h
kernel/exit.c
kernel/fork.c

index 475543002f13847ad7e2ef42eac0d1313c7a837a..b152029f18f61e68e260d63bab4a9720f2e31ab6 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1269,19 +1269,13 @@ int do_execve(char * filename,
        struct linux_binprm *bprm;
        struct file *file;
        unsigned long env_p;
-       struct files_struct *files;
+       struct files_struct *displaced;
        int retval;
 
-       files = current->files;
-       retval = unshare_files();
+       retval = unshare_files(&displaced);
        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)
@@ -1340,8 +1334,8 @@ int do_execve(char * filename,
                security_bprm_free(bprm);
                acct_update_integrals(current);
                kfree(bprm);
-               if (files)
-                       put_files_struct(files);
+               if (displaced)
+                       put_files_struct(displaced);
                return retval;
        }
 
@@ -1363,8 +1357,8 @@ out_kfree:
        kfree(bprm);
 
 out_files:
-       if (files)
-               reset_files_struct(current, files);
+       if (displaced)
+               reset_files_struct(displaced);
 out_ret:
        return retval;
 }
index 653477021e4c545b9f4dcc983587ceb26eb11eac..69baf5a4f0a582cfeb061af5735e4a2d70e701e6 100644 (file)
@@ -117,7 +117,8 @@ struct task_struct;
 
 struct files_struct *get_files_struct(struct task_struct *);
 void put_files_struct(struct files_struct *fs);
-void reset_files_struct(struct task_struct *, struct files_struct *);
+void reset_files_struct(struct files_struct *);
+int unshare_files(struct files_struct **);
 
 extern struct kmem_cache *files_cachep;
 
index ad41d0bbcb4dd437514f0e655f6adfa2869448f9..e057438a05ad56a2acb70ed770e674da41f40a63 100644 (file)
@@ -2033,9 +2033,6 @@ static inline ino_t parent_ino(struct dentry *dentry)
        return res;
 }
 
-/* kernel/fork.c */
-extern int unshare_files(void);
-
 /* Transaction based IO helpers */
 
 /*
index 3d320003cc03f627e45d4328d53def9985333a77..97f609f574b1843556a6a07fc4c6c50180725b31 100644 (file)
@@ -507,8 +507,9 @@ void put_files_struct(struct files_struct *files)
        }
 }
 
-void reset_files_struct(struct task_struct *tsk, struct files_struct *files)
+void reset_files_struct(struct files_struct *files)
 {
+       struct task_struct *tsk = current;
        struct files_struct *old;
 
        old = tsk->files;
index 2fc11f2e2b21a7c5d19d347774de930d9e4bc0ee..efb618fc8ffe4724aafa4d14ecb324a12df1ddea 100644 (file)
@@ -840,36 +840,6 @@ static int copy_io(unsigned long clone_flags, struct task_struct *tsk)
        return 0;
 }
 
-/*
- *     Helper to unshare the files of the current task.
- *     We don't want to expose copy_files internals to
- *     the exec layer of the kernel.
- */
-
-int unshare_files(void)
-{
-       struct files_struct *files  = current->files;
-       struct files_struct *newf;
-       int error = 0;
-
-       BUG_ON(!files);
-
-       /* This can race but the race causes us to copy when we don't
-          need to and drop the copy */
-       if(atomic_read(&files->count) == 1)
-       {
-               atomic_inc(&files->count);
-               return 0;
-       }
-       newf = dup_fd(files, &error);
-       if (newf) {
-               task_lock(current);
-               current->files = newf;
-               task_unlock(current);
-       }
-       return error;
-}
-
 static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk)
 {
        struct sighand_struct *sig;
@@ -1807,3 +1777,27 @@ bad_unshare_cleanup_thread:
 bad_unshare_out:
        return err;
 }
+
+/*
+ *     Helper to unshare the files of the current task.
+ *     We don't want to expose copy_files internals to
+ *     the exec layer of the kernel.
+ */
+
+int unshare_files(struct files_struct **displaced)
+{
+       struct task_struct *task = current;
+       struct files_struct *copy;
+       int error;
+
+       error = unshare_fd(CLONE_FILES, &copy);
+       if (error || !copy) {
+               *displaced = NULL;
+               return error;
+       }
+       *displaced = task->files;
+       task_lock(task);
+       task->files = copy;
+       task_unlock(task);
+       return 0;
+}