aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorLino Sanfilippo <LinoSanfilippo@gmx.de>2013-07-08 18:59:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-07-09 13:33:19 -0400
commit7b18527c4a95397b443c8c22f75634d5d11c9d47 (patch)
treea701ab062a6f00f8e5c11851ca482b687f65ef10 /fs
parentde1e0c40aceb9d5bff09c3a3b97b2f1b178af53f (diff)
fanotify: fix races when adding/removing marks
For both adding an event to an existing mark and destroying a mark we first have to find it via fsnotify_find_[inode|vfsmount]_mark(). But getting the mark and adding an event (or destroying it) is not done atomically. This opens a race where a thread is about to destroy a mark while another thread still finds the same mark and adds an event to its mask although it will be destroyed. Another race exists concerning the excess of a groups number of marks limit: When a mark is added the number of group marks is checked against the max number of marks per group and increased afterwards. Since check and increment is also not done atomically, this may result in 2 or more processes passing the check at the same time and increasing the number of group marks above the allowed limit. With this patch both races are avoided by doing the concerning operations with the groups mark mutex locked. Signed-off-by: Lino Sanfilippo <LinoSanfilippo@gmx.de> Cc: Eric Paris <eparis@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify_user.c49
1 files changed, 37 insertions, 12 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index e16076d386c4..4e1d8ec77b04 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -524,14 +524,18 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
524 __u32 removed; 524 __u32 removed;
525 int destroy_mark; 525 int destroy_mark;
526 526
527 mutex_lock(&group->mark_mutex);
527 fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); 528 fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
528 if (!fsn_mark) 529 if (!fsn_mark) {
530 mutex_unlock(&group->mark_mutex);
529 return -ENOENT; 531 return -ENOENT;
532 }
530 533
531 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, 534 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
532 &destroy_mark); 535 &destroy_mark);
533 if (destroy_mark) 536 if (destroy_mark)
534 fsnotify_destroy_mark(fsn_mark, group); 537 fsnotify_destroy_mark_locked(fsn_mark, group);
538 mutex_unlock(&group->mark_mutex);
535 539
536 fsnotify_put_mark(fsn_mark); 540 fsnotify_put_mark(fsn_mark);
537 if (removed & real_mount(mnt)->mnt_fsnotify_mask) 541 if (removed & real_mount(mnt)->mnt_fsnotify_mask)
@@ -548,14 +552,19 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
548 __u32 removed; 552 __u32 removed;
549 int destroy_mark; 553 int destroy_mark;
550 554
555 mutex_lock(&group->mark_mutex);
551 fsn_mark = fsnotify_find_inode_mark(group, inode); 556 fsn_mark = fsnotify_find_inode_mark(group, inode);
552 if (!fsn_mark) 557 if (!fsn_mark) {
558 mutex_unlock(&group->mark_mutex);
553 return -ENOENT; 559 return -ENOENT;
560 }
554 561
555 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, 562 removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
556 &destroy_mark); 563 &destroy_mark);
557 if (destroy_mark) 564 if (destroy_mark)
558 fsnotify_destroy_mark(fsn_mark, group); 565 fsnotify_destroy_mark_locked(fsn_mark, group);
566 mutex_unlock(&group->mark_mutex);
567
559 /* matches the fsnotify_find_inode_mark() */ 568 /* matches the fsnotify_find_inode_mark() */
560 fsnotify_put_mark(fsn_mark); 569 fsnotify_put_mark(fsn_mark);
561 if (removed & inode->i_fsnotify_mask) 570 if (removed & inode->i_fsnotify_mask)
@@ -599,21 +608,29 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
599 __u32 added; 608 __u32 added;
600 int ret = 0; 609 int ret = 0;
601 610
611 mutex_lock(&group->mark_mutex);
602 fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); 612 fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
603 if (!fsn_mark) { 613 if (!fsn_mark) {
604 if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) 614 if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) {
615 mutex_unlock(&group->mark_mutex);
605 return -ENOSPC; 616 return -ENOSPC;
617 }
606 618
607 fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); 619 fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
608 if (!fsn_mark) 620 if (!fsn_mark) {
621 mutex_unlock(&group->mark_mutex);
609 return -ENOMEM; 622 return -ENOMEM;
623 }
610 624
611 fsnotify_init_mark(fsn_mark, fanotify_free_mark); 625 fsnotify_init_mark(fsn_mark, fanotify_free_mark);
612 ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); 626 ret = fsnotify_add_mark_locked(fsn_mark, group, NULL, mnt, 0);
613 if (ret) 627 if (ret) {
628 mutex_unlock(&group->mark_mutex);
614 goto err; 629 goto err;
630 }
615 } 631 }
616 added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); 632 added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
633 mutex_unlock(&group->mark_mutex);
617 634
618 if (added & ~real_mount(mnt)->mnt_fsnotify_mask) 635 if (added & ~real_mount(mnt)->mnt_fsnotify_mask)
619 fsnotify_recalc_vfsmount_mask(mnt); 636 fsnotify_recalc_vfsmount_mask(mnt);
@@ -642,21 +659,29 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
642 (atomic_read(&inode->i_writecount) > 0)) 659 (atomic_read(&inode->i_writecount) > 0))
643 return 0; 660 return 0;
644 661
662 mutex_lock(&group->mark_mutex);
645 fsn_mark = fsnotify_find_inode_mark(group, inode); 663 fsn_mark = fsnotify_find_inode_mark(group, inode);
646 if (!fsn_mark) { 664 if (!fsn_mark) {
647 if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) 665 if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) {
666 mutex_unlock(&group->mark_mutex);
648 return -ENOSPC; 667 return -ENOSPC;
668 }
649 669
650 fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); 670 fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
651 if (!fsn_mark) 671 if (!fsn_mark) {
672 mutex_unlock(&group->mark_mutex);
652 return -ENOMEM; 673 return -ENOMEM;
674 }
653 675
654 fsnotify_init_mark(fsn_mark, fanotify_free_mark); 676 fsnotify_init_mark(fsn_mark, fanotify_free_mark);
655 ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); 677 ret = fsnotify_add_mark_locked(fsn_mark, group, inode, NULL, 0);
656 if (ret) 678 if (ret) {
679 mutex_unlock(&group->mark_mutex);
657 goto err; 680 goto err;
681 }
658 } 682 }
659 added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); 683 added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
684 mutex_unlock(&group->mark_mutex);
660 685
661 if (added & ~inode->i_fsnotify_mask) 686 if (added & ~inode->i_fsnotify_mask)
662 fsnotify_recalc_inode_mask(inode); 687 fsnotify_recalc_inode_mask(inode);