[Devel] [PATCH RHEL10 COMMIT] ve/mntns: thread owning ve through alloc_mnt_ns/copy_mnt_ns
Konstantin Khorenko
khorenko at virtuozzo.com
Thu May 14 18:51:19 MSK 2026
The commit is pushed to "branch-rh10-6.12.0-55.52.1.5.x.vz10-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh10-6.12.0-55.52.1.5.24.vz10
------>
commit 275fed0137dcc9316c6f1aeaeaf0e80428d6720c
Author: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Date: Wed Apr 29 15:41:37 2026 +0200
ve/mntns: thread owning ve through alloc_mnt_ns/copy_mnt_ns
Add new_ve parameter to alloc_mnt_ns() and copy_mnt_ns(). NULL preserves
the existing behaviour of taking current ve via get_exec_env().
This will be used to derive correct ownership of newly created mounts in
case of simultaneous creation of new ve namespace and mount namespace,
where we would like new mounts to have the new ve as an owner.
There is no change in behaviour yet: create_new_namespaces() still uses
NULL.
https://virtuozzo.atlassian.net/browse/VSTOR-129744
Signed-off-by: Pavel Tikhomirov <ptikhomirov at virtuozzo.com>
Reviewed-by: Vasileios Almpanis <vasileios.almpanis at virtuozzo.com>
Feature: ve: ve generic structures
======
Patchset description:
ve: fix owner_ve of net/mnt namespaces created together with CLONE_NEWVE
When CLONE_NEWVE is combined with CLONE_NEWNET and/or CLONE_NEWNS in a
single clone3() or unshare(), copy_net_ns() and copy_mnt_ns() resolve
the owning ve via get_exec_env(), which still points at the parent ve
at that point. The freshly created net/mnt namespaces end up wired to
the wrong ve, and unshare(CLONE_NEWVE | CLONE_NEW{NS,NET}) is rejected
outright by check_unshare_flags().
Fix it by threading the new ve from copy_namespaces() and
unshare_nsproxy_namespaces() down into copy_net_ns() and copy_mnt_ns(),
so the correct ve is charged for the new netns and for every mount in
the new mntns.
Patches 1-4 are pure plumbing (signature changes, no behaviour change).
Patch 5 is the actual fix that forwards the new ve. Patch 6 drops the
now-redundant CLONE_NEWVE-alone restriction in check_unshare_flags().
Patch 7 exposes ve.mnt_nr via cgroupfs to make per-ve mount accounting
observable from userspace. Patch 8 adds a selftest covering both the
clone3() and unshare() paths.
Verified with crash on a vzctl-started container: task_ve,
nsproxy->net_ns->owner_ve, nsproxy->mnt_ns->ve_owner and
nsproxy->mnt_ns->root.ve_owner all resolve to the new ve.
The new selftest passes both cases.
---
fs/namespace.c | 21 ++++++++++++---------
include/linux/mnt_namespace.h | 4 +++-
kernel/nsproxy.c | 3 ++-
3 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/fs/namespace.c b/fs/namespace.c
index ba2cee9a6db1c..865635366fd7e 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2239,7 +2239,8 @@ struct vfsmount *collect_mounts(const struct path *path)
}
static void free_mnt_ns(struct mnt_namespace *);
-static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *, bool);
+static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *, bool,
+ struct ve_struct *);
void dissolve_on_fput(struct vfsmount *mnt)
{
@@ -2867,7 +2868,7 @@ static int do_loopback(struct path *path, const char *old_name,
static struct file *open_detached_copy(struct path *path, bool recursive)
{
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
- struct mnt_namespace *ns = alloc_mnt_ns(user_ns, true);
+ struct mnt_namespace *ns = alloc_mnt_ns(user_ns, true, NULL);
struct mount *mnt, *p;
struct file *file;
@@ -4163,7 +4164,8 @@ static void free_mnt_ns(struct mnt_namespace *ns)
*/
static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
-static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool anon)
+static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns,
+ bool anon, struct ve_struct *new_ve)
{
struct mnt_namespace *new_ns;
struct ucounts *ucounts;
@@ -4203,14 +4205,15 @@ static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns, bool a
new_ns->user_ns = get_user_ns(user_ns);
new_ns->ucounts = ucounts;
#ifdef CONFIG_VE
- new_ns->ve_owner = get_ve(get_exec_env());
+ new_ns->ve_owner = get_ve(new_ve ?: get_exec_env());
#endif
return new_ns;
}
__latent_entropy
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
- struct user_namespace *user_ns, struct fs_struct *new_fs)
+ struct user_namespace *user_ns, struct fs_struct *new_fs,
+ struct ve_struct *new_ve)
{
struct mnt_namespace *new_ns;
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
@@ -4228,7 +4231,7 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
old = ns->root;
- new_ns = alloc_mnt_ns(user_ns, false);
+ new_ns = alloc_mnt_ns(user_ns, false, new_ve);
if (IS_ERR(new_ns))
return new_ns;
@@ -4304,7 +4307,7 @@ struct dentry *mount_subtree(struct vfsmount *m, const char *name)
struct path path;
int err;
- ns = alloc_mnt_ns(&init_user_ns, true);
+ ns = alloc_mnt_ns(&init_user_ns, true, NULL);
if (IS_ERR(ns)) {
mntput(m);
return ERR_CAST(ns);
@@ -4483,7 +4486,7 @@ SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags,
*/
vfs_clean_context(fc);
- ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true);
+ ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true, NULL);
if (IS_ERR(ns)) {
ret = PTR_ERR(ns);
goto err_path;
@@ -5794,7 +5797,7 @@ static void __init init_mount_tree(void)
if (IS_ERR(mnt))
panic("Can't create rootfs");
- ns = alloc_mnt_ns(&init_user_ns, false);
+ ns = alloc_mnt_ns(&init_user_ns, false, NULL);
if (IS_ERR(ns))
panic("Can't allocate initial namespace");
m = real_mount(mnt);
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
index 70b366b648160..61a26bc998136 100644
--- a/include/linux/mnt_namespace.h
+++ b/include/linux/mnt_namespace.h
@@ -10,9 +10,11 @@ struct mnt_namespace;
struct fs_struct;
struct user_namespace;
struct ns_common;
+struct ve_struct;
extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
- struct user_namespace *, struct fs_struct *);
+ struct user_namespace *, struct fs_struct *,
+ struct ve_struct *);
extern void put_mnt_ns(struct mnt_namespace *ns);
DEFINE_FREE(put_mnt_ns, struct mnt_namespace *, if (!IS_ERR_OR_NULL(_T)) put_mnt_ns(_T))
extern struct ns_common *from_mnt_ns(struct mnt_namespace *);
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 2827d4f277ad6..12de23ec80bf5 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -76,7 +76,8 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
if (!new_nsp)
return ERR_PTR(-ENOMEM);
- new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns, new_fs);
+ new_nsp->mnt_ns = copy_mnt_ns(flags, tsk->nsproxy->mnt_ns, user_ns,
+ new_fs, NULL);
if (IS_ERR(new_nsp->mnt_ns)) {
err = PTR_ERR(new_nsp->mnt_ns);
goto out_ns;
More information about the Devel
mailing list