diff options
| -rw-r--r-- | fs/namei.c | 3 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify.c | 6 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 81 | ||||
| -rw-r--r-- | fs/notify/inotify/inotify_user.c | 1 | ||||
| -rw-r--r-- | include/linux/fanotify.h | 10 | ||||
| -rw-r--r-- | include/linux/fsnotify.h | 3 | ||||
| -rw-r--r-- | include/linux/fsnotify_backend.h | 2 |
7 files changed, 68 insertions, 38 deletions
diff --git a/fs/namei.c b/fs/namei.c index 5362af9b737..4ff7ca53053 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -1748,6 +1748,9 @@ struct file *do_filp_open(int dfd, const char *pathname, | |||
| 1748 | if (!(open_flag & O_CREAT)) | 1748 | if (!(open_flag & O_CREAT)) |
| 1749 | mode = 0; | 1749 | mode = 0; |
| 1750 | 1750 | ||
| 1751 | /* Must never be set by userspace */ | ||
| 1752 | open_flag &= ~FMODE_NONOTIFY; | ||
| 1753 | |||
| 1751 | /* | 1754 | /* |
| 1752 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only | 1755 | * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only |
| 1753 | * check for O_DSYNC if the need any syncing at all we enforce it's | 1756 | * check for O_DSYNC if the need any syncing at all we enforce it's |
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index b04f88eed09..f35794b97e8 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
| @@ -92,7 +92,11 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, | |||
| 92 | 92 | ||
| 93 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 93 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
| 94 | 94 | ||
| 95 | wait_event(group->fanotify_data.access_waitq, event->response); | 95 | wait_event(group->fanotify_data.access_waitq, event->response || |
| 96 | atomic_read(&group->fanotify_data.bypass_perm)); | ||
| 97 | |||
| 98 | if (!event->response) /* bypass_perm set */ | ||
| 99 | return 0; | ||
| 96 | 100 | ||
| 97 | /* userspace responded, convert to something usable */ | 101 | /* userspace responded, convert to something usable */ |
| 98 | spin_lock(&event->lock); | 102 | spin_lock(&event->lock); |
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 063224812b7..8b61220cffc 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
| @@ -106,20 +106,29 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) | |||
| 106 | return client_fd; | 106 | return client_fd; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | static ssize_t fill_event_metadata(struct fsnotify_group *group, | 109 | static int fill_event_metadata(struct fsnotify_group *group, |
| 110 | struct fanotify_event_metadata *metadata, | 110 | struct fanotify_event_metadata *metadata, |
| 111 | struct fsnotify_event *event) | 111 | struct fsnotify_event *event) |
| 112 | { | 112 | { |
| 113 | int ret = 0; | ||
| 114 | |||
| 113 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, | 115 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, |
| 114 | group, metadata, event); | 116 | group, metadata, event); |
| 115 | 117 | ||
| 116 | metadata->event_len = FAN_EVENT_METADATA_LEN; | 118 | metadata->event_len = FAN_EVENT_METADATA_LEN; |
| 119 | metadata->metadata_len = FAN_EVENT_METADATA_LEN; | ||
| 117 | metadata->vers = FANOTIFY_METADATA_VERSION; | 120 | metadata->vers = FANOTIFY_METADATA_VERSION; |
| 118 | metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; | 121 | metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; |
| 119 | metadata->pid = pid_vnr(event->tgid); | 122 | metadata->pid = pid_vnr(event->tgid); |
| 120 | metadata->fd = create_fd(group, event); | 123 | if (unlikely(event->mask & FAN_Q_OVERFLOW)) |
| 124 | metadata->fd = FAN_NOFD; | ||
| 125 | else { | ||
| 126 | metadata->fd = create_fd(group, event); | ||
| 127 | if (metadata->fd < 0) | ||
| 128 | ret = metadata->fd; | ||
| 129 | } | ||
| 121 | 130 | ||
| 122 | return metadata->fd; | 131 | return ret; |
| 123 | } | 132 | } |
| 124 | 133 | ||
| 125 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 134 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
| @@ -200,7 +209,7 @@ static int prepare_for_access_response(struct fsnotify_group *group, | |||
| 200 | 209 | ||
| 201 | mutex_lock(&group->fanotify_data.access_mutex); | 210 | mutex_lock(&group->fanotify_data.access_mutex); |
| 202 | 211 | ||
| 203 | if (group->fanotify_data.bypass_perm) { | 212 | if (atomic_read(&group->fanotify_data.bypass_perm)) { |
| 204 | mutex_unlock(&group->fanotify_data.access_mutex); | 213 | mutex_unlock(&group->fanotify_data.access_mutex); |
| 205 | kmem_cache_free(fanotify_response_event_cache, re); | 214 | kmem_cache_free(fanotify_response_event_cache, re); |
| 206 | event->response = FAN_ALLOW; | 215 | event->response = FAN_ALLOW; |
| @@ -257,24 +266,34 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, | |||
| 257 | 266 | ||
| 258 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 267 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
| 259 | 268 | ||
| 260 | fd = fill_event_metadata(group, &fanotify_event_metadata, event); | 269 | ret = fill_event_metadata(group, &fanotify_event_metadata, event); |
| 261 | if (fd < 0) | 270 | if (ret < 0) |
| 262 | return fd; | 271 | goto out; |
| 263 | 272 | ||
| 273 | fd = fanotify_event_metadata.fd; | ||
| 264 | ret = prepare_for_access_response(group, event, fd); | 274 | ret = prepare_for_access_response(group, event, fd); |
| 265 | if (ret) | 275 | if (ret) |
| 266 | goto out_close_fd; | 276 | goto out_close_fd; |
| 267 | 277 | ||
| 268 | ret = -EFAULT; | 278 | ret = -EFAULT; |
| 269 | if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) | 279 | if (copy_to_user(buf, &fanotify_event_metadata, |
| 280 | fanotify_event_metadata.event_len)) | ||
| 270 | goto out_kill_access_response; | 281 | goto out_kill_access_response; |
| 271 | 282 | ||
| 272 | return FAN_EVENT_METADATA_LEN; | 283 | return fanotify_event_metadata.event_len; |
| 273 | 284 | ||
| 274 | out_kill_access_response: | 285 | out_kill_access_response: |
| 275 | remove_access_response(group, event, fd); | 286 | remove_access_response(group, event, fd); |
| 276 | out_close_fd: | 287 | out_close_fd: |
| 277 | sys_close(fd); | 288 | if (fd != FAN_NOFD) |
| 289 | sys_close(fd); | ||
| 290 | out: | ||
| 291 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | ||
| 292 | if (event->mask & FAN_ALL_PERM_EVENTS) { | ||
| 293 | event->response = FAN_DENY; | ||
| 294 | wake_up(&group->fanotify_data.access_waitq); | ||
| 295 | } | ||
| 296 | #endif | ||
| 278 | return ret; | 297 | return ret; |
| 279 | } | 298 | } |
| 280 | 299 | ||
| @@ -382,7 +401,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) | |||
| 382 | 401 | ||
| 383 | mutex_lock(&group->fanotify_data.access_mutex); | 402 | mutex_lock(&group->fanotify_data.access_mutex); |
| 384 | 403 | ||
| 385 | group->fanotify_data.bypass_perm = true; | 404 | atomic_inc(&group->fanotify_data.bypass_perm); |
| 386 | 405 | ||
| 387 | list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { | 406 | list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { |
| 388 | pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, | 407 | pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, |
| @@ -586,11 +605,10 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, | |||
| 586 | { | 605 | { |
| 587 | struct fsnotify_mark *fsn_mark; | 606 | struct fsnotify_mark *fsn_mark; |
| 588 | __u32 added; | 607 | __u32 added; |
| 608 | int ret = 0; | ||
| 589 | 609 | ||
| 590 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); | 610 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); |
| 591 | if (!fsn_mark) { | 611 | if (!fsn_mark) { |
| 592 | int ret; | ||
| 593 | |||
| 594 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) | 612 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) |
| 595 | return -ENOSPC; | 613 | return -ENOSPC; |
| 596 | 614 | ||
| @@ -600,17 +618,16 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, | |||
| 600 | 618 | ||
| 601 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); | 619 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); |
| 602 | ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); | 620 | ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); |
| 603 | if (ret) { | 621 | if (ret) |
| 604 | fanotify_free_mark(fsn_mark); | 622 | goto err; |
| 605 | return ret; | ||
| 606 | } | ||
| 607 | } | 623 | } |
| 608 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); | 624 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); |
| 609 | fsnotify_put_mark(fsn_mark); | 625 | |
| 610 | if (added & ~mnt->mnt_fsnotify_mask) | 626 | if (added & ~mnt->mnt_fsnotify_mask) |
| 611 | fsnotify_recalc_vfsmount_mask(mnt); | 627 | fsnotify_recalc_vfsmount_mask(mnt); |
| 612 | 628 | err: | |
| 613 | return 0; | 629 | fsnotify_put_mark(fsn_mark); |
| 630 | return ret; | ||
| 614 | } | 631 | } |
| 615 | 632 | ||
| 616 | static int fanotify_add_inode_mark(struct fsnotify_group *group, | 633 | static int fanotify_add_inode_mark(struct fsnotify_group *group, |
| @@ -619,6 +636,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
| 619 | { | 636 | { |
| 620 | struct fsnotify_mark *fsn_mark; | 637 | struct fsnotify_mark *fsn_mark; |
| 621 | __u32 added; | 638 | __u32 added; |
| 639 | int ret = 0; | ||
| 622 | 640 | ||
| 623 | pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); | 641 | pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); |
| 624 | 642 | ||
| @@ -634,8 +652,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
| 634 | 652 | ||
| 635 | fsn_mark = fsnotify_find_inode_mark(group, inode); | 653 | fsn_mark = fsnotify_find_inode_mark(group, inode); |
| 636 | if (!fsn_mark) { | 654 | if (!fsn_mark) { |
| 637 | int ret; | ||
| 638 | |||
| 639 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) | 655 | if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) |
| 640 | return -ENOSPC; | 656 | return -ENOSPC; |
| 641 | 657 | ||
| @@ -645,16 +661,16 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, | |||
| 645 | 661 | ||
| 646 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); | 662 | fsnotify_init_mark(fsn_mark, fanotify_free_mark); |
| 647 | ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); | 663 | ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); |
| 648 | if (ret) { | 664 | if (ret) |
| 649 | fanotify_free_mark(fsn_mark); | 665 | goto err; |
| 650 | return ret; | ||
| 651 | } | ||
| 652 | } | 666 | } |
| 653 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); | 667 | added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); |
| 654 | fsnotify_put_mark(fsn_mark); | 668 | |
| 655 | if (added & ~inode->i_fsnotify_mask) | 669 | if (added & ~inode->i_fsnotify_mask) |
| 656 | fsnotify_recalc_inode_mask(inode); | 670 | fsnotify_recalc_inode_mask(inode); |
| 657 | return 0; | 671 | err: |
| 672 | fsnotify_put_mark(fsn_mark); | ||
| 673 | return ret; | ||
| 658 | } | 674 | } |
| 659 | 675 | ||
| 660 | /* fanotify syscalls */ | 676 | /* fanotify syscalls */ |
| @@ -687,8 +703,10 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) | |||
| 687 | 703 | ||
| 688 | /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ | 704 | /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ |
| 689 | group = fsnotify_alloc_group(&fanotify_fsnotify_ops); | 705 | group = fsnotify_alloc_group(&fanotify_fsnotify_ops); |
| 690 | if (IS_ERR(group)) | 706 | if (IS_ERR(group)) { |
| 707 | free_uid(user); | ||
| 691 | return PTR_ERR(group); | 708 | return PTR_ERR(group); |
| 709 | } | ||
| 692 | 710 | ||
| 693 | group->fanotify_data.user = user; | 711 | group->fanotify_data.user = user; |
| 694 | atomic_inc(&user->fanotify_listeners); | 712 | atomic_inc(&user->fanotify_listeners); |
| @@ -698,6 +716,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) | |||
| 698 | mutex_init(&group->fanotify_data.access_mutex); | 716 | mutex_init(&group->fanotify_data.access_mutex); |
| 699 | init_waitqueue_head(&group->fanotify_data.access_waitq); | 717 | init_waitqueue_head(&group->fanotify_data.access_waitq); |
| 700 | INIT_LIST_HEAD(&group->fanotify_data.access_list); | 718 | INIT_LIST_HEAD(&group->fanotify_data.access_list); |
| 719 | atomic_set(&group->fanotify_data.bypass_perm, 0); | ||
| 701 | #endif | 720 | #endif |
| 702 | switch (flags & FAN_ALL_CLASS_BITS) { | 721 | switch (flags & FAN_ALL_CLASS_BITS) { |
| 703 | case FAN_CLASS_NOTIF: | 722 | case FAN_CLASS_NOTIF: |
| @@ -764,8 +783,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, | |||
| 764 | if (flags & ~FAN_ALL_MARK_FLAGS) | 783 | if (flags & ~FAN_ALL_MARK_FLAGS) |
| 765 | return -EINVAL; | 784 | return -EINVAL; |
| 766 | switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { | 785 | switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { |
| 767 | case FAN_MARK_ADD: | 786 | case FAN_MARK_ADD: /* fallthrough */ |
| 768 | case FAN_MARK_REMOVE: | 787 | case FAN_MARK_REMOVE: |
| 788 | if (!mask) | ||
| 789 | return -EINVAL; | ||
| 769 | case FAN_MARK_FLUSH: | 790 | case FAN_MARK_FLUSH: |
| 770 | break; | 791 | break; |
| 771 | default: | 792 | default: |
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 444c305a468..4cd5d5d78f9 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
| @@ -752,6 +752,7 @@ SYSCALL_DEFINE1(inotify_init1, int, flags) | |||
| 752 | if (ret >= 0) | 752 | if (ret >= 0) |
| 753 | return ret; | 753 | return ret; |
| 754 | 754 | ||
| 755 | fsnotify_put_group(group); | ||
| 755 | atomic_dec(&user->inotify_devs); | 756 | atomic_dec(&user->inotify_devs); |
| 756 | out_free_uid: | 757 | out_free_uid: |
| 757 | free_uid(user); | 758 | free_uid(user); |
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 0f0121467fc..6c6133f76e1 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h | |||
| @@ -83,11 +83,13 @@ | |||
| 83 | FAN_ALL_PERM_EVENTS |\ | 83 | FAN_ALL_PERM_EVENTS |\ |
| 84 | FAN_Q_OVERFLOW) | 84 | FAN_Q_OVERFLOW) |
| 85 | 85 | ||
| 86 | #define FANOTIFY_METADATA_VERSION 2 | 86 | #define FANOTIFY_METADATA_VERSION 3 |
| 87 | 87 | ||
| 88 | struct fanotify_event_metadata { | 88 | struct fanotify_event_metadata { |
| 89 | __u32 event_len; | 89 | __u32 event_len; |
| 90 | __u32 vers; | 90 | __u8 vers; |
| 91 | __u8 reserved; | ||
| 92 | __u16 metadata_len; | ||
| 91 | __aligned_u64 mask; | 93 | __aligned_u64 mask; |
| 92 | __s32 fd; | 94 | __s32 fd; |
| 93 | __s32 pid; | 95 | __s32 pid; |
| @@ -96,11 +98,13 @@ struct fanotify_event_metadata { | |||
| 96 | struct fanotify_response { | 98 | struct fanotify_response { |
| 97 | __s32 fd; | 99 | __s32 fd; |
| 98 | __u32 response; | 100 | __u32 response; |
| 99 | } __attribute__ ((packed)); | 101 | }; |
| 100 | 102 | ||
| 101 | /* Legit userspace responses to a _PERM event */ | 103 | /* Legit userspace responses to a _PERM event */ |
| 102 | #define FAN_ALLOW 0x01 | 104 | #define FAN_ALLOW 0x01 |
| 103 | #define FAN_DENY 0x02 | 105 | #define FAN_DENY 0x02 |
| 106 | /* No fd set in event */ | ||
| 107 | #define FAN_NOFD -1 | ||
| 104 | 108 | ||
| 105 | /* Helper functions to deal with fanotify_event_metadata buffers */ | 109 | /* Helper functions to deal with fanotify_event_metadata buffers */ |
| 106 | #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) | 110 | #define FAN_EVENT_METADATA_LEN (sizeof(struct fanotify_event_metadata)) |
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 5c185fa2708..b10bcdeaef7 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h | |||
| @@ -235,9 +235,6 @@ static inline void fsnotify_open(struct file *file) | |||
| 235 | if (S_ISDIR(inode->i_mode)) | 235 | if (S_ISDIR(inode->i_mode)) |
| 236 | mask |= FS_ISDIR; | 236 | mask |= FS_ISDIR; |
| 237 | 237 | ||
| 238 | /* FMODE_NONOTIFY must never be set from user */ | ||
| 239 | file->f_mode &= ~FMODE_NONOTIFY; | ||
| 240 | |||
| 241 | fsnotify_parent(path, NULL, mask); | 238 | fsnotify_parent(path, NULL, mask); |
| 242 | fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); | 239 | fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); |
| 243 | } | 240 | } |
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0a68f924f06..7380763595d 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
| @@ -166,7 +166,7 @@ struct fsnotify_group { | |||
| 166 | struct mutex access_mutex; | 166 | struct mutex access_mutex; |
| 167 | struct list_head access_list; | 167 | struct list_head access_list; |
| 168 | wait_queue_head_t access_waitq; | 168 | wait_queue_head_t access_waitq; |
| 169 | bool bypass_perm; /* protected by access_mutex */ | 169 | atomic_t bypass_perm; |
| 170 | #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ | 170 | #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ |
| 171 | int f_flags; | 171 | int f_flags; |
| 172 | unsigned int max_marks; | 172 | unsigned int max_marks; |
