aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2019-04-24 12:39:57 -0400
committerJan Kara <jack@suse.cz>2019-04-28 16:14:50 -0400
commitb1da6a51871c6929dced1a7fad81990988b36ed6 (patch)
tree2c487de5f9e6369a9ee459e93d10dd37f094e945
parentba25b50d582ff6c6021eee80824134aeb9ab8785 (diff)
fsnotify: Fix NULL ptr deref in fanotify_get_fsid()
fanotify_get_fsid() is reading mark->connector->fsid under srcu. It can happen that it sees mark not fully initialized or mark that is already detached from the object list. In these cases mark->connector can be NULL leading to NULL ptr dereference. Fix the problem by being careful when reading mark->connector and check it for being NULL. Also use WRITE_ONCE when writing the mark just to prevent compiler from doing something stupid. Reported-by: syzbot+15927486a4f1bfcbaf91@syzkaller.appspotmail.com Fixes: 77115225acc6 ("fanotify: cache fsid in fsnotify_mark_connector") Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/notify/fanotify/fanotify.c14
-rw-r--r--fs/notify/mark.c12
2 files changed, 18 insertions, 8 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 6b9c27548997..63c6bb1f8c4d 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -346,10 +346,16 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
346 __kernel_fsid_t fsid = {}; 346 __kernel_fsid_t fsid = {};
347 347
348 fsnotify_foreach_obj_type(type) { 348 fsnotify_foreach_obj_type(type) {
349 struct fsnotify_mark_connector *conn;
350
349 if (!fsnotify_iter_should_report_type(iter_info, type)) 351 if (!fsnotify_iter_should_report_type(iter_info, type))
350 continue; 352 continue;
351 353
352 fsid = iter_info->marks[type]->connector->fsid; 354 conn = READ_ONCE(iter_info->marks[type]->connector);
355 /* Mark is just getting destroyed or created? */
356 if (!conn)
357 continue;
358 fsid = conn->fsid;
353 if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1])) 359 if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
354 continue; 360 continue;
355 return fsid; 361 return fsid;
@@ -408,8 +414,12 @@ static int fanotify_handle_event(struct fsnotify_group *group,
408 return 0; 414 return 0;
409 } 415 }
410 416
411 if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) 417 if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
412 fsid = fanotify_get_fsid(iter_info); 418 fsid = fanotify_get_fsid(iter_info);
419 /* Racing with mark destruction or creation? */
420 if (!fsid.val[0] && !fsid.val[1])
421 return 0;
422 }
413 423
414 event = fanotify_alloc_event(group, inode, mask, data, data_type, 424 event = fanotify_alloc_event(group, inode, mask, data, data_type,
415 &fsid); 425 &fsid);
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index d593d4269561..22acb0a79b53 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -239,13 +239,13 @@ static void fsnotify_drop_object(unsigned int type, void *objp)
239 239
240void fsnotify_put_mark(struct fsnotify_mark *mark) 240void fsnotify_put_mark(struct fsnotify_mark *mark)
241{ 241{
242 struct fsnotify_mark_connector *conn; 242 struct fsnotify_mark_connector *conn = READ_ONCE(mark->connector);
243 void *objp = NULL; 243 void *objp = NULL;
244 unsigned int type = FSNOTIFY_OBJ_TYPE_DETACHED; 244 unsigned int type = FSNOTIFY_OBJ_TYPE_DETACHED;
245 bool free_conn = false; 245 bool free_conn = false;
246 246
247 /* Catch marks that were actually never attached to object */ 247 /* Catch marks that were actually never attached to object */
248 if (!mark->connector) { 248 if (!conn) {
249 if (refcount_dec_and_test(&mark->refcnt)) 249 if (refcount_dec_and_test(&mark->refcnt))
250 fsnotify_final_mark_destroy(mark); 250 fsnotify_final_mark_destroy(mark);
251 return; 251 return;
@@ -255,10 +255,9 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
255 * We have to be careful so that traversals of obj_list under lock can 255 * We have to be careful so that traversals of obj_list under lock can
256 * safely grab mark reference. 256 * safely grab mark reference.
257 */ 257 */
258 if (!refcount_dec_and_lock(&mark->refcnt, &mark->connector->lock)) 258 if (!refcount_dec_and_lock(&mark->refcnt, &conn->lock))
259 return; 259 return;
260 260
261 conn = mark->connector;
262 hlist_del_init_rcu(&mark->obj_list); 261 hlist_del_init_rcu(&mark->obj_list);
263 if (hlist_empty(&conn->list)) { 262 if (hlist_empty(&conn->list)) {
264 objp = fsnotify_detach_connector_from_object(conn, &type); 263 objp = fsnotify_detach_connector_from_object(conn, &type);
@@ -266,7 +265,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
266 } else { 265 } else {
267 __fsnotify_recalc_mask(conn); 266 __fsnotify_recalc_mask(conn);
268 } 267 }
269 mark->connector = NULL; 268 WRITE_ONCE(mark->connector, NULL);
270 spin_unlock(&conn->lock); 269 spin_unlock(&conn->lock);
271 270
272 fsnotify_drop_object(type, objp); 271 fsnotify_drop_object(type, objp);
@@ -620,7 +619,7 @@ restart:
620 /* mark should be the last entry. last is the current last entry */ 619 /* mark should be the last entry. last is the current last entry */
621 hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); 620 hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
622added: 621added:
623 mark->connector = conn; 622 WRITE_ONCE(mark->connector, conn);
624out_err: 623out_err:
625 spin_unlock(&conn->lock); 624 spin_unlock(&conn->lock);
626 spin_unlock(&mark->lock); 625 spin_unlock(&mark->lock);
@@ -808,6 +807,7 @@ void fsnotify_init_mark(struct fsnotify_mark *mark,
808 refcount_set(&mark->refcnt, 1); 807 refcount_set(&mark->refcnt, 1);
809 fsnotify_get_group(group); 808 fsnotify_get_group(group);
810 mark->group = group; 809 mark->group = group;
810 WRITE_ONCE(mark->connector, NULL);
811} 811}
812 812
813/* 813/*