diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2012-08-19 12:30:45 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2012-09-26 21:08:52 -0400 |
commit | 352e3b249284235e00745f3e71fc348b913e5deb (patch) | |
tree | 4d0c7342c95cb30f59b706048486c72a78be9f77 /fs/notify/fanotify/fanotify_user.c | |
parent | ab72a7028c0cc22731dc60beceb595b321d1cdb9 (diff) |
fanotify: sanitize failure exits in copy_event_to_user()
* do copy_to_user() before prepare_for_access_response(); that kills
the need in remove_access_response().
* don't do fd_install() until we are past the last possible failure
exit. Don't use sys_close() on cleanup side - just put_unused_fd()
and fput(). Less racy that way...
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/notify/fanotify/fanotify_user.c')
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 59 |
1 files changed, 20 insertions, 39 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index d43803669739..ea48693940f1 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
@@ -58,7 +58,9 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, | |||
58 | return fsnotify_remove_notify_event(group); | 58 | return fsnotify_remove_notify_event(group); |
59 | } | 59 | } |
60 | 60 | ||
61 | static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) | 61 | static int create_fd(struct fsnotify_group *group, |
62 | struct fsnotify_event *event, | ||
63 | struct file **file) | ||
62 | { | 64 | { |
63 | int client_fd; | 65 | int client_fd; |
64 | struct file *new_file; | 66 | struct file *new_file; |
@@ -98,7 +100,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) | |||
98 | put_unused_fd(client_fd); | 100 | put_unused_fd(client_fd); |
99 | client_fd = PTR_ERR(new_file); | 101 | client_fd = PTR_ERR(new_file); |
100 | } else { | 102 | } else { |
101 | fd_install(client_fd, new_file); | 103 | *file = new_file; |
102 | } | 104 | } |
103 | 105 | ||
104 | return client_fd; | 106 | return client_fd; |
@@ -106,13 +108,15 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) | |||
106 | 108 | ||
107 | static int fill_event_metadata(struct fsnotify_group *group, | 109 | static int fill_event_metadata(struct fsnotify_group *group, |
108 | struct fanotify_event_metadata *metadata, | 110 | struct fanotify_event_metadata *metadata, |
109 | struct fsnotify_event *event) | 111 | struct fsnotify_event *event, |
112 | struct file **file) | ||
110 | { | 113 | { |
111 | int ret = 0; | 114 | int ret = 0; |
112 | 115 | ||
113 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, | 116 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, |
114 | group, metadata, event); | 117 | group, metadata, event); |
115 | 118 | ||
119 | *file = NULL; | ||
116 | metadata->event_len = FAN_EVENT_METADATA_LEN; | 120 | metadata->event_len = FAN_EVENT_METADATA_LEN; |
117 | metadata->metadata_len = FAN_EVENT_METADATA_LEN; | 121 | metadata->metadata_len = FAN_EVENT_METADATA_LEN; |
118 | metadata->vers = FANOTIFY_METADATA_VERSION; | 122 | metadata->vers = FANOTIFY_METADATA_VERSION; |
@@ -121,7 +125,7 @@ static int fill_event_metadata(struct fsnotify_group *group, | |||
121 | if (unlikely(event->mask & FAN_Q_OVERFLOW)) | 125 | if (unlikely(event->mask & FAN_Q_OVERFLOW)) |
122 | metadata->fd = FAN_NOFD; | 126 | metadata->fd = FAN_NOFD; |
123 | else { | 127 | else { |
124 | metadata->fd = create_fd(group, event); | 128 | metadata->fd = create_fd(group, event, file); |
125 | if (metadata->fd < 0) | 129 | if (metadata->fd < 0) |
126 | ret = metadata->fd; | 130 | ret = metadata->fd; |
127 | } | 131 | } |
@@ -220,25 +224,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, | |||
220 | return 0; | 224 | return 0; |
221 | } | 225 | } |
222 | 226 | ||
223 | static void remove_access_response(struct fsnotify_group *group, | ||
224 | struct fsnotify_event *event, | ||
225 | __s32 fd) | ||
226 | { | ||
227 | struct fanotify_response_event *re; | ||
228 | |||
229 | if (!(event->mask & FAN_ALL_PERM_EVENTS)) | ||
230 | return; | ||
231 | |||
232 | re = dequeue_re(group, fd); | ||
233 | if (!re) | ||
234 | return; | ||
235 | |||
236 | BUG_ON(re->event != event); | ||
237 | |||
238 | kmem_cache_free(fanotify_response_event_cache, re); | ||
239 | |||
240 | return; | ||
241 | } | ||
242 | #else | 227 | #else |
243 | static int prepare_for_access_response(struct fsnotify_group *group, | 228 | static int prepare_for_access_response(struct fsnotify_group *group, |
244 | struct fsnotify_event *event, | 229 | struct fsnotify_event *event, |
@@ -247,12 +232,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, | |||
247 | return 0; | 232 | return 0; |
248 | } | 233 | } |
249 | 234 | ||
250 | static void remove_access_response(struct fsnotify_group *group, | ||
251 | struct fsnotify_event *event, | ||
252 | __s32 fd) | ||
253 | { | ||
254 | return; | ||
255 | } | ||
256 | #endif | 235 | #endif |
257 | 236 | ||
258 | static ssize_t copy_event_to_user(struct fsnotify_group *group, | 237 | static ssize_t copy_event_to_user(struct fsnotify_group *group, |
@@ -260,31 +239,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, | |||
260 | char __user *buf) | 239 | char __user *buf) |
261 | { | 240 | { |
262 | struct fanotify_event_metadata fanotify_event_metadata; | 241 | struct fanotify_event_metadata fanotify_event_metadata; |
242 | struct file *f; | ||
263 | int fd, ret; | 243 | int fd, ret; |
264 | 244 | ||
265 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); | 245 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
266 | 246 | ||
267 | ret = fill_event_metadata(group, &fanotify_event_metadata, event); | 247 | ret = fill_event_metadata(group, &fanotify_event_metadata, event, &f); |
268 | if (ret < 0) | 248 | if (ret < 0) |
269 | goto out; | 249 | goto out; |
270 | 250 | ||
271 | fd = fanotify_event_metadata.fd; | 251 | fd = fanotify_event_metadata.fd; |
272 | ret = prepare_for_access_response(group, event, fd); | ||
273 | if (ret) | ||
274 | goto out_close_fd; | ||
275 | |||
276 | ret = -EFAULT; | 252 | ret = -EFAULT; |
277 | if (copy_to_user(buf, &fanotify_event_metadata, | 253 | if (copy_to_user(buf, &fanotify_event_metadata, |
278 | fanotify_event_metadata.event_len)) | 254 | fanotify_event_metadata.event_len)) |
279 | goto out_kill_access_response; | 255 | goto out_close_fd; |
256 | |||
257 | ret = prepare_for_access_response(group, event, fd); | ||
258 | if (ret) | ||
259 | goto out_close_fd; | ||
280 | 260 | ||
261 | fd_install(fd, f); | ||
281 | return fanotify_event_metadata.event_len; | 262 | return fanotify_event_metadata.event_len; |
282 | 263 | ||
283 | out_kill_access_response: | ||
284 | remove_access_response(group, event, fd); | ||
285 | out_close_fd: | 264 | out_close_fd: |
286 | if (fd != FAN_NOFD) | 265 | if (fd != FAN_NOFD) { |
287 | sys_close(fd); | 266 | put_unused_fd(fd); |
267 | fput(f); | ||
268 | } | ||
288 | out: | 269 | out: |
289 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 270 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
290 | if (event->mask & FAN_ALL_PERM_EVENTS) { | 271 | if (event->mask & FAN_ALL_PERM_EVENTS) { |