aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-07-07 10:28:23 -0400
committerEric Paris <eparis@redhat.com>2009-07-21 15:26:26 -0400
commit75fe2b26394c59c8e16bd7b76f4be5d048103ad1 (patch)
tree0f2a6c59dd1c6eddc497b9c7363298e949c0768a
parent5549f7cdf84c02939fd368d0842aa2f472bb6e98 (diff)
inotify: do not leak inode marks in inotify_add_watch
inotify_add_watch had a couple of problems. The biggest being that if inotify_add_watch was called on the same inode twice (to update or change the event mask) a refence was taken on the original inode mark by fsnotify_find_mark_entry but was not being dropped at the end of the inotify_add_watch call. Thus if inotify_rm_watch was called although the mark was removed from the inode, the refcnt wouldn't hit zero and we would leak memory. Reported-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Eric Paris <eparis@redhat.com>
-rw-r--r--fs/notify/inotify/inotify_user.c14
1 files changed, 10 insertions, 4 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 1a870f9157b..aff4214f16c 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -463,9 +463,6 @@ retry:
goto out_err;
spin_lock(&group->inotify_data.idr_lock);
- /* if entry is added to the idr we keep the reference obtained
- * through fsnotify_mark_add. remember to drop this reference
- * when entry is removed from idr */
ret = idr_get_new_above(&group->inotify_data.idr, entry,
++group->inotify_data.last_wd,
&ientry->wd);
@@ -476,8 +473,13 @@ retry:
goto out_err;
}
atomic_inc(&group->inotify_data.user->inotify_watches);
+
+ /* we put the mark on the idr, take a reference */
+ fsnotify_get_mark(entry);
}
+ ret = ientry->wd;
+
spin_lock(&entry->lock);
old_mask = entry->mask;
@@ -508,7 +510,11 @@ retry:
fsnotify_recalc_group_mask(group);
}
- return ientry->wd;
+ /* this either matches fsnotify_find_mark_entry, or init_mark_entry
+ * depending on which path we took... */
+ fsnotify_put_mark(entry);
+
+ return ret;
out_err:
/* see this isn't supposed to happen, just kill the watch */