diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-02-11 18:53:38 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-02-11 18:53:38 -0500 |
commit | 2dab597441667d6c04451a7dcf215241ad4c74f6 (patch) | |
tree | f56a6f7fcabf3a9b82a5e77ef9c96268224efbd1 /fs | |
parent | d2478521afc20227658a10a8c5c2bf1a2aa615b3 (diff) |
Fix possible filp_cachep memory corruption
In commit 31e6b01f4183 ("fs: rcu-walk for path lookup") we started doing
path lookup using RCU, which then falls back to a careful non-RCU lookup
in case of problems (LOOKUP_REVAL). So do_filp_open() has this "re-do
the lookup carefully" looping case.
However, that means that we must not release the open-intent file data
if we are going to loop around and use it once more!
Fix this by moving the release of the open-intent data to the function
that allocates it (do_filp_open() itself) rather than the helper
functions that can get called multiple times (finish_open() and
do_last()). This makes the logic for the lifetime of that field much
more obvious, and avoids the possible double free.
Reported-by: J. R. Okajima <hooanon05@yahoo.co.jp>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: Nick Piggin <npiggin@kernel.dk>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 20 | ||||
-rw-r--r-- | fs/open.c | 2 |
2 files changed, 12 insertions, 10 deletions
diff --git a/fs/namei.c b/fs/namei.c index 7d77f24d32a9..ec4b2d0190a8 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -561,10 +561,14 @@ static inline int nameidata_drop_rcu_last_maybe(struct nameidata *nd) | |||
561 | */ | 561 | */ |
562 | void release_open_intent(struct nameidata *nd) | 562 | void release_open_intent(struct nameidata *nd) |
563 | { | 563 | { |
564 | if (nd->intent.open.file->f_path.dentry == NULL) | 564 | struct file *file = nd->intent.open.file; |
565 | put_filp(nd->intent.open.file); | 565 | |
566 | else | 566 | if (file && !IS_ERR(file)) { |
567 | fput(nd->intent.open.file); | 567 | if (file->f_path.dentry == NULL) |
568 | put_filp(file); | ||
569 | else | ||
570 | fput(file); | ||
571 | } | ||
568 | } | 572 | } |
569 | 573 | ||
570 | /* | 574 | /* |
@@ -2265,8 +2269,6 @@ static struct file *finish_open(struct nameidata *nd, | |||
2265 | return filp; | 2269 | return filp; |
2266 | 2270 | ||
2267 | exit: | 2271 | exit: |
2268 | if (!IS_ERR(nd->intent.open.file)) | ||
2269 | release_open_intent(nd); | ||
2270 | path_put(&nd->path); | 2272 | path_put(&nd->path); |
2271 | return ERR_PTR(error); | 2273 | return ERR_PTR(error); |
2272 | } | 2274 | } |
@@ -2389,8 +2391,6 @@ exit_mutex_unlock: | |||
2389 | exit_dput: | 2391 | exit_dput: |
2390 | path_put_conditional(path, nd); | 2392 | path_put_conditional(path, nd); |
2391 | exit: | 2393 | exit: |
2392 | if (!IS_ERR(nd->intent.open.file)) | ||
2393 | release_open_intent(nd); | ||
2394 | path_put(&nd->path); | 2394 | path_put(&nd->path); |
2395 | return ERR_PTR(error); | 2395 | return ERR_PTR(error); |
2396 | } | 2396 | } |
@@ -2477,6 +2477,7 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
2477 | } | 2477 | } |
2478 | audit_inode(pathname, nd.path.dentry); | 2478 | audit_inode(pathname, nd.path.dentry); |
2479 | filp = finish_open(&nd, open_flag, acc_mode); | 2479 | filp = finish_open(&nd, open_flag, acc_mode); |
2480 | release_open_intent(&nd); | ||
2480 | return filp; | 2481 | return filp; |
2481 | 2482 | ||
2482 | creat: | 2483 | creat: |
@@ -2553,6 +2554,7 @@ out: | |||
2553 | path_put(&nd.root); | 2554 | path_put(&nd.root); |
2554 | if (filp == ERR_PTR(-ESTALE) && !(flags & LOOKUP_REVAL)) | 2555 | if (filp == ERR_PTR(-ESTALE) && !(flags & LOOKUP_REVAL)) |
2555 | goto reval; | 2556 | goto reval; |
2557 | release_open_intent(&nd); | ||
2556 | return filp; | 2558 | return filp; |
2557 | 2559 | ||
2558 | exit_dput: | 2560 | exit_dput: |
@@ -2560,8 +2562,6 @@ exit_dput: | |||
2560 | out_path: | 2562 | out_path: |
2561 | path_put(&nd.path); | 2563 | path_put(&nd.path); |
2562 | out_filp: | 2564 | out_filp: |
2563 | if (!IS_ERR(nd.intent.open.file)) | ||
2564 | release_open_intent(&nd); | ||
2565 | filp = ERR_PTR(error); | 2565 | filp = ERR_PTR(error); |
2566 | goto out; | 2566 | goto out; |
2567 | } | 2567 | } |
@@ -790,6 +790,8 @@ struct file *nameidata_to_filp(struct nameidata *nd) | |||
790 | 790 | ||
791 | /* Pick up the filp from the open intent */ | 791 | /* Pick up the filp from the open intent */ |
792 | filp = nd->intent.open.file; | 792 | filp = nd->intent.open.file; |
793 | nd->intent.open.file = NULL; | ||
794 | |||
793 | /* Has the filesystem initialised the file for us? */ | 795 | /* Has the filesystem initialised the file for us? */ |
794 | if (filp->f_path.dentry == NULL) { | 796 | if (filp->f_path.dentry == NULL) { |
795 | path_get(&nd->path); | 797 | path_get(&nd->path); |