kernfs_perms_shown() tests a node's ve_perms_map with kmapset_lookup(), which walks an rcu hlist (map->links). That walk must run inside an rcu read-side critical section, but the seq read holds only kernfs_rwsem and the caller's perms mutex, neither of which is one.
kmapset frees links with kfree_rcu() from kmapset_unlink() (called by ve_destroy()) and kmapset_del_value(), under the kmapset set lock, a different lock from the ones the reader holds. So a concurrent VE teardown can free a link the lookup is walking, a use-after-free. Take rcu_read_lock() around the lookup. kernfs_perms_show() reaches the same list walk through kmapset_get_value(), which already takes rcu_read_lock() itself, so only kernfs_perms_shown() needs the wrapper. https://virtuozzo.atlassian.net/browse/VSTOR-136480 Fixes: 008aa8b6ff0b ("ve/kernfs: add new interface to control per-VE nodes visibility") Signed-off-by: Mirian Shilakadze <[email protected]> --- fs/kernfs/ve.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/kernfs/ve.c b/fs/kernfs/ve.c index 2858b7e97f6e..66bb4b534f48 100644 --- a/fs/kernfs/ve.c +++ b/fs/kernfs/ve.c @@ -146,11 +146,17 @@ static struct kernfs_node *kernfs_next_recursive(struct kernfs_node *kn) static bool kernfs_perms_shown(struct ve_struct *ve, struct kernfs_node *kn, struct kmapset_key *key) { + bool ret; + if (!kn->ve_perms_map) return false; if (ve_is_super(ve)) return kn->ve_perms_map->default_value != 0; - return kmapset_lookup(kn->ve_perms_map, key) != NULL; + /* kmapset_lookup() walks an rcu list, so keep the section to the lookup. */ + rcu_read_lock(); + ret = kmapset_lookup(kn->ve_perms_map, key) != NULL; + rcu_read_unlock(); + return ret; } void *kernfs_perms_start(struct seq_file *m, loff_t *ppos, -- 2.43.0 _______________________________________________ Devel mailing list [email protected] https://lists.openvz.org/mailman/listinfo/devel
