diff options
author | Lino Sanfilippo <LinoSanfilippo@gmx.de> | 2010-11-09 12:18:16 -0500 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2010-12-07 16:14:21 -0500 |
commit | fa218ab98c31eeacd12b89501e6b99d146ea56cc (patch) | |
tree | 789e0f922fbb30844a89a4aaf707c01991c74ea3 | |
parent | b1085ba80cd2784400a7beec3fda5099198ed01c (diff) |
fanotify: correct broken ref counting in case adding a mark failed
If adding a mount or inode mark failed fanotify_free_mark() is called explicitly.
But at this time the mark has already been put into the destroy list of the
fsnotify_mark kernel thread. If the thread is too slow it will try to decrease
the reference of a mark, that has already been freed by fanotify_free_mark().
(If its fast enough it will only decrease the marks ref counter from 2 to 1 - note
that the counter has been increased to 2 in add_mark() - which has practically no
effect.)
This patch fixes the ref counting by not calling free_mark() explicitly, but
decreasing the ref counter and rely on the fsnotify_mark thread to cleanup in
case adding the mark has failed.
Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de>
Signed-off-by: Eric Paris <eparis@redhat.com>
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 31 |
1 files changed, 14 insertions, 17 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 045c0794d435..c0ca1fa1550c 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
@@ -594,11 +594,10 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, | |||
594 | { | 594 | { |
595 | struct fsnotify_mark *fsn_mark; | 595 | struct fsnotify_mark *fsn_mark; |
596 | __u32 added; | 596 | __u32 added; |
597 | int ret = 0; | ||
597 | 598 | ||
598 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); | 599 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); |
599 | if (!fsn_mark) { | 600 | if (!fsn_mark) { |
600 | int ret; | ||
601 | |||
602 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) | 601 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) |
603 | return -ENOSPC; | 602 | return -ENOSPC; |
604 | 603 | ||
@@ -608,17 +607,16 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, | |||
608 | 607 | ||
609 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); | 608 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); |
610 | ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); | 609 | ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); |
611 | if (ret) { | 610 | if (ret) |
612 | fanotify_free_mark(fsn_mark); | 611 | goto err; |
613 | return ret; | ||
614 | } | ||
615 | } | 612 | } |
616 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); | 613 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); |
617 | fsnotify_put_mark(fsn_mark); | 614 | |
618 | if (added & ~mnt->mnt_fsnotify_mask) | 615 | if (added & ~mnt->mnt_fsnotify_mask) |
619 | fsnotify_recalc_vfsmount_mask(mnt); | 616 | fsnotify_recalc_vfsmount_mask(mnt); |
620 | 617 | err: | |
621 | return 0; | 618 | fsnotify_put_mark(fsn_mark); |
619 | return ret; | ||
622 | } | 620 | } |
623 | 621 | ||
624 | static int fanotify_add_inode_mark(struct fsnotify_group *group, | 622 | static int fanotify_add_inode_mark(struct fsnotify_group *group, |
@@ -627,6 +625,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
627 | { | 625 | { |
628 | struct fsnotify_mark *fsn_mark; | 626 | struct fsnotify_mark *fsn_mark; |
629 | __u32 added; | 627 | __u32 added; |
628 | int ret = 0; | ||
630 | 629 | ||
631 | pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); | 630 | pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); |
632 | 631 | ||
@@ -642,8 +641,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
642 | 641 | ||
643 | fsn_mark = fsnotify_find_inode_mark(group, inode); | 642 | fsn_mark = fsnotify_find_inode_mark(group, inode); |
644 | if (!fsn_mark) { | 643 | if (!fsn_mark) { |
645 | int ret; | ||
646 | |||
647 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) | 644 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) |
648 | return -ENOSPC; | 645 | return -ENOSPC; |
649 | 646 | ||
@@ -653,16 +650,16 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
653 | 650 | ||
654 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); | 651 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); |
655 | ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); | 652 | ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); |
656 | if (ret) { | 653 | if (ret) |
657 | fanotify_free_mark(fsn_mark); | 654 | goto err; |
658 | return ret; | ||
659 | } | ||
660 | } | 655 | } |
661 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); | 656 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); |
662 | fsnotify_put_mark(fsn_mark); | 657 | |
663 | if (added & ~inode->i_fsnotify_mask) | 658 | if (added & ~inode->i_fsnotify_mask) |
664 | fsnotify_recalc_inode_mask(inode); | 659 | fsnotify_recalc_inode_mask(inode); |
665 | return 0; | 660 | err: |
661 | fsnotify_put_mark(fsn_mark); | ||
662 | return ret; | ||
666 | } | 663 | } |
667 | 664 | ||
668 | /* fanotify syscalls */ | 665 | /* fanotify syscalls */ |