diff options
author | Eric Paris <eparis@redhat.com> | 2009-07-07 10:28:23 -0400 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2009-07-21 15:26:26 -0400 |
commit | 75fe2b26394c59c8e16bd7b76f4be5d048103ad1 (patch) | |
tree | 0f2a6c59dd1c6eddc497b9c7363298e949c0768a /fs/notify | |
parent | 5549f7cdf84c02939fd368d0842aa2f472bb6e98 (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>
Diffstat (limited to 'fs/notify')
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1a870f9157b3..aff4214f16c3 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -463,9 +463,6 @@ retry: | |||
463 | goto out_err; | 463 | goto out_err; |
464 | 464 | ||
465 | spin_lock(&group->inotify_data.idr_lock); | 465 | spin_lock(&group->inotify_data.idr_lock); |
466 | /* if entry is added to the idr we keep the reference obtained | ||
467 | * through fsnotify_mark_add. remember to drop this reference | ||
468 | * when entry is removed from idr */ | ||
469 | ret = idr_get_new_above(&group->inotify_data.idr, entry, | 466 | ret = idr_get_new_above(&group->inotify_data.idr, entry, |
470 | ++group->inotify_data.last_wd, | 467 | ++group->inotify_data.last_wd, |
471 | &ientry->wd); | 468 | &ientry->wd); |
@@ -476,8 +473,13 @@ retry: | |||
476 | goto out_err; | 473 | goto out_err; |
477 | } | 474 | } |
478 | atomic_inc(&group->inotify_data.user->inotify_watches); | 475 | atomic_inc(&group->inotify_data.user->inotify_watches); |
476 | |||
477 | /* we put the mark on the idr, take a reference */ | ||
478 | fsnotify_get_mark(entry); | ||
479 | } | 479 | } |
480 | 480 | ||
481 | ret = ientry->wd; | ||
482 | |||
481 | spin_lock(&entry->lock); | 483 | spin_lock(&entry->lock); |
482 | 484 | ||
483 | old_mask = entry->mask; | 485 | old_mask = entry->mask; |
@@ -508,7 +510,11 @@ retry: | |||
508 | fsnotify_recalc_group_mask(group); | 510 | fsnotify_recalc_group_mask(group); |
509 | } | 511 | } |
510 | 512 | ||
511 | return ientry->wd; | 513 | /* this either matches fsnotify_find_mark_entry, or init_mark_entry |
514 | * depending on which path we took... */ | ||
515 | fsnotify_put_mark(entry); | ||
516 | |||
517 | return ret; | ||
512 | 518 | ||
513 | out_err: | 519 | out_err: |
514 | /* see this isn't supposed to happen, just kill the watch */ | 520 | /* see this isn't supposed to happen, just kill the watch */ |