aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2008-04-22 05:31:30 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2008-04-25 09:23:59 -0400
commit3b1253880b7a9e6db54b943b2d40bcf2202f58ab (patch)
tree5301be7b4d4310faa8db5a0d027b81421e36570e
parent[PATCH] sanitize handling of shared descriptor tables in failing execve() (diff)
downloadlinux-dev-3b1253880b7a9e6db54b943b2d40bcf2202f58ab.tar.xz
linux-dev-3b1253880b7a9e6db54b943b2d40bcf2202f58ab.zip
[PATCH] sanitize unshare_files/reset_files_struct
* 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>
-rw-r--r--fs/exec.c18
-rw-r--r--include/linux/file.h3
-rw-r--r--include/linux/fs.h3
-rw-r--r--kernel/exit.c3
-rw-r--r--kernel/fork.c54
5 files changed, 34 insertions, 47 deletions
diff --git a/fs/exec.c b/fs/exec.c
index 475543002f13..b152029f18f6 100644
--- 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;
}
diff --git a/include/linux/file.h b/include/linux/file.h
index 653477021e4c..69baf5a4f0a5 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
@@ -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;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index ad41d0bbcb4d..e057438a05ad 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -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 */
/*
diff --git a/kernel/exit.c b/kernel/exit.c
index 3d320003cc03..97f609f574b1 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -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;
diff --git a/kernel/fork.c b/kernel/fork.c
index 2fc11f2e2b21..efb618fc8ffe 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -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;
+}