Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fanotify: Track permission event state

Track whether permission event got already reported to userspace and
whether userspace already answered to the permission event. Protect
stores to this field together with updates to ->response field by
group->notification_lock. This will allow aborting wait for reply to
permission event from userspace.

Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>

Jan Kara 40873284 ca6f8699

+40 -11
+3 -3
fs/notify/fanotify/fanotify.c
··· 85 85 86 86 pr_debug("%s: group=%p event=%p\n", __func__, group, event); 87 87 88 - wait_event(group->fanotify_data.access_waitq, event->response); 88 + wait_event(group->fanotify_data.access_waitq, 89 + event->state == FAN_EVENT_ANSWERED); 89 90 90 91 /* userspace responded, convert to something usable */ 91 92 switch (event->response & ~FAN_AUDIT) { ··· 101 100 /* Check if the response should be audited */ 102 101 if (event->response & FAN_AUDIT) 103 102 audit_fanotify(event->response & ~FAN_AUDIT); 104 - 105 - event->response = 0; 106 103 107 104 pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, 108 105 group, event, ret); ··· 274 275 goto out; 275 276 event = &pevent->fae; 276 277 pevent->response = 0; 278 + pevent->state = FAN_EVENT_INIT; 277 279 goto init; 278 280 } 279 281 event = kmem_cache_alloc(fanotify_event_cachep, gfp);
+9 -1
fs/notify/fanotify/fanotify.h
··· 8 8 extern struct kmem_cache *fanotify_event_cachep; 9 9 extern struct kmem_cache *fanotify_perm_event_cachep; 10 10 11 + /* Possible states of the permission event */ 12 + enum { 13 + FAN_EVENT_INIT, 14 + FAN_EVENT_REPORTED, 15 + FAN_EVENT_ANSWERED 16 + }; 17 + 11 18 /* 12 19 * 3 dwords are sufficient for most local fs (64bit ino, 32bit generation). 13 20 * For 32bit arch, fid increases the size of fanotify_event by 12 bytes and ··· 116 109 */ 117 110 struct fanotify_perm_event { 118 111 struct fanotify_event fae; 119 - int response; /* userspace answer to question */ 112 + unsigned short response; /* userspace answer to the event */ 113 + unsigned short state; /* state of the event */ 120 114 int fd; /* fd we passed to userspace for this event */ 121 115 }; 122 116
+28 -7
fs/notify/fanotify/fanotify_user.c
··· 64 64 /* 65 65 * Get an fsnotify notification event if one exists and is small 66 66 * enough to fit in "count". Return an error pointer if the count 67 - * is not large enough. 67 + * is not large enough. When permission event is dequeued, its state is 68 + * updated accordingly. 68 69 */ 69 70 static struct fsnotify_event *get_one_event(struct fsnotify_group *group, 70 71 size_t count) ··· 89 88 goto out; 90 89 } 91 90 fsn_event = fsnotify_remove_first_event(group); 91 + if (fanotify_is_perm_event(FANOTIFY_E(fsn_event)->mask)) 92 + FANOTIFY_PE(fsn_event)->state = FAN_EVENT_REPORTED; 92 93 out: 93 94 spin_unlock(&group->notification_lock); 94 95 return fsn_event; ··· 138 135 return client_fd; 139 136 } 140 137 138 + /* 139 + * Finish processing of permission event by setting it to ANSWERED state and 140 + * drop group->notification_lock. 141 + */ 142 + static void finish_permission_event(struct fsnotify_group *group, 143 + struct fanotify_perm_event *event, 144 + unsigned int response) 145 + __releases(&group->notification_lock) 146 + { 147 + assert_spin_locked(&group->notification_lock); 148 + event->response = response; 149 + event->state = FAN_EVENT_ANSWERED; 150 + spin_unlock(&group->notification_lock); 151 + } 152 + 141 153 static int process_access_response(struct fsnotify_group *group, 142 154 struct fanotify_response *response_struct) 143 155 { ··· 188 170 continue; 189 171 190 172 list_del_init(&event->fae.fse.list); 191 - event->response = response; 192 - spin_unlock(&group->notification_lock); 173 + finish_permission_event(group, event, response); 193 174 wake_up(&group->fanotify_data.access_waitq); 194 175 return 0; 195 176 } ··· 371 354 fsnotify_destroy_event(group, kevent); 372 355 } else { 373 356 if (ret <= 0) { 374 - FANOTIFY_PE(kevent)->response = FAN_DENY; 357 + spin_lock(&group->notification_lock); 358 + finish_permission_event(group, 359 + FANOTIFY_PE(kevent), FAN_DENY); 375 360 wake_up(&group->fanotify_data.access_waitq); 376 361 } else { 377 362 spin_lock(&group->notification_lock); ··· 442 423 event = list_first_entry(&group->fanotify_data.access_list, 443 424 struct fanotify_perm_event, fae.fse.list); 444 425 list_del_init(&event->fae.fse.list); 445 - event->response = FAN_ALLOW; 426 + finish_permission_event(group, event, FAN_ALLOW); 427 + spin_lock(&group->notification_lock); 446 428 } 447 429 448 430 /* ··· 456 436 if (!(FANOTIFY_E(fsn_event)->mask & FANOTIFY_PERM_EVENTS)) { 457 437 spin_unlock(&group->notification_lock); 458 438 fsnotify_destroy_event(group, fsn_event); 459 - spin_lock(&group->notification_lock); 460 439 } else { 461 - FANOTIFY_PE(fsn_event)->response = FAN_ALLOW; 440 + finish_permission_event(group, FANOTIFY_PE(fsn_event), 441 + FAN_ALLOW); 462 442 } 443 + spin_lock(&group->notification_lock); 463 444 } 464 445 spin_unlock(&group->notification_lock); 465 446