aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2010-05-11 17:17:40 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-05-26 17:32:08 -0400
commit93ff009d2e74e6983fddb4674d339a9a6d09715c (patch)
tree821e6ef260b591d25a811bb85d26db66644e4676
parentdc96a775c1a8e72a4a96d59b8afedc0eeb0c3b21 (diff)
inotify: race use after free/double free in inotify inode marks
commit e08733446e72b983fed850fc5d8bd21b386feb29 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--fs/notify/inotify/inotify_user.c9
1 files changed, 6 insertions, 3 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index a94e8bd8eb1f..75aa15a0be82 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -550,21 +550,24 @@ retry:
550 if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) 550 if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL)))
551 goto out_err; 551 goto out_err;
552 552
553 /* we are putting the mark on the idr, take a reference */
554 fsnotify_get_mark(&tmp_ientry->fsn_entry);
555
553 spin_lock(&group->inotify_data.idr_lock); 556 spin_lock(&group->inotify_data.idr_lock);
554 ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, 557 ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry,
555 group->inotify_data.last_wd+1, 558 group->inotify_data.last_wd+1,
556 &tmp_ientry->wd); 559 &tmp_ientry->wd);
557 spin_unlock(&group->inotify_data.idr_lock); 560 spin_unlock(&group->inotify_data.idr_lock);
558 if (ret) { 561 if (ret) {
562 /* we didn't get on the idr, drop the idr reference */
563 fsnotify_put_mark(&tmp_ientry->fsn_entry);
564
559 /* idr was out of memory allocate and try again */ 565 /* idr was out of memory allocate and try again */
560 if (ret == -EAGAIN) 566 if (ret == -EAGAIN)
561 goto retry; 567 goto retry;
562 goto out_err; 568 goto out_err;
563 } 569 }
564 570
565 /* we put the mark on the idr, take a reference */
566 fsnotify_get_mark(&tmp_ientry->fsn_entry);
567
568 /* we are on the idr, now get on the inode */ 571 /* we are on the idr, now get on the inode */
569 ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); 572 ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
570 if (ret) { 573 if (ret) {