diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-12-16 18:45:49 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-12-16 18:45:49 -0500 |
commit | a3383e8372c0c11238f9bb9777929bfc3a2d320a (patch) | |
tree | 1eb117842e70b17e8b7e96a955cd0ef6f0f40732 | |
parent | 68a4ec9c03461e94a9577cf499069621bb074833 (diff) | |
parent | 7d13162332f2b67a941d18cee20f1c0413e020de (diff) |
Merge branch 'for-linus' of git://git.infradead.org/users/eparis/notify
* 'for-linus' of git://git.infradead.org/users/eparis/notify:
fanotify: fill in the metadata_len field on struct fanotify_event_metadata
fanotify: split version into version and metadata_len
fanotify: Dont try to open a file descriptor for the overflow event
fanotify: Introduce FAN_NOFD
fanotify: do not leak user reference on allocation failure
inotify: stop kernel memory leak on file creation failure
fanotify: on group destroy allow all waiters to bypass permission check
fanotify: Dont allow a mask of 0 if setting or removing a mark
fanotify: correct broken ref counting in case adding a mark failed
fanotify: if set by user unset FMODE_NONOTIFY before fsnotify_perm() is called
fanotify: remove packed from access response message
fanotify: deny permissions when no event was sent
-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 5362af9b7372..4ff7ca530533 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 b04f88eed09e..f35794b97e8e 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 063224812b7e..8b61220cffc5 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 444c305a468c..4cd5d5d78f9f 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 0f0121467fc4..6c6133f76e16 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 5c185fa27089..b10bcdeaef76 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 0a68f924f06f..7380763595d3 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; |