summaryrefslogtreecommitdiffstats
path: root/fs/notify/inotify
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2010-05-11 23:17:40 +0200
committerEric Paris <eparis@redhat.com>2010-05-14 17:52:57 +0200
commite08733446e72b983fed850fc5d8bd21b386feb29 (patch)
tree6b169001ff9faa82577341fbfa4cbcd9ccf00663 /fs/notify/inotify
parentinotify: clean up the inotify_add_watch out path (diff)
downloadlinux-e08733446e72b983fed850fc5d8bd21b386feb29.tar.xz
linux-e08733446e72b983fed850fc5d8bd21b386feb29.zip
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>
Diffstat (limited to 'fs/notify/inotify')
-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 40da732eb73c..e46ca685b9be 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -546,21 +546,24 @@ retry:
if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL)))
goto out_err;
+ /* we are putting the mark on the idr, take a reference */
+ fsnotify_get_mark(&tmp_ientry->fsn_entry);
+
spin_lock(&group->inotify_data.idr_lock);
ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry,
group->inotify_data.last_wd+1,
&tmp_ientry->wd);
spin_unlock(&group->inotify_data.idr_lock);
if (ret) {
+ /* we didn't get on the idr, drop the idr reference */
+ fsnotify_put_mark(&tmp_ientry->fsn_entry);
+
/* idr was out of memory allocate and try again */
if (ret == -EAGAIN)
goto retry;
goto out_err;
}
- /* we put the mark on the idr, take a reference */
- fsnotify_get_mark(&tmp_ientry->fsn_entry);
-
/* we are on the idr, now get on the inode */
ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode);
if (ret) {