VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46106

CVE-2026-46106

Description

In the Linux kernel, the following vulnerability has been resolved:

eventfs: Hold eventfs_mutex and SRCU when remount walks events

Commit 340f0c7067a9 ("eventfs: Update all the eventfs_inodes from the events descriptor") had eventfs_set_attrs() recurse through ei->children on remount. The walk only holds the rcu_read_lock() taken by tracefs_apply_options() over tracefs_inodes, which is wrong:

- list_for_each_entry over ei->children races with the list_del_rcu() in eventfs_remove_rec() -- LIST_POISON1 deref, same shape as d2603279c7d6. - eventfs_inodes are freed via call_srcu(&eventfs_srcu, ...). rcu_read_lock() does not extend an SRCU grace period, so ti->private can be reclaimed under the walk. - The writes to ei->attr race with eventfs_set_attr(), which holds eventfs_mutex.

Reproducer:

while :; do mount -o remount,uid=$((RANDOM%1000)) /sys/kernel/tracing; done & while :; do echo "p:kp submit_bio" > /sys/kernel/tracing/kprobe_events echo > /sys/kernel/tracing/kprobe_events done

Wrap the events portion of tracefs_apply_options() in eventfs_remount_lock()/_unlock() that take eventfs_mutex and srcu_read_lock(&eventfs_srcu). eventfs_set_attrs() doesn't sleep so the nested rcu_read_lock() is fine; lockdep_assert_held() pins the contract.

Comment in tracefs_drop_inode() said "RCU cycle" -- it is SRCU.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A race condition in Linux kernel's eventfs subsystem allows use-after-free during remount, potentially leading to privilege escalation.

Vulnerability

The vulnerability resides in eventfs_set_attrs(), which recursively walks ei->children without proper locking. The walk only holds rcu_read_lock(), but the list uses list_del_rcu() and call_srcu() for freeing, so it races with removal and use-after-free. Affected versions are those containing commit 340f0c7067a9 (likely 6.x). The fix is in commit ae9cd0b46b1890040006a2fc5e905c5d6053fd02 [1].

Exploitation

An attacker needs local access to mount and remount tracefs (/sys/kernel/tracing) and create/destroy kprobe events. The reproducer shows a loop of remount with random UID and concurrent kprobe creation/deletion. The race window is small but can be triggered repeatedly [1].

Impact

Successful exploitation leads to a use-after-free, potentially allowing an attacker to corrupt kernel memory and escalate privileges. The exact impact depends on memory layout, but it could lead to arbitrary code execution in kernel context [1].

Mitigation

The fix is in commit ae9cd0b46b1890040006a2fc5e905c5d6053fd02, which wraps the walk in eventfs_remount_lock()/_unlock() that takes eventfs_mutex and srcu_read_lock(&eventfs_srcu). Users should apply the kernel patch. No workaround is mentioned; the vulnerability is not listed in KEV [1].

AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
07004a8c4b57

eventfs: Hold eventfs_mutex and SRCU when remount walks events

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid CarlierApr 18, 2026Fixed in 7.1-rc1via kernel-cna
3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 8dd554508828b8..26b6453de30ef6 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -244,6 +244,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -886,3 +888,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	d_make_discardable(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 5602baf980f685..1e8a78c5e996a6 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -313,6 +313,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -337,6 +338,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -358,6 +360,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -403,7 +406,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
ae9cd0b46b18

eventfs: Hold eventfs_mutex and SRCU when remount walks events

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid CarlierApr 18, 2026Fixed in 6.6.140via kernel-cna
3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 4190e615504490..f98315e91e99b4 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -310,6 +310,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -985,3 +987,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 6b70965063d739..6c7b4877094800 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -362,6 +362,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct tracefs_mount_opts *opts = &fsi->mount_opts;
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -386,6 +387,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = opts->opts & BIT(Opt_uid);
     		update_gid = opts->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid)
    @@ -398,6 +400,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -444,7 +447,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
44e64d8a4328

eventfs: Hold eventfs_mutex and SRCU when remount walks events

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid CarlierApr 18, 2026Fixed in 6.12.88via kernel-cna
3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 93c231601c8e23..0d2bc92b760f3f 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 9f15d606dfde79..8e5db8cc42188e 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
52b109f1b875

eventfs: Hold eventfs_mutex and SRCU when remount walks events

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid CarlierApr 18, 2026Fixed in 6.18.30via kernel-cna
3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 93c231601c8e23..0d2bc92b760f3f 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 35c0cc557fbb75..363a5d75541198 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
ed2ad73bcb0a

eventfs: Hold eventfs_mutex and SRCU when remount walks events

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitDavid CarlierApr 18, 2026Fixed in 7.0.7via kernel-cna
3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 8e5ac464b32849..af3387eebef5b9 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	d_make_discardable(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 8ba72c5a435cae..40477513cce1e4 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
07004a8c4b57

eventfs: Hold eventfs_mutex and SRCU when remount walks events

3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 8dd554508828b8..26b6453de30ef6 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -244,6 +244,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -886,3 +888,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	d_make_discardable(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 5602baf980f685..1e8a78c5e996a6 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -313,6 +313,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -337,6 +338,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -358,6 +360,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -403,7 +406,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
44e64d8a4328

eventfs: Hold eventfs_mutex and SRCU when remount walks events

3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 93c231601c8e23..0d2bc92b760f3f 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 9f15d606dfde79..8e5db8cc42188e 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
52b109f1b875

eventfs: Hold eventfs_mutex and SRCU when remount walks events

3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 93c231601c8e23..0d2bc92b760f3f 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 35c0cc557fbb75..363a5d75541198 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
ae9cd0b46b18

eventfs: Hold eventfs_mutex and SRCU when remount walks events

3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 4190e615504490..f98315e91e99b4 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -310,6 +310,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -985,3 +987,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	dput(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 6b70965063d739..6c7b4877094800 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -362,6 +362,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct tracefs_mount_opts *opts = &fsi->mount_opts;
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -386,6 +387,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = opts->opts & BIT(Opt_uid);
     		update_gid = opts->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid)
    @@ -398,6 +400,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -444,7 +447,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    
ed2ad73bcb0a

eventfs: Hold eventfs_mutex and SRCU when remount walks events

3 files changed · +21 2
  • fs/tracefs/event_inode.c+14 0 modified
    diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c
    index 8e5ac464b32849..af3387eebef5b9 100644
    --- a/fs/tracefs/event_inode.c
    +++ b/fs/tracefs/event_inode.c
    @@ -250,6 +250,8 @@ static void eventfs_set_attrs(struct eventfs_inode *ei, bool update_uid, kuid_t
     {
     	struct eventfs_inode *ei_child;
     
    +	lockdep_assert_held(&eventfs_mutex);
    +
     	/* Update events/<system>/<event> */
     	if (WARN_ON_ONCE(level > 3))
     		return;
    @@ -912,3 +914,15 @@ void eventfs_remove_events_dir(struct eventfs_inode *ei)
     	d_invalidate(dentry);
     	d_make_discardable(dentry);
     }
    +
    +int eventfs_remount_lock(void)
    +{
    +	mutex_lock(&eventfs_mutex);
    +	return srcu_read_lock(&eventfs_srcu);
    +}
    +
    +void eventfs_remount_unlock(int srcu_idx)
    +{
    +	srcu_read_unlock(&eventfs_srcu, srcu_idx);
    +	mutex_unlock(&eventfs_mutex);
    +}
    
  • fs/tracefs/inode.c+4 1 modified
    diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c
    index 8ba72c5a435cae..40477513cce1e4 100644
    --- a/fs/tracefs/inode.c
    +++ b/fs/tracefs/inode.c
    @@ -336,6 +336,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     	struct inode *inode = d_inode(sb->s_root);
     	struct tracefs_inode *ti;
     	bool update_uid, update_gid;
    +	int srcu_idx;
     	umode_t tmp_mode;
     
     	/*
    @@ -360,6 +361,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     		update_uid = fsi->opts & BIT(Opt_uid);
     		update_gid = fsi->opts & BIT(Opt_gid);
     
    +		srcu_idx = eventfs_remount_lock();
     		rcu_read_lock();
     		list_for_each_entry_rcu(ti, &tracefs_inodes, list) {
     			if (update_uid) {
    @@ -381,6 +383,7 @@ static int tracefs_apply_options(struct super_block *sb, bool remount)
     				eventfs_remount(ti, update_uid, update_gid);
     		}
     		rcu_read_unlock();
    +		eventfs_remount_unlock(srcu_idx);
     	}
     
     	return 0;
    @@ -426,7 +429,7 @@ static int tracefs_drop_inode(struct inode *inode)
     	 * This inode is being freed and cannot be used for
     	 * eventfs. Clear the flag so that it doesn't call into
     	 * eventfs during the remount flag updates. The eventfs_inode
    -	 * gets freed after an RCU cycle, so the content will still
    +	 * gets freed after an SRCU cycle, so the content will still
     	 * be safe if the iteration is going on now.
     	 */
     	ti->flags &= ~TRACEFS_EVENT_INODE;
    
  • fs/tracefs/internal.h+3 1 modified
    diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h
    index d83c2a25f288e0..a4a7f8431affb0 100644
    --- a/fs/tracefs/internal.h
    +++ b/fs/tracefs/internal.h
    @@ -76,4 +76,7 @@ struct inode *tracefs_get_inode(struct super_block *sb);
     void eventfs_remount(struct tracefs_inode *ti, bool update_uid, bool update_gid);
     void eventfs_d_release(struct dentry *dentry);
     
    +int eventfs_remount_lock(void);
    +void eventfs_remount_unlock(int srcu_idx);
    +
     #endif /* _TRACEFS_INTERNAL_H */
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Insufficient locking in the remount walk of eventfs_inodes — the traversal of ei->children only held rcu_read_lock() instead of eventfs_mutex and SRCU read lock, allowing concurrent list removal, use-after-free, and attribute races."

Attack vector

An attacker with local access can trigger a race condition by concurrently running a remount loop (e.g., `mount -o remount,uid=$((RANDOM%1000)) /sys/kernel/tracing`) and a kprobe create/remove loop (e.g., `echo "p:kp submit_bio" > /sys/kernel/tracing/kprobe_events` followed by clearing it) [patch_id=2898675]. The remount walk of `ei->children` via `list_for_each_entry` races with `list_del_rcu()` in `eventfs_remove_rec()`, causing a LIST_POISON1 dereference. Additionally, `eventfs_inodes` are freed via `call_srcu(&eventfs_srcu, ...)` but the walk only holds `rcu_read_lock()`, which does not extend an SRCU grace period, so `ti->private` can be reclaimed under the walk. Writes to `ei->attr` also race with `eventfs_set_attr()` which holds `eventfs_mutex` [patch_id=2898675].

Affected code

The vulnerability is in `fs/tracefs/event_inode.c` in the `eventfs_set_attrs()` function and in `fs/tracefs/inode.c` in `tracefs_apply_options()`. The remount path in `tracefs_apply_options()` iterates `tracefs_inodes` and calls `eventfs_remount()`, which recurses through `ei->children` via `eventfs_set_attrs()` [patch_id=2898675].

What the fix does

The patch wraps the events portion of `tracefs_apply_options()` in new `eventfs_remount_lock()`/`eventfs_remount_unlock()` helpers that acquire `eventfs_mutex` and take `srcu_read_lock(&eventfs_srcu)` [patch_id=2898675]. This prevents the three races: `eventfs_mutex` serializes the `ei->children` list walk against concurrent `eventfs_remove_rec()` and `eventfs_set_attr()`; `srcu_read_lock()` ensures the SRCU grace period protects `ti->private` from being freed during the walk. A `lockdep_assert_held(&eventfs_mutex)` is also added to `eventfs_set_attrs()` to pin the contract. The comment in `tracefs_drop_inode()` is corrected from "RCU cycle" to "SRCU cycle" [patch_id=2898675].

Preconditions

  • authLocal access to the system with ability to mount/remount tracefs and create/remove kprobes
  • inputThe attacker must be able to run concurrent processes to trigger the race condition

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.