aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2017-04-25 07:29:35 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-05-25 09:44:31 -0400
commitcc0f994c205df817b9ed4a29fd712fa0da82c68b (patch)
tree19b6a6527e63c517a8400e30390907c811352d1b /fs
parente8b6d43ce3ea4afd8aecf2ac6a8523b085058db0 (diff)
fanotify: don't expose EOPENSTALE to userspace
commit 4ff33aafd32e084f5ee7faa54ba06e95f8b1b8af upstream. When delivering an event to userspace for a file on an NFS share, if the file is deleted on server side before user reads the event, user will not get the event. If the event queue contained several events, the stale event is quietly dropped and read() returns to user with events read so far in the buffer. If the event queue contains a single stale event or if the stale event is a permission event, read() returns to user with the kernel internal error code 518 (EOPENSTALE), which is not a POSIX error code. Check the internal return value -EOPENSTALE in fanotify_read(), just the same as it is checked in path_openat() and drop the event in the cases that it is not already dropped. This is a reproducer from Marko Rauhamaa: Just take the example program listed under "man fanotify" ("fantest") and follow these steps: ============================================================== NFS Server NFS Client(1) NFS Client(2) ============================================================== # echo foo >/nfsshare/bar.txt # cat /nfsshare/bar.txt foo # ./fantest /nfsshare Press enter key to terminate. Listening for events. # rm -f /nfsshare/bar.txt # cat /nfsshare/bar.txt read: Unknown error 518 cat: /nfsshare/bar.txt: Operation not permitted ============================================================== where NFS Client (1) and (2) are two terminal sessions on a single NFS Client machine. Reported-by: Marko Rauhamaa <marko.rauhamaa@f-secure.com> Tested-by: Marko Rauhamaa <marko.rauhamaa@f-secure.com> Cc: <linux-api@vger.kernel.org> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/notify/fanotify/fanotify_user.c26
1 files changed, 18 insertions, 8 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 7ebfca6a1427..258e8f635148 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -294,27 +294,37 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
294 } 294 }
295 295
296 ret = copy_event_to_user(group, kevent, buf); 296 ret = copy_event_to_user(group, kevent, buf);
297 if (unlikely(ret == -EOPENSTALE)) {
298 /*
299 * We cannot report events with stale fd so drop it.
300 * Setting ret to 0 will continue the event loop and
301 * do the right thing if there are no more events to
302 * read (i.e. return bytes read, -EAGAIN or wait).
303 */
304 ret = 0;
305 }
306
297 /* 307 /*
298 * Permission events get queued to wait for response. Other 308 * Permission events get queued to wait for response. Other
299 * events can be destroyed now. 309 * events can be destroyed now.
300 */ 310 */
301 if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) { 311 if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) {
302 fsnotify_destroy_event(group, kevent); 312 fsnotify_destroy_event(group, kevent);
303 if (ret < 0)
304 break;
305 } else { 313 } else {
306#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS 314#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
307 if (ret < 0) { 315 if (ret <= 0) {
308 FANOTIFY_PE(kevent)->response = FAN_DENY; 316 FANOTIFY_PE(kevent)->response = FAN_DENY;
309 wake_up(&group->fanotify_data.access_waitq); 317 wake_up(&group->fanotify_data.access_waitq);
310 break; 318 } else {
319 spin_lock(&group->notification_lock);
320 list_add_tail(&kevent->list,
321 &group->fanotify_data.access_list);
322 spin_unlock(&group->notification_lock);
311 } 323 }
312 spin_lock(&group->notification_lock);
313 list_add_tail(&kevent->list,
314 &group->fanotify_data.access_list);
315 spin_unlock(&group->notification_lock);
316#endif 324#endif
317 } 325 }
326 if (ret < 0)
327 break;
318 buf += ret; 328 buf += ret;
319 count -= ret; 329 count -= ret;
320 } 330 }