Merge patch series "ns: tweak ns common handling"

Christian Brauner <brauner@kernel.org> says:

This contains three minor tweaks for namespace handling:

* Make struct ns_tree private. There's no need for anything to access
  that directly.

* Drop a debug assert that would trigger in conditions that are benign.

* Move the type of the namespace out of struct proc_ns_operations and
  into struct ns_common. This eliminates a pointer dereference and also
  allows assertions to work when the namespace type is disabled and the
  operations field set to NULL.

* patches from https://lore.kernel.org/20250924-work-namespaces-fixes-v1-0-8fb682c8678e@kernel.org:
  ns: drop assert
  ns: move ns type into struct ns_common
  nstree: make struct ns_tree private

Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
Christian Brauner 2025-09-25 09:22:09 +02:00
commit 6e65f4e8fc
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2
20 changed files with 65 additions and 49 deletions

View File

@ -4927,7 +4927,7 @@ static int build_mount_idmapped(const struct mount_attr *attr, size_t usize,
return -EINVAL;
ns = get_proc_ns(file_inode(fd_file(f)));
if (ns->ops->type != CLONE_NEWUSER)
if (ns->ns_type != CLONE_NEWUSER)
return -EINVAL;
/*
@ -5830,7 +5830,7 @@ static struct mnt_namespace *grab_requested_mnt_ns(const struct mnt_id_req *kreq
return ERR_PTR(-EINVAL);
ns = get_proc_ns(file_inode(fd_file(f)));
if (ns->ops->type != CLONE_NEWNS)
if (ns->ns_type != CLONE_NEWNS)
return ERR_PTR(-EINVAL);
mnt_ns = to_mnt_ns(ns);
@ -6016,6 +6016,7 @@ struct mnt_namespace init_mnt_ns = {
.ns.ops = &mntns_operations,
.user_ns = &init_user_ns,
.ns.__ns_ref = REFCOUNT_INIT(1),
.ns.ns_type = ns_common_type(&init_mnt_ns),
.passive = REFCOUNT_INIT(1),
.mounts = RB_ROOT,
.poll = __WAIT_QUEUE_HEAD_INITIALIZER(init_mnt_ns.poll),
@ -6333,7 +6334,6 @@ static struct user_namespace *mntns_owner(struct ns_common *ns)
const struct proc_ns_operations mntns_operations = {
.name = "mnt",
.type = CLONE_NEWNS,
.get = mntns_get,
.put = mntns_put,
.install = mntns_install,

View File

@ -219,9 +219,9 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
return -EINVAL;
return open_related_ns(ns, ns->ops->get_parent);
case NS_GET_NSTYPE:
return ns->ops->type;
return ns->ns_type;
case NS_GET_OWNER_UID:
if (ns->ops->type != CLONE_NEWUSER)
if (ns->ns_type != CLONE_NEWUSER)
return -EINVAL;
user_ns = container_of(ns, struct user_namespace, ns);
argp = (uid_t __user *) arg;
@ -234,7 +234,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
case NS_GET_PID_IN_PIDNS:
fallthrough;
case NS_GET_TGID_IN_PIDNS: {
if (ns->ops->type != CLONE_NEWPID)
if (ns->ns_type != CLONE_NEWPID)
return -EINVAL;
ret = -ESRCH;
@ -273,7 +273,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
return ret;
}
case NS_GET_MNTNS_ID:
if (ns->ops->type != CLONE_NEWNS)
if (ns->ns_type != CLONE_NEWNS)
return -EINVAL;
fallthrough;
case NS_GET_ID: {
@ -293,7 +293,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
struct mnt_ns_info __user *uinfo = (struct mnt_ns_info __user *)arg;
size_t usize = _IOC_SIZE(ioctl);
if (ns->ops->type != CLONE_NEWNS)
if (ns->ns_type != CLONE_NEWNS)
return -EINVAL;
if (!uinfo)
@ -314,7 +314,7 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
struct file *f __free(fput) = NULL;
size_t usize = _IOC_SIZE(ioctl);
if (ns->ops->type != CLONE_NEWNS)
if (ns->ns_type != CLONE_NEWNS)
return -EINVAL;
if (usize < MNT_NS_INFO_SIZE_VER0)
@ -453,7 +453,7 @@ static int nsfs_encode_fh(struct inode *inode, u32 *fh, int *max_len,
}
fid->ns_id = ns->ns_id;
fid->ns_type = ns->ops->type;
fid->ns_type = ns->ns_type;
fid->ns_inum = inode->i_ino;
return FILEID_NSFS;
}
@ -489,14 +489,14 @@ static struct dentry *nsfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
return NULL;
VFS_WARN_ON_ONCE(ns->ns_id != fid->ns_id);
VFS_WARN_ON_ONCE(ns->ops->type != fid->ns_type);
VFS_WARN_ON_ONCE(ns->ns_type != fid->ns_type);
VFS_WARN_ON_ONCE(ns->inum != fid->ns_inum);
if (!__ns_ref_get(ns))
return NULL;
}
switch (ns->ops->type) {
switch (ns->ns_type) {
#ifdef CONFIG_CGROUPS
case CLONE_NEWCGROUP:
if (!current_in_namespace(to_cg_ns(ns)))

View File

@ -4,6 +4,7 @@
#include <linux/refcount.h>
#include <linux/rbtree.h>
#include <uapi/linux/sched.h>
struct proc_ns_operations;
@ -37,6 +38,7 @@ extern const struct proc_ns_operations timens_operations;
extern const struct proc_ns_operations timens_for_children_operations;
struct ns_common {
u32 ns_type;
struct dentry *stashed;
const struct proc_ns_operations *ops;
unsigned int inum;
@ -51,7 +53,7 @@ struct ns_common {
};
};
int __ns_common_init(struct ns_common *ns, const struct proc_ns_operations *ops, int inum);
int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum);
void __ns_common_free(struct ns_common *ns);
#define to_ns_common(__ns) \
@ -106,10 +108,28 @@ void __ns_common_free(struct ns_common *ns);
struct user_namespace *: (IS_ENABLED(CONFIG_USER_NS) ? &userns_operations : NULL), \
struct uts_namespace *: (IS_ENABLED(CONFIG_UTS_NS) ? &utsns_operations : NULL))
#define ns_common_init(__ns) \
__ns_common_init(to_ns_common(__ns), to_ns_operations(__ns), (((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0))
#define ns_common_type(__ns) \
_Generic((__ns), \
struct cgroup_namespace *: CLONE_NEWCGROUP, \
struct ipc_namespace *: CLONE_NEWIPC, \
struct mnt_namespace *: CLONE_NEWNS, \
struct net *: CLONE_NEWNET, \
struct pid_namespace *: CLONE_NEWPID, \
struct time_namespace *: CLONE_NEWTIME, \
struct user_namespace *: CLONE_NEWUSER, \
struct uts_namespace *: CLONE_NEWUTS)
#define ns_common_init_inum(__ns, __inum) __ns_common_init(to_ns_common(__ns), to_ns_operations(__ns), __inum)
#define ns_common_init(__ns) \
__ns_common_init(to_ns_common(__ns), \
ns_common_type(__ns), \
to_ns_operations(__ns), \
(((__ns) == ns_init_ns(__ns)) ? ns_init_inum(__ns) : 0))
#define ns_common_init_inum(__ns, __inum) \
__ns_common_init(to_ns_common(__ns), \
ns_common_type(__ns), \
to_ns_operations(__ns), \
__inum)
#define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns)))

View File

@ -9,19 +9,6 @@
#include <linux/rculist.h>
#include <linux/cookie.h>
/**
* struct ns_tree - Namespace tree
* @ns_tree: Rbtree of namespaces of a particular type
* @ns_list: Sequentially walkable list of all namespaces of this type
* @ns_tree_lock: Seqlock to protect the tree and list
*/
struct ns_tree {
struct rb_root ns_tree;
struct list_head ns_list;
seqlock_t ns_tree_lock;
int type;
};
extern struct ns_tree cgroup_ns_tree;
extern struct ns_tree ipc_ns_tree;
extern struct ns_tree mnt_ns_tree;

View File

@ -17,7 +17,6 @@ struct inode;
struct proc_ns_operations {
const char *name;
const char *real_ns_name;
int type;
struct ns_common *(*get)(struct task_struct *task);
void (*put)(struct ns_common *ns);
int (*install)(struct nsset *nsset, struct ns_common *ns);

View File

@ -8,6 +8,7 @@
#include <linux/utsname.h>
struct uts_namespace init_uts_ns = {
.ns.ns_type = ns_common_type(&init_uts_ns),
.ns.__ns_ref = REFCOUNT_INIT(2),
.name = {
.sysname = UTS_SYSNAME,

View File

@ -33,6 +33,7 @@ struct ipc_namespace init_ipc_ns = {
#ifdef CONFIG_IPC_NS
.ns.ops = &ipcns_operations,
#endif
.ns.ns_type = ns_common_type(&init_ipc_ns),
};
struct msg_msgseg {

View File

@ -248,7 +248,6 @@ static struct user_namespace *ipcns_owner(struct ns_common *ns)
const struct proc_ns_operations ipcns_operations = {
.name = "ipc",
.type = CLONE_NEWIPC,
.get = ipcns_get,
.put = ipcns_put,
.install = ipcns_install,

View File

@ -224,6 +224,7 @@ struct cgroup_namespace init_cgroup_ns = {
.ns.ops = &cgroupns_operations,
.ns.inum = ns_init_inum(&init_cgroup_ns),
.root_cset = &init_css_set,
.ns.ns_type = ns_common_type(&init_cgroup_ns),
};
static struct file_system_type cgroup2_fs_type;

View File

@ -137,7 +137,6 @@ static struct user_namespace *cgroupns_owner(struct ns_common *ns)
const struct proc_ns_operations cgroupns_operations = {
.name = "cgroup",
.type = CLONE_NEWCGROUP,
.get = cgroupns_get,
.put = cgroupns_put,
.install = cgroupns_install,

View File

@ -7,7 +7,7 @@
#ifdef CONFIG_DEBUG_VFS
static void ns_debug(struct ns_common *ns, const struct proc_ns_operations *ops)
{
switch (ns->ops->type) {
switch (ns->ns_type) {
#ifdef CONFIG_CGROUPS
case CLONE_NEWCGROUP:
VFS_WARN_ON_ONCE(ops != &cgroupns_operations);
@ -46,18 +46,17 @@ static void ns_debug(struct ns_common *ns, const struct proc_ns_operations *ops)
VFS_WARN_ON_ONCE(ops != &utsns_operations);
break;
#endif
default:
VFS_WARN_ON_ONCE(true);
}
}
#endif
int __ns_common_init(struct ns_common *ns, const struct proc_ns_operations *ops, int inum)
int __ns_common_init(struct ns_common *ns, u32 ns_type, const struct proc_ns_operations *ops, int inum)
{
refcount_set(&ns->__ns_ref, 1);
ns->stashed = NULL;
ns->ops = ops;
ns->ns_id = 0;
ns->ns_type = ns_type;
RB_CLEAR_NODE(&ns->ns_tree_node);
INIT_LIST_HEAD(&ns->ns_list_node);

View File

@ -545,9 +545,9 @@ SYSCALL_DEFINE2(setns, int, fd, int, flags)
if (proc_ns_file(fd_file(f))) {
ns = get_proc_ns(file_inode(fd_file(f)));
if (flags && (ns->ops->type != flags))
if (flags && (ns->ns_type != flags))
err = -EINVAL;
flags = ns->ops->type;
flags = ns->ns_type;
} else if (!IS_ERR(pidfd_pid(fd_file(f)))) {
err = check_setns_flags(flags);
} else {

View File

@ -4,6 +4,20 @@
#include <linux/proc_ns.h>
#include <linux/vfsdebug.h>
/**
* struct ns_tree - Namespace tree
* @ns_tree: Rbtree of namespaces of a particular type
* @ns_list: Sequentially walkable list of all namespaces of this type
* @ns_tree_lock: Seqlock to protect the tree and list
* @type: type of namespaces in this tree
*/
struct ns_tree {
struct rb_root ns_tree;
struct list_head ns_list;
seqlock_t ns_tree_lock;
int type;
};
struct ns_tree mnt_ns_tree = {
.ns_tree = RB_ROOT,
.ns_list = LIST_HEAD_INIT(mnt_ns_tree.ns_list),
@ -92,7 +106,7 @@ void __ns_tree_add_raw(struct ns_common *ns, struct ns_tree *ns_tree)
write_seqlock(&ns_tree->ns_tree_lock);
VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type);
VFS_WARN_ON_ONCE(ns->ns_type != ns_tree->type);
node = rb_find_add_rcu(&ns->ns_tree_node, &ns_tree->ns_tree, ns_cmp);
/*
@ -114,7 +128,7 @@ void __ns_tree_remove(struct ns_common *ns, struct ns_tree *ns_tree)
{
VFS_WARN_ON_ONCE(RB_EMPTY_NODE(&ns->ns_tree_node));
VFS_WARN_ON_ONCE(list_empty(&ns->ns_list_node));
VFS_WARN_ON_ONCE(ns->ops->type != ns_tree->type);
VFS_WARN_ON_ONCE(ns->ns_type != ns_tree->type);
write_seqlock(&ns_tree->ns_tree_lock);
rb_erase(&ns->ns_tree_node, &ns_tree->ns_tree);
@ -183,7 +197,7 @@ struct ns_common *ns_tree_lookup_rcu(u64 ns_id, int ns_type)
if (!node)
return NULL;
VFS_WARN_ON_ONCE(node_to_ns(node)->ops->type != ns_type);
VFS_WARN_ON_ONCE(node_to_ns(node)->ns_type != ns_type);
return node_to_ns(node);
}
@ -211,7 +225,7 @@ struct ns_common *__ns_tree_adjoined_rcu(struct ns_common *ns,
if (list_is_head(list, &ns_tree->ns_list))
return ERR_PTR(-ENOENT);
VFS_WARN_ON_ONCE(list_entry_rcu(list, struct ns_common, ns_list_node)->ops->type != ns_tree->type);
VFS_WARN_ON_ONCE(list_entry_rcu(list, struct ns_common, ns_list_node)->ns_type != ns_tree->type);
return list_entry_rcu(list, struct ns_common, ns_list_node);
}

View File

@ -85,6 +85,7 @@ struct pid_namespace init_pid_ns = {
#if defined(CONFIG_SYSCTL) && defined(CONFIG_MEMFD_CREATE)
.memfd_noexec_scope = MEMFD_NOEXEC_SCOPE_EXEC,
#endif
.ns.ns_type = ns_common_type(&init_pid_ns),
};
EXPORT_SYMBOL_GPL(init_pid_ns);

View File

@ -443,7 +443,6 @@ static struct user_namespace *pidns_owner(struct ns_common *ns)
const struct proc_ns_operations pidns_operations = {
.name = "pid",
.type = CLONE_NEWPID,
.get = pidns_get,
.put = pidns_put,
.install = pidns_install,
@ -454,7 +453,6 @@ const struct proc_ns_operations pidns_operations = {
const struct proc_ns_operations pidns_for_children_operations = {
.name = "pid_for_children",
.real_ns_name = "pid",
.type = CLONE_NEWPID,
.get = pidns_for_children_get,
.put = pidns_put,
.install = pidns_install,

View File

@ -462,7 +462,6 @@ out:
const struct proc_ns_operations timens_operations = {
.name = "time",
.type = CLONE_NEWTIME,
.get = timens_get,
.put = timens_put,
.install = timens_install,
@ -472,7 +471,6 @@ const struct proc_ns_operations timens_operations = {
const struct proc_ns_operations timens_for_children_operations = {
.name = "time_for_children",
.real_ns_name = "time",
.type = CLONE_NEWTIME,
.get = timens_for_children_get,
.put = timens_put,
.install = timens_install,
@ -480,6 +478,7 @@ const struct proc_ns_operations timens_for_children_operations = {
};
struct time_namespace init_time_ns = {
.ns.ns_type = ns_common_type(&init_time_ns),
.ns.__ns_ref = REFCOUNT_INIT(3),
.user_ns = &init_user_ns,
.ns.inum = ns_init_inum(&init_time_ns),

View File

@ -65,6 +65,7 @@ struct user_namespace init_user_ns = {
.nr_extents = 1,
},
},
.ns.ns_type = ns_common_type(&init_user_ns),
.ns.__ns_ref = REFCOUNT_INIT(3),
.owner = GLOBAL_ROOT_UID,
.group = GLOBAL_ROOT_GID,

View File

@ -1400,7 +1400,6 @@ static struct user_namespace *userns_owner(struct ns_common *ns)
const struct proc_ns_operations userns_operations = {
.name = "user",
.type = CLONE_NEWUSER,
.get = userns_get,
.put = userns_put,
.install = userns_install,

View File

@ -146,7 +146,6 @@ static struct user_namespace *utsns_owner(struct ns_common *ns)
const struct proc_ns_operations utsns_operations = {
.name = "uts",
.type = CLONE_NEWUTS,
.get = utsns_get,
.put = utsns_put,
.install = utsns_install,

View File

@ -1543,7 +1543,6 @@ static struct user_namespace *netns_owner(struct ns_common *ns)
const struct proc_ns_operations netns_operations = {
.name = "net",
.type = CLONE_NEWNET,
.get = netns_get,
.put = netns_put,
.install = netns_install,