]>
Commit | Line | Data |
---|---|---|
208fc782 GKH |
1 | From 4ff33aafd32e084f5ee7faa54ba06e95f8b1b8af Mon Sep 17 00:00:00 2001 |
2 | From: Amir Goldstein <amir73il@gmail.com> | |
3 | Date: Tue, 25 Apr 2017 14:29:35 +0300 | |
4 | Subject: fanotify: don't expose EOPENSTALE to userspace | |
5 | ||
6 | From: Amir Goldstein <amir73il@gmail.com> | |
7 | ||
8 | commit 4ff33aafd32e084f5ee7faa54ba06e95f8b1b8af upstream. | |
9 | ||
10 | When delivering an event to userspace for a file on an NFS share, | |
11 | if the file is deleted on server side before user reads the event, | |
12 | user will not get the event. | |
13 | ||
14 | If the event queue contained several events, the stale event is | |
15 | quietly dropped and read() returns to user with events read so far | |
16 | in the buffer. | |
17 | ||
18 | If the event queue contains a single stale event or if the stale | |
19 | event is a permission event, read() returns to user with the kernel | |
20 | internal error code 518 (EOPENSTALE), which is not a POSIX error code. | |
21 | ||
22 | Check the internal return value -EOPENSTALE in fanotify_read(), just | |
23 | the same as it is checked in path_openat() and drop the event in the | |
24 | cases that it is not already dropped. | |
25 | ||
26 | This is a reproducer from Marko Rauhamaa: | |
27 | ||
28 | Just take the example program listed under "man fanotify" ("fantest") | |
29 | and follow these steps: | |
30 | ||
31 | ============================================================== | |
32 | NFS Server NFS Client(1) NFS Client(2) | |
33 | ============================================================== | |
34 | # echo foo >/nfsshare/bar.txt | |
35 | # cat /nfsshare/bar.txt | |
36 | foo | |
37 | # ./fantest /nfsshare | |
38 | Press enter key to terminate. | |
39 | Listening for events. | |
40 | # rm -f /nfsshare/bar.txt | |
41 | # cat /nfsshare/bar.txt | |
42 | read: Unknown error 518 | |
43 | cat: /nfsshare/bar.txt: Operation not permitted | |
44 | ============================================================== | |
45 | ||
46 | where NFS Client (1) and (2) are two terminal sessions on a single NFS | |
47 | Client machine. | |
48 | ||
49 | Reported-by: Marko Rauhamaa <marko.rauhamaa@f-secure.com> | |
50 | Tested-by: Marko Rauhamaa <marko.rauhamaa@f-secure.com> | |
51 | Cc: <linux-api@vger.kernel.org> | |
52 | Signed-off-by: Amir Goldstein <amir73il@gmail.com> | |
53 | Signed-off-by: Jan Kara <jack@suse.cz> | |
54 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
55 | ||
56 | --- | |
57 | fs/notify/fanotify/fanotify_user.c | 26 ++++++++++++++++++-------- | |
58 | 1 file changed, 18 insertions(+), 8 deletions(-) | |
59 | ||
60 | --- a/fs/notify/fanotify/fanotify_user.c | |
61 | +++ b/fs/notify/fanotify/fanotify_user.c | |
62 | @@ -294,27 +294,37 @@ static ssize_t fanotify_read(struct file | |
63 | } | |
64 | ||
65 | ret = copy_event_to_user(group, kevent, buf); | |
66 | + if (unlikely(ret == -EOPENSTALE)) { | |
67 | + /* | |
68 | + * We cannot report events with stale fd so drop it. | |
69 | + * Setting ret to 0 will continue the event loop and | |
70 | + * do the right thing if there are no more events to | |
71 | + * read (i.e. return bytes read, -EAGAIN or wait). | |
72 | + */ | |
73 | + ret = 0; | |
74 | + } | |
75 | + | |
76 | /* | |
77 | * Permission events get queued to wait for response. Other | |
78 | * events can be destroyed now. | |
79 | */ | |
80 | if (!(kevent->mask & FAN_ALL_PERM_EVENTS)) { | |
81 | fsnotify_destroy_event(group, kevent); | |
82 | - if (ret < 0) | |
83 | - break; | |
84 | } else { | |
85 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | |
86 | - if (ret < 0) { | |
87 | + if (ret <= 0) { | |
88 | FANOTIFY_PE(kevent)->response = FAN_DENY; | |
89 | wake_up(&group->fanotify_data.access_waitq); | |
90 | - break; | |
91 | + } else { | |
92 | + spin_lock(&group->notification_lock); | |
93 | + list_add_tail(&kevent->list, | |
94 | + &group->fanotify_data.access_list); | |
95 | + spin_unlock(&group->notification_lock); | |
96 | } | |
97 | - spin_lock(&group->notification_lock); | |
98 | - list_add_tail(&kevent->list, | |
99 | - &group->fanotify_data.access_list); | |
100 | - spin_unlock(&group->notification_lock); | |
101 | #endif | |
102 | } | |
103 | + if (ret < 0) | |
104 | + break; | |
105 | buf += ret; | |
106 | count -= ret; | |
107 | } |