diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-22 05:31:30 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2008-04-25 09:23:59 -0400 |
commit | 3b1253880b7a9e6db54b943b2d40bcf2202f58ab (patch) | |
tree | 5301be7b4d4310faa8db5a0d027b81421e36570e | |
parent | fd8328be874f4190a811c58cd4778ec2c74d2c05 (diff) |
[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.c | 18 | ||||
-rw-r--r-- | include/linux/file.h | 3 | ||||
-rw-r--r-- | include/linux/fs.h | 3 | ||||
-rw-r--r-- | kernel/exit.c | 3 | ||||
-rw-r--r-- | kernel/fork.c | 54 |
5 files changed, 34 insertions, 47 deletions
@@ -1269,19 +1269,13 @@ int do_execve(char * filename, | |||
1269 | struct linux_binprm *bprm; | 1269 | struct linux_binprm *bprm; |
1270 | struct file *file; | 1270 | struct file *file; |
1271 | unsigned long env_p; | 1271 | unsigned long env_p; |
1272 | struct files_struct *files; | 1272 | struct files_struct *displaced; |
1273 | int retval; | 1273 | int retval; |
1274 | 1274 | ||
1275 | files = current->files; | 1275 | retval = unshare_files(&displaced); |
1276 | retval = unshare_files(); | ||
1277 | if (retval) | 1276 | if (retval) |
1278 | goto out_ret; | 1277 | goto out_ret; |
1279 | 1278 | ||
1280 | if (files == current->files) { | ||
1281 | put_files_struct(files); | ||
1282 | files = NULL; | ||
1283 | } | ||
1284 | |||
1285 | retval = -ENOMEM; | 1279 | retval = -ENOMEM; |
1286 | bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); | 1280 | bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); |
1287 | if (!bprm) | 1281 | if (!bprm) |
@@ -1340,8 +1334,8 @@ int do_execve(char * filename, | |||
1340 | security_bprm_free(bprm); | 1334 | security_bprm_free(bprm); |
1341 | acct_update_integrals(current); | 1335 | acct_update_integrals(current); |
1342 | kfree(bprm); | 1336 | kfree(bprm); |
1343 | if (files) | 1337 | if (displaced) |
1344 | put_files_struct(files); | 1338 | put_files_struct(displaced); |
1345 | return retval; | 1339 | return retval; |
1346 | } | 1340 | } |
1347 | 1341 | ||
@@ -1363,8 +1357,8 @@ out_kfree: | |||
1363 | kfree(bprm); | 1357 | kfree(bprm); |
1364 | 1358 | ||
1365 | out_files: | 1359 | out_files: |
1366 | if (files) | 1360 | if (displaced) |
1367 | reset_files_struct(current, files); | 1361 | reset_files_struct(displaced); |
1368 | out_ret: | 1362 | out_ret: |
1369 | return retval; | 1363 | return retval; |
1370 | } | 1364 | } |
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; | |||
117 | 117 | ||
118 | struct files_struct *get_files_struct(struct task_struct *); | 118 | struct files_struct *get_files_struct(struct task_struct *); |
119 | void put_files_struct(struct files_struct *fs); | 119 | void put_files_struct(struct files_struct *fs); |
120 | void reset_files_struct(struct task_struct *, struct files_struct *); | 120 | void reset_files_struct(struct files_struct *); |
121 | int unshare_files(struct files_struct **); | ||
121 | 122 | ||
122 | extern struct kmem_cache *files_cachep; | 123 | extern struct kmem_cache *files_cachep; |
123 | 124 | ||
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) | |||
2033 | return res; | 2033 | return res; |
2034 | } | 2034 | } |
2035 | 2035 | ||
2036 | /* kernel/fork.c */ | ||
2037 | extern int unshare_files(void); | ||
2038 | |||
2039 | /* Transaction based IO helpers */ | 2036 | /* Transaction based IO helpers */ |
2040 | 2037 | ||
2041 | /* | 2038 | /* |
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) | |||
507 | } | 507 | } |
508 | } | 508 | } |
509 | 509 | ||
510 | void reset_files_struct(struct task_struct *tsk, struct files_struct *files) | 510 | void reset_files_struct(struct files_struct *files) |
511 | { | 511 | { |
512 | struct task_struct *tsk = current; | ||
512 | struct files_struct *old; | 513 | struct files_struct *old; |
513 | 514 | ||
514 | old = tsk->files; | 515 | 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) | |||
840 | return 0; | 840 | return 0; |
841 | } | 841 | } |
842 | 842 | ||
843 | /* | ||
844 | * Helper to unshare the files of the current task. | ||
845 | * We don't want to expose copy_files internals to | ||
846 | * the exec layer of the kernel. | ||
847 | */ | ||
848 | |||
849 | int unshare_files(void) | ||
850 | { | ||
851 | struct files_struct *files = current->files; | ||
852 | struct files_struct *newf; | ||
853 | int error = 0; | ||
854 | |||
855 | BUG_ON(!files); | ||
856 | |||
857 | /* This can race but the race causes us to copy when we don't | ||
858 | need to and drop the copy */ | ||
859 | if(atomic_read(&files->count) == 1) | ||
860 | { | ||
861 | atomic_inc(&files->count); | ||
862 | return 0; | ||
863 | } | ||
864 | newf = dup_fd(files, &error); | ||
865 | if (newf) { | ||
866 | task_lock(current); | ||
867 | current->files = newf; | ||
868 | task_unlock(current); | ||
869 | } | ||
870 | return error; | ||
871 | } | ||
872 | |||
873 | static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) | 843 | static int copy_sighand(unsigned long clone_flags, struct task_struct *tsk) |
874 | { | 844 | { |
875 | struct sighand_struct *sig; | 845 | struct sighand_struct *sig; |
@@ -1807,3 +1777,27 @@ bad_unshare_cleanup_thread: | |||
1807 | bad_unshare_out: | 1777 | bad_unshare_out: |
1808 | return err; | 1778 | return err; |
1809 | } | 1779 | } |
1780 | |||
1781 | /* | ||
1782 | * Helper to unshare the files of the current task. | ||
1783 | * We don't want to expose copy_files internals to | ||
1784 | * the exec layer of the kernel. | ||
1785 | */ | ||
1786 | |||
1787 | int unshare_files(struct files_struct **displaced) | ||
1788 | { | ||
1789 | struct task_struct *task = current; | ||
1790 | struct files_struct *copy; | ||
1791 | int error; | ||
1792 | |||
1793 | error = unshare_fd(CLONE_FILES, ©); | ||
1794 | if (error || !copy) { | ||
1795 | *displaced = NULL; | ||
1796 | return error; | ||
1797 | } | ||
1798 | *displaced = task->files; | ||
1799 | task_lock(task); | ||
1800 | task->files = copy; | ||
1801 | task_unlock(task); | ||
1802 | return 0; | ||
1803 | } | ||