Skip to content

Commit 35ceae4

Browse files
committed
fsnotify: Avoid data race between fsnotify_recalc_mask() and fsnotify_object_watched()
When __fsnotify_recalc_mask() recomputes the mask on the watched object, the compiler can "optimize" the code to perform partial updates to the mask (including zeroing it at the beginning). Thus places checking the object mask without conn->lock such as fsnotify_object_watched() could see invalid states of the mask. Make sure the mask update is performed by one memory store using WRITE_ONCE(). Reported-by: [email protected] Reported-by: Dmitry Vyukov <[email protected]> Link: https://lore.kernel.org/all/CACT4Y+Zk0ohwwwHSD63U2-PQ=UuamXczr1mKBD6xtj2dyYKBvA@mail.gmail.com Signed-off-by: Jan Kara <[email protected]> Reviewed-by: Josef Bacik <[email protected]> Link: https://patch.msgid.link/[email protected]
1 parent 9852d85 commit 35ceae4

File tree

3 files changed

+19
-12
lines changed

3 files changed

+19
-12
lines changed

fs/notify/fsnotify.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,8 +183,10 @@ static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask,
183183
BUILD_BUG_ON(FS_EVENTS_POSS_ON_CHILD & ~FS_EVENTS_POSS_TO_PARENT);
184184

185185
/* Did either inode/sb/mount subscribe for events with parent/name? */
186-
marks_mask |= fsnotify_parent_needed_mask(inode->i_fsnotify_mask);
187-
marks_mask |= fsnotify_parent_needed_mask(inode->i_sb->s_fsnotify_mask);
186+
marks_mask |= fsnotify_parent_needed_mask(
187+
READ_ONCE(inode->i_fsnotify_mask));
188+
marks_mask |= fsnotify_parent_needed_mask(
189+
READ_ONCE(inode->i_sb->s_fsnotify_mask));
188190
marks_mask |= fsnotify_parent_needed_mask(mnt_mask);
189191

190192
/* Did they subscribe for this event with parent/name info? */
@@ -195,8 +197,8 @@ static bool fsnotify_event_needs_parent(struct inode *inode, __u32 mnt_mask,
195197
static inline bool fsnotify_object_watched(struct inode *inode, __u32 mnt_mask,
196198
__u32 mask)
197199
{
198-
__u32 marks_mask = inode->i_fsnotify_mask | mnt_mask |
199-
inode->i_sb->s_fsnotify_mask;
200+
__u32 marks_mask = READ_ONCE(inode->i_fsnotify_mask) | mnt_mask |
201+
READ_ONCE(inode->i_sb->s_fsnotify_mask);
200202

201203
return mask & marks_mask & ALL_FSNOTIFY_EVENTS;
202204
}
@@ -213,7 +215,8 @@ int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
213215
int data_type)
214216
{
215217
const struct path *path = fsnotify_data_path(data, data_type);
216-
__u32 mnt_mask = path ? real_mount(path->mnt)->mnt_fsnotify_mask : 0;
218+
__u32 mnt_mask = path ?
219+
READ_ONCE(real_mount(path->mnt)->mnt_fsnotify_mask) : 0;
217220
struct inode *inode = d_inode(dentry);
218221
struct dentry *parent;
219222
bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
@@ -557,13 +560,13 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
557560
(!inode2 || !inode2->i_fsnotify_marks))
558561
return 0;
559562

560-
marks_mask = sb->s_fsnotify_mask;
563+
marks_mask = READ_ONCE(sb->s_fsnotify_mask);
561564
if (mnt)
562-
marks_mask |= mnt->mnt_fsnotify_mask;
565+
marks_mask |= READ_ONCE(mnt->mnt_fsnotify_mask);
563566
if (inode)
564-
marks_mask |= inode->i_fsnotify_mask;
567+
marks_mask |= READ_ONCE(inode->i_fsnotify_mask);
565568
if (inode2)
566-
marks_mask |= inode2->i_fsnotify_mask;
569+
marks_mask |= READ_ONCE(inode2->i_fsnotify_mask);
567570

568571

569572
/*

fs/notify/inotify/inotify_user.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
569569
/* more bits in old than in new? */
570570
int dropped = (old_mask & ~new_mask);
571571
/* more bits in this fsn_mark than the inode's mask? */
572-
int do_inode = (new_mask & ~inode->i_fsnotify_mask);
572+
int do_inode = (new_mask & ~READ_ONCE(inode->i_fsnotify_mask));
573573

574574
/* update the inode with this new fsn_mark */
575575
if (dropped || do_inode)

fs/notify/mark.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ __u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn)
128128
if (WARN_ON(!fsnotify_valid_obj_type(conn->type)))
129129
return 0;
130130

131-
return *fsnotify_conn_mask_p(conn);
131+
return READ_ONCE(*fsnotify_conn_mask_p(conn));
132132
}
133133

134134
static void fsnotify_get_sb_watched_objects(struct super_block *sb)
@@ -245,7 +245,11 @@ static void *__fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
245245
!(mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
246246
want_iref = true;
247247
}
248-
*fsnotify_conn_mask_p(conn) = new_mask;
248+
/*
249+
* We use WRITE_ONCE() to prevent silly compiler optimizations from
250+
* confusing readers not holding conn->lock with partial updates.
251+
*/
252+
WRITE_ONCE(*fsnotify_conn_mask_p(conn), new_mask);
249253

250254
return fsnotify_update_iref(conn, want_iref);
251255
}

0 commit comments

Comments
 (0)