mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 12:52:29 -04:00
When an eventfs inode is freed, it sets ei->is_freed and then uses its
ei->list to add it to the srcu link list as the list field is a union with
the rcu list head. As the ei->list is used to iterate over an SRCU
protected list without taking the eventfs_mutex, there's nothing stopping
the iteration over that list to see the ei->rcu instead of the ei->list
and it will read a corrupt target.
To fix this, change the union of the rcu list head with the children list.
On freeing the eventfs inode, set the is_free and execute a smp_wmb()
before adding the eventfs inode to the SRCU list.
On iteration of the ei->children list, at the start, execute a smp_rmb()
and then read the is_freed of the ei to see if the children list is still
valid. If is_freed is set, then the ei_child read is not valid and the
loop should exit immediately.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260808094215.4252430d@robin
Fixes: 704f960dbe ("eventfs: Read ei->entries before ei->children in eventfs_iterate()")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260806022719.375354-1-shuangpeng.kernel%40gmail.com
Reviewed-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
83 lines
2.5 KiB
C
83 lines
2.5 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _TRACEFS_INTERNAL_H
|
|
#define _TRACEFS_INTERNAL_H
|
|
|
|
enum {
|
|
TRACEFS_EVENT_INODE = BIT(1),
|
|
TRACEFS_GID_PERM_SET = BIT(2),
|
|
TRACEFS_UID_PERM_SET = BIT(3),
|
|
TRACEFS_INSTANCE_INODE = BIT(4),
|
|
};
|
|
|
|
struct tracefs_inode {
|
|
struct inode vfs_inode;
|
|
/* The below gets initialized with memset_after(ti, 0, vfs_inode) */
|
|
struct list_head list;
|
|
unsigned long flags;
|
|
void *private;
|
|
};
|
|
|
|
/*
|
|
* struct eventfs_attr - cache the mode and ownership of a eventfs entry
|
|
* @mode: saved mode plus flags of what is saved
|
|
* @uid: saved uid if changed
|
|
* @gid: saved gid if changed
|
|
*/
|
|
struct eventfs_attr {
|
|
int mode;
|
|
kuid_t uid;
|
|
kgid_t gid;
|
|
};
|
|
|
|
/*
|
|
* struct eventfs_inode - hold the properties of the eventfs directories.
|
|
* @list: link list into the parent directory
|
|
* @rcu: Union with @list for freeing
|
|
* @children: link list into the child eventfs_inode
|
|
* @entries: the array of entries representing the files in the directory
|
|
* @name: the name of the directory to create
|
|
* @entry_attrs: Saved mode and ownership of the @d_children
|
|
* @data: The private data to pass to the callbacks
|
|
* @attr: Saved mode and ownership of eventfs_inode itself
|
|
* @is_freed: Flag set if the eventfs is on its way to be freed
|
|
* Note if is_freed is set, then dentry is corrupted.
|
|
* @is_events: Flag set for only the top level "events" directory
|
|
* @nr_entries: The number of items in @entries
|
|
* @ino: The saved inode number
|
|
*/
|
|
struct eventfs_inode {
|
|
struct list_head list;
|
|
union {
|
|
struct list_head children;
|
|
struct rcu_head rcu;
|
|
};
|
|
const struct eventfs_entry *entries;
|
|
const char *name;
|
|
struct eventfs_attr *entry_attrs;
|
|
void *data;
|
|
struct eventfs_attr attr;
|
|
struct kref kref;
|
|
unsigned int is_freed:1;
|
|
unsigned int is_events:1;
|
|
unsigned int nr_entries:30;
|
|
unsigned int ino;
|
|
};
|
|
|
|
static inline struct tracefs_inode *get_tracefs(const struct inode *inode)
|
|
{
|
|
return container_of(inode, struct tracefs_inode, vfs_inode);
|
|
}
|
|
|
|
struct dentry *tracefs_start_creating(const char *name, struct dentry *parent);
|
|
struct dentry *tracefs_end_creating(struct dentry *dentry);
|
|
struct dentry *tracefs_failed_creating(struct dentry *dentry);
|
|
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 */
|