diff options
author | Eric Paris <eparis@redhat.com> | 2010-05-11 17:17:40 -0400 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2010-05-14 11:52:57 -0400 |
commit | e08733446e72b983fed850fc5d8bd21b386feb29 (patch) | |
tree | 6b169001ff9faa82577341fbfa4cbcd9ccf00663 | |
parent | 3dbc6fb6a3c8a7dc164ae330ab024a3fe65ae53e (diff) |
inotify: race use after free/double free in inotify inode marks
There is a race in the inotify add/rm watch code. A task can find and
remove a mark which doesn't have all of it's references. This can
result in a use after free/double free situation.
Task A Task B
------------ -----------
inotify_new_watch()
allocate a mark (refcnt == 1)
add it to the idr
inotify_rm_watch()
inotify_remove_from_idr()
fsnotify_put_mark()
refcnt hits 0, free
take reference because we are on idr
[at this point it is a use after free]
[time goes on]
refcnt may hit 0 again, double free
The fix is to take the reference BEFORE the object can be found in the
idr.
Signed-off-by: Eric Paris <eparis@redhat.com>
Cc: <stable@kernel.org>
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 40da732eb73c..e46ca685b9be 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -546,21 +546,24 @@ retry: | |||
546 | if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) | 546 | if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) |
547 | goto out_err; | 547 | goto out_err; |
548 | 548 | ||
549 | /* we are putting the mark on the idr, take a reference */ | ||
550 | fsnotify_get_mark(&tmp_ientry->fsn_entry); | ||
551 | |||
549 | spin_lock(&group->inotify_data.idr_lock); | 552 | spin_lock(&group->inotify_data.idr_lock); |
550 | ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, | 553 | ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, |
551 | group->inotify_data.last_wd+1, | 554 | group->inotify_data.last_wd+1, |
552 | &tmp_ientry->wd); | 555 | &tmp_ientry->wd); |
553 | spin_unlock(&group->inotify_data.idr_lock); | 556 | spin_unlock(&group->inotify_data.idr_lock); |
554 | if (ret) { | 557 | if (ret) { |
558 | /* we didn't get on the idr, drop the idr reference */ | ||
559 | fsnotify_put_mark(&tmp_ientry->fsn_entry); | ||
560 | |||
555 | /* idr was out of memory allocate and try again */ | 561 | /* idr was out of memory allocate and try again */ |
556 | if (ret == -EAGAIN) | 562 | if (ret == -EAGAIN) |
557 | goto retry; | 563 | goto retry; |
558 | goto out_err; | 564 | goto out_err; |
559 | } | 565 | } |
560 | 566 | ||
561 | /* we put the mark on the idr, take a reference */ | ||
562 | fsnotify_get_mark(&tmp_ientry->fsn_entry); | ||
563 | |||
564 | /* we are on the idr, now get on the inode */ | 567 | /* we are on the idr, now get on the inode */ |
565 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); | 568 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); |
566 | if (ret) { | 569 | if (ret) { |