aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLino Sanfilippo <LinoSanfilippo@gmx.de>2010-11-09 12:18:16 -0500
committerEric Paris <eparis@redhat.com>2010-12-07 16:14:21 -0500
commitfa218ab98c31eeacd12b89501e6b99d146ea56cc (patch)
tree789e0f922fbb30844a89a4aaf707c01991c74ea3
parentb1085ba80cd2784400a7beec3fda5099198ed01c (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.c31
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 617err:
621 return 0; 618 fsnotify_put_mark(fsn_mark);
619 return ret;
622} 620}
623 621
624static int fanotify_add_inode_mark(struct fsnotify_group *group, 622static 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; 660err:
661 fsnotify_put_mark(fsn_mark);
662 return ret;
666} 663}
667 664
668/* fanotify syscalls */ 665/* fanotify syscalls */