All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] fsnotify: Fix NULL ptr deref in fanotify_get_fsid()
@ 2019-04-25  7:59 Jan Kara
  0 siblings, 0 replies; only message in thread
From: Jan Kara @ 2019-04-25  7:59 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: Amir Goldstein, Jan Kara

fanotify_get_fsid() is reading mark->connector->fsid under srcu. It can
happen that it sees mark not fully initialized and thus mark->connector
is still NULL leading to NULL ptr dereference. Fix the problem by
setting mark->connector before adding mark to the object's list and use
appropriate memory barriers to make sure proper mark->connector value is
seen for any mark added to the list.

Reported-by: syzbot+15927486a4f1bfcbaf91@syzkaller.appspotmail.com
Fixes: 77115225acc6 ("fanotify: cache fsid in fsnotify_mark_connector")
Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/notify/fanotify/fanotify.c |  2 +-
 fs/notify/mark.c              | 43 +++++++++++++++++++++++++++++--------------
 2 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 6b9c27548997..f34f0667ea60 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -349,7 +349,7 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
 		if (!fsnotify_iter_should_report_type(iter_info, type))
 			continue;
 
-		fsid = iter_info->marks[type]->connector->fsid;
+		fsid = READ_ONCE(iter_info->marks[type]->connector)->fsid;
 		if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
 			continue;
 		return fsid;
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index d593d4269561..a10abf90f1bc 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)
 
 void fsnotify_put_mark(struct fsnotify_mark *mark)
 {
-	struct fsnotify_mark_connector *conn;
+	struct fsnotify_mark_connector *conn = READ_ONCE(mark->connector);
 	void *objp = NULL;
 	unsigned int type = FSNOTIFY_OBJ_TYPE_DETACHED;
 	bool free_conn = false;
 
 	/* Catch marks that were actually never attached to object */
-	if (!mark->connector) {
+	if (!conn) {
 		if (refcount_dec_and_test(&mark->refcnt))
 			fsnotify_final_mark_destroy(mark);
 		return;
@@ -255,10 +255,9 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
 	 * We have to be careful so that traversals of obj_list under lock can
 	 * safely grab mark reference.
 	 */
-	if (!refcount_dec_and_lock(&mark->refcnt, &mark->connector->lock))
+	if (!refcount_dec_and_lock(&mark->refcnt, &conn->lock))
 		return;
 
-	conn = mark->connector;
 	hlist_del_init_rcu(&mark->obj_list);
 	if (hlist_empty(&conn->list)) {
 		objp = fsnotify_detach_connector_from_object(conn, &type);
@@ -543,6 +542,23 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector(
 	return conn;
 }
 
+/*
+ * We need to assign mark->connector before adding mark to the list so that
+ * RCU readers can safely dereference it but do not want to set it before
+ * we are really sure we are adding the mark to the connector's list so that
+ * someone cannot see connector value that was reset back to NULL in the end.
+ */
+#define fanotify_attach_obj_list(where, mark, conn, head) \
+	do {\
+		mark->connector = conn;\
+		/*
+		 * Make sure RCU readers of object list see connector
+		 * initialized.  Pairs in READ_ONCE for unlocked readers.
+		 */\
+		smp_wmb();\
+		hlist_add_##where##_rcu(&mark->obj_list, head);\
+	} while (0)
+
 /*
  * Add mark into proper place in given list of marks. These marks may be used
  * for the fsnotify backend to determine which event types should be delivered
@@ -589,13 +605,13 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 				    fsid->val[0], fsid->val[1],
 				    conn->fsid.val[0], conn->fsid.val[1]);
 		err = -EXDEV;
-		goto out_err;
+		goto out;
 	}
 
 	/* is mark the first mark? */
 	if (hlist_empty(&conn->list)) {
-		hlist_add_head_rcu(&mark->obj_list, &conn->list);
-		goto added;
+		fanotify_attach_obj_list(head, mark, conn, &conn->list);
+		goto out;
 	}
 
 	/* should mark be in the middle of the current list? */
@@ -606,22 +622,21 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
 		    (lmark->flags & FSNOTIFY_MARK_FLAG_ATTACHED) &&
 		    !allow_dups) {
 			err = -EEXIST;
-			goto out_err;
+			goto out;
 		}
 
 		cmp = fsnotify_compare_groups(lmark->group, mark->group);
 		if (cmp >= 0) {
-			hlist_add_before_rcu(&mark->obj_list, &lmark->obj_list);
-			goto added;
+			fanotify_attach_obj_list(before, mark, conn,
+						 &lmark->obj_list);
+			goto out;
 		}
 	}
 
 	BUG_ON(last == NULL);
 	/* mark should be the last entry.  last is the current last entry */
-	hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
-added:
-	mark->connector = conn;
-out_err:
+	fanotify_attach_obj_list(behind, mark, conn, &last->obj_list);
+out:
 	spin_unlock(&conn->lock);
 	spin_unlock(&mark->lock);
 	return err;
-- 
2.16.4


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2019-04-25  7:59 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-25  7:59 [PATCH] fsnotify: Fix NULL ptr deref in fanotify_get_fsid() Jan Kara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.