* [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs
@ 2026-10-26 14:52 Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state Vladimir Riabchun
` (3 more replies)
0 siblings, 4 replies; 10+ messages in thread
From: Vladimir Riabchun @ 2026-10-26 14:52 UTC (permalink / raw)
To: Konstantin Khorenko, ptikhomirov, vasileios.almpanis; +Cc: devel
When testing CRIU with containers with docker images,
we found an easily reproducible kernel crash:
[ 2300.870181] BUG: kernel NULL pointer dereference, address: 0000000000000005
[ 2300.871537] #PF: supervisor read access in kernel mode
[ 2300.872577] #PF: error_code(0x0000) - not-present page
[ 2300.873595] PGD 800000010a18f067 P4D 800000010a18f067 PUD 10730c067 PMD 0
[ 2300.874900] Oops: Oops: 0000 [#1] SMP PTI
[ 2300.875742] CPU: 1 UID: 0 PID: 5419 Comm: criu ve: 0 Kdump: loaded Not tainted 6.12.0-211.39.1.16.6.vz10.x86_64 #1 16.6 PREEMPT(voluntary)
[ 2300.878057] Hardware name: Acronis OpenStack Compute/Virtuozzo, BIOS 1.16.1-1.vz9.2 04/01/2014
[ 2300.879684] RIP: 0010:ovl_get_index_name_fh+0x18/0x70 [overlay]
[ 2300.880859] Code: ...
[ 2300.884128] RSP: 0018:ffffd32582e0f7c0 EFLAGS: 00010246
[ 2300.895807] Call Trace:
[ 2300.896397] <TASK>
[ 2300.904435] ovl_create_index+0x82/0x260 [overlay]
[ 2300.905406] ovl_copy_up_workdir+0x30a/0x330 [overlay]
[ 2300.906427] ovl_do_copy_up+0x10d/0x2e0 [overlay]
[ 2300.907387] ovl_copy_up_one+0x456/0x720 [overlay]
[ 2300.910736] ovl_copy_up_flags+0x81/0x110 [overlay]
[ 2300.911740] ovl_create_object+0x51/0x130 [overlay]
[ 2300.912719] vfs_mkdir+0x194/0x250
[ 2300.913470] do_mkdirat+0x14c/0x180
[ 2300.914223] __x64_sys_mkdir+0x46/0x70
[ 2300.915019] do_syscall_64+0x7d/0x160
[ 2300.922814] entry_SYSCALL_64_after_hwframe+0x76/0x7e
The cause is that overlayfs does some feature checks on superblock
creation using capable/ve_capable and it stores current credentials.
But ve_capable check result depends on both current creds and
current VE. Later, overlayfs switches credentials only, leading
to unexpected feature disappearance.
This was reworked completely after v2, the approach is different now.
1. Patch 1 removes separate STOPPED state - VE goes directly to DEAD.
2. Patch 2 adds helpers for overriding tasks VE.
3. Patch 3 adds VE owner to overlay superblock.
4. Patch 4 fixes the bug by switching VE together with creds in overlayfs.
v2: http://lore.virtuozzo.com/all/20260920-ovl-use-orig-ve-v1-0-b3e63f6ff80d@virtuozzo.com/
https://virtuozzo.atlassian.net/browse/VSTOR-143259 - non-CRIU repro
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
Changes in v2:
- Patch 1: updated description as Sashiko suggested..
- Patch 2: moved stubs outside CONFIG_X86 section,
added task_lock protection in helpers. Verified via lockdep,
no splats after vzctl tests, C/R and crash reproducer.
- Patch 4: fixed typo in comment.
- Link to v1: http://lore.virtuozzo.com/20261022-ovl-use-orig-ve-v1-0-da84c00c8065@virtuozzo.com
---
Vladimir Riabchun (4):
ve: Remove STOPPED state
ve: Introduce override_ve/revert_ve helpers
ovl: Keep creator VE in overlayfs superblock
ve/overlayfs: Override VE together with creds
fs/overlayfs/copy_up.c | 4 ++--
fs/overlayfs/dir.c | 28 ++++++++++++++--------------
fs/overlayfs/file.c | 28 ++++++++++++++--------------
fs/overlayfs/inode.c | 38 +++++++++++++++++++-------------------
fs/overlayfs/namei.c | 18 +++++++++---------
fs/overlayfs/overlayfs.h | 9 ++++++++-
fs/overlayfs/ovl_entry.h | 1 +
fs/overlayfs/params.c | 3 +++
fs/overlayfs/readdir.c | 16 ++++++++--------
fs/overlayfs/super.c | 3 +++
fs/overlayfs/util.c | 22 ++++++++++++++++------
fs/overlayfs/xattrs.c | 14 +++++++-------
include/linux/ve.h | 27 +++++++++++++++++++++++++--
kernel/ve/ve.c | 6 ++----
14 files changed, 131 insertions(+), 86 deletions(-)
---
base-commit: f5d1fdcfffe101fe2bafa7df09d95d2e811c5c5e
change-id: 20260918-ovl-use-orig-ve-2dc365976202
Best regards,
--
Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state
2026-10-26 14:52 [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs Vladimir Riabchun
@ 2026-10-26 14:52 ` Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers Vladimir Riabchun
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Vladimir Riabchun @ 2026-10-26 14:52 UTC (permalink / raw)
To: Konstantin Khorenko, ptikhomirov, vasileios.almpanis; +Cc: devel
We don't need extra STOPPED state. It makes us feel that CT
could be started again from it, but it is actually not true.
Separate STOPPED stated doesn't bring any value. Drop it and
transition VE to DEAD state directly on the stop.
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
include/linux/ve.h | 3 +--
kernel/ve/ve.c | 6 ++----
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index a400d21593b2..717567775f58 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -40,8 +40,7 @@ struct vfsmount;
#define VE_STATE_STARTING 0
#define VE_STATE_RUNNING 1
#define VE_STATE_STOPPING 2
-#define VE_STATE_STOPPED 3
-#define VE_STATE_DEAD 4
+#define VE_STATE_DEAD 3
#define VE_IS_RUNNING(ve) ((ve)->state == VE_STATE_RUNNING)
diff --git a/kernel/ve/ve.c b/kernel/ve/ve.c
index 579628a8a310..d74f74ca958a 100644
--- a/kernel/ve/ve.c
+++ b/kernel/ve/ve.c
@@ -604,7 +604,7 @@ static int ve_start_container(struct ve_struct *ve)
err_mark_ve:
ve_list_del(ve);
err_list:
- ve_set_state(ve, VE_STATE_STOPPED);
+ ve_set_state(ve, VE_STATE_DEAD);
ve_drop_context(ve);
return err;
}
@@ -680,7 +680,7 @@ void ve_exit_ns(struct pid_namespace *pid_ns)
ve_hook_iterate_fini(VE_SS_CHAIN, ve);
ve_list_del(ve);
ve_drop_context(ve);
- ve_set_state(ve, VE_STATE_STOPPED);
+ ve_set_state(ve, VE_STATE_DEAD);
up_write(&ve->op_sem);
printk(KERN_INFO "CT: %s: stopped\n", ve_name(ve));
@@ -935,7 +935,6 @@ static void ve_destroy(struct cgroup_subsys_state *css)
ve_free_vdso(ve);
mntput(ve->devtmpfs_mnt);
free_percpu(ve->sched_lat_ve.cur);
- ve_set_state(ve, VE_STATE_DEAD);
kmem_cache_free(ve_cachep, ve);
}
@@ -955,7 +954,6 @@ static int ve_state_show(struct seq_file *sf, void *v)
case VE_STATE_STOPPING:
seq_puts(sf, "STOPPING");
break;
- case VE_STATE_STOPPED:
case VE_STATE_DEAD:
seq_puts(sf, "STOPPED");
break;
--
2.52.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers
2026-10-26 14:52 [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state Vladimir Riabchun
@ 2026-10-26 14:52 ` Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 3/4] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds Vladimir Riabchun
3 siblings, 1 reply; 10+ messages in thread
From: Vladimir Riabchun @ 2026-10-26 14:52 UTC (permalink / raw)
To: Konstantin Khorenko, ptikhomirov, vasileios.almpanis; +Cc: devel
These helpers mirror override_creds/revert_creds and are supposed to
be used together in cases when VE creds may be stored and later used
by the different VE task.
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
include/linux/ve.h | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/include/linux/ve.h b/include/linux/ve.h
index 717567775f58..9d633717a7fe 100644
--- a/include/linux/ve.h
+++ b/include/linux/ve.h
@@ -295,6 +295,23 @@ static inline struct vdso_image *ve_get_vdso_64(struct ve_struct *ve)
return ve->vdso_64;
}
+static inline struct ve_struct *override_ve(struct ve_struct *new_ve)
+{
+ struct ve_struct *old_ve;
+
+ guard(task_lock)(current);
+ old_ve = current->task_ve;
+
+ current->task_ve = new_ve;
+ return old_ve;
+}
+
+static inline void revert_ve(struct ve_struct *orig_ve)
+{
+ guard(task_lock)(current);
+ current->task_ve = orig_ve;
+}
+
#else /* CONFIG_VE */
#include <linux/init_task.h>
#include <net/net_namespace.h>
@@ -405,6 +422,13 @@ static inline struct vdso_image *ve_get_vdso_64(struct ve_struct *ve)
}
#endif /* CONFIG_X86 */
+static inline struct ve_struct *override_ve(struct ve_struct *new_ve)
+{
+ return NULL;
+}
+
+static inline void revert_ve(struct ve_struct *orig_ve) {}
+
#endif /* CONFIG_VE */
struct seq_file;
--
2.52.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Devel] [PATCH hci-8.1 v2 3/4] ovl: Keep creator VE in overlayfs superblock
2026-10-26 14:52 [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers Vladimir Riabchun
@ 2026-10-26 14:52 ` Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds Vladimir Riabchun
3 siblings, 0 replies; 10+ messages in thread
From: Vladimir Riabchun @ 2026-10-26 14:52 UTC (permalink / raw)
To: Konstantin Khorenko, ptikhomirov, vasileios.almpanis; +Cc: devel
Overlayfs superblock stores creator creds in it, but these creds
should go together with the VE they were taken from.
Store the VE in the superblock. We can't use mount->owner_ve,
because several mounts can reference one superblock.
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
fs/overlayfs/ovl_entry.h | 1 +
fs/overlayfs/params.c | 3 +++
fs/overlayfs/super.c | 3 +++
3 files changed, 7 insertions(+)
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
index ab78001635e7..a6ad8b8de48c 100644
--- a/fs/overlayfs/ovl_entry.h
+++ b/fs/overlayfs/ovl_entry.h
@@ -77,6 +77,7 @@ struct ovl_fs {
struct ovl_config config;
/* creds of process who forced instantiation of super block */
const struct cred *creator_cred;
+ struct ve_struct *creator_ve;
bool tmpfile;
bool noxattr;
bool nofh;
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index 32c58fcfe52f..c047f1265b8e 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -8,6 +8,7 @@
#include <linux/posix_acl_xattr.h>
#include <linux/seq_file.h>
#include <linux/xattr.h>
+#include <linux/ve.h>
#include "overlayfs.h"
#include "params.h"
@@ -761,6 +762,8 @@ void ovl_free_fs(struct ovl_fs *ofs)
kfree(ofs->config.workdir);
if (ofs->creator_cred)
put_cred(ofs->creator_cred);
+ if (ofs->creator_ve)
+ put_ve(ofs->creator_ve);
kfree(ofs);
}
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index fb47711c8fb9..8ab39a35a873 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -18,6 +18,7 @@
#include <linux/file.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
+#include <linux/ve.h>
#include "overlayfs.h"
#include "params.h"
@@ -1328,6 +1329,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc)
sb->s_d_op = &ovl_dentry_operations;
+ ofs->creator_ve = get_ve(get_exec_env());
+
err = -ENOMEM;
ofs->creator_cred = cred = prepare_creds();
if (!cred)
--
2.52.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds
2026-10-26 14:52 [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs Vladimir Riabchun
` (2 preceding siblings ...)
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 3/4] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun
@ 2026-10-26 14:52 ` Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
3 siblings, 1 reply; 10+ messages in thread
From: Vladimir Riabchun @ 2026-10-26 14:52 UTC (permalink / raw)
To: Konstantin Khorenko, ptikhomirov, vasileios.almpanis; +Cc: devel
Host users might want to access overlayfs, owned by CT. In such case
ve_capable checks will fail - they will perform VE cred checks against
host user_ns.
Modify ovl_override_creds so that it returns both old cred and old VE.
This will also protect us from future rebase issues - all new users of
old_cred and ovl_override_creds will fail with compilation error.
This fixes an observed crash in overlayfs when checkpointing
a container with a docker running inside:
[ 2300.870181] BUG: kernel NULL pointer dereference, address: 0000000000000005
[ 2300.871537] #PF: supervisor read access in kernel mode
[ 2300.872577] #PF: error_code(0x0000) - not-present page
[ 2300.873595] PGD 800000010a18f067 P4D 800000010a18f067 PUD 10730c067 PMD 0
[ 2300.874900] Oops: Oops: 0000 [#1] SMP PTI
[ 2300.875742] CPU: 1 UID: 0 PID: 5419 Comm: criu ve: 0 Kdump: loaded Not tainted 6.12.0-211.39.1.16.6.vz10.x86_64 #1 16.6 PREEMPT(voluntary)
[ 2300.878057] Hardware name: Acronis OpenStack Compute/Virtuozzo, BIOS 1.16.1-1.vz9.2 04/01/2014
[ 2300.879684] RIP: 0010:ovl_get_index_name_fh+0x18/0x70 [overlay]
[ 2300.880859] Code: ...
[ 2300.884128] RSP: 0018:ffffd32582e0f7c0 EFLAGS: 00010246
[ 2300.885153] RAX: ...
[ 2300.895807] Call Trace:
[ 2300.896397]
[ 2300.903298] ? ovl_get_index_name_fh+0x18/0x70 [overlay]
[ 2300.904435] ovl_create_index+0x82/0x260 [overlay]
[ 2300.905406] ovl_copy_up_workdir+0x30a/0x330 [overlay]
[ 2300.906427] ovl_do_copy_up+0x10d/0x2e0 [overlay]
[ 2300.907387] ovl_copy_up_one+0x456/0x720 [overlay]
[ 2300.910736] ovl_copy_up_flags+0x81/0x110 [overlay]
[ 2300.911740] ovl_create_object+0x51/0x130 [overlay]
[ 2300.912719] vfs_mkdir+0x194/0x250
[ 2300.913470] do_mkdirat+0x14c/0x180
[ 2300.914223] __x64_sys_mkdir+0x46/0x70
[ 2300.915019] do_syscall_64+0x7d/0x160
[ 2300.922814] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2300.923823] RIP: 0033:0x7f5a2f8de3eb
Overlayfs was created inside CT, so ovl_fill_super stored
in-container credentials to ofs->creator_cred.
Those creds were later used in ovl_override_creds to temporarily
swich creds and perform checks using them.
One of these checks happened in ovl_get_origin_fh/ovl_can_decode_fh.
On fs creation this check passed because we had container VE
and container creds, so overlayfs thought that we support
file handles (ovl_can_decode_fh call in ovl_make_workdir).
When we tried to do something with the overlayfs from host VE,
ve_capable check was performed with credentials from CT
and with host VE.
ovl_get_origin_fh returned unexpected NULL and all exploded.
Stroing ve_capable check result on creation is a possible fix,
but it just masks the problem. We have other paths from overlay to
ve_capable checks:
ovl_fileattr_set
ovl_override_creds
ovl_real_fileattr_set
vfs_fileattr_set
fileattr_set_prepare
ve_capable
And storing capability check result will make rebase dangerous - we
would have to track new added code - not funny.
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
---
fs/overlayfs/copy_up.c | 4 ++--
fs/overlayfs/dir.c | 28 ++++++++++++++--------------
fs/overlayfs/file.c | 28 ++++++++++++++--------------
fs/overlayfs/inode.c | 38 +++++++++++++++++++-------------------
fs/overlayfs/namei.c | 18 +++++++++---------
fs/overlayfs/overlayfs.h | 9 ++++++++-
fs/overlayfs/readdir.c | 16 ++++++++--------
fs/overlayfs/util.c | 22 ++++++++++++++++------
fs/overlayfs/xattrs.c | 14 +++++++-------
9 files changed, 97 insertions(+), 80 deletions(-)
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 4388004a319d..0b1270bc6d8d 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -1215,7 +1215,7 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
static int ovl_copy_up_flags(struct dentry *dentry, int flags)
{
int err = 0;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED);
/*
@@ -1260,7 +1260,7 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags)
dput(parent);
dput(next);
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index 1c8009bf194b..fe91fd4b04f0 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -554,7 +554,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
}
static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
- umode_t mode, const struct cred *old_cred)
+ umode_t mode, const struct ovl_creds_desc *old_cred)
{
int err;
struct cred *override_cred;
@@ -566,7 +566,7 @@ static int ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode,
override_cred->fsuid = inode->i_uid;
override_cred->fsgid = inode->i_gid;
err = security_dentry_create_files_as(dentry, mode, &dentry->d_name,
- old_cred, override_cred);
+ old_cred->cred, override_cred);
if (err) {
put_cred(override_cred);
return err;
@@ -581,7 +581,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
struct ovl_cattr *attr, bool origin)
{
int err;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct dentry *parent = dentry->d_parent;
old_cred = ovl_override_creds(dentry->d_sb);
@@ -610,7 +610,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
* create a new inode, so just use the ovl mounter's
* fs{u,g}id.
*/
- err = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred);
+ err = ovl_setup_cred_for_create(dentry, inode, attr->mode, &old_cred);
if (err)
goto out_revert_creds;
}
@@ -621,7 +621,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
err = ovl_create_over_whiteout(dentry, inode, attr);
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -697,12 +697,12 @@ static int ovl_symlink(struct mnt_idmap *idmap, struct inode *dir,
static int ovl_set_link_redirect(struct dentry *dentry)
{
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_set_redirect(dentry, false);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -888,7 +888,7 @@ static void ovl_drop_nlink(struct dentry *dentry)
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{
int err;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
bool lower_positive = ovl_lower_positive(dentry);
LIST_HEAD(list);
@@ -912,7 +912,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
err = ovl_remove_upper(dentry, is_dir, &list);
else
err = ovl_remove_and_whiteout(dentry, &list);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (!err) {
if (is_dir)
clear_nlink(dentry->d_inode);
@@ -1095,7 +1095,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
bool new_is_dir = d_is_dir(new);
bool samedir = olddir == newdir;
struct dentry *opaquedir = NULL;
- const struct cred *old_cred = NULL;
+ struct ovl_creds_desc old_cred = {};
struct ovl_fs *ofs = OVL_FS(old->d_sb);
LIST_HEAD(list);
@@ -1292,7 +1292,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
out_unlock:
unlock_rename(new_upperdir, old_upperdir);
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (update_nlink)
ovl_nlink_end(new);
else
@@ -1306,7 +1306,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir,
static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
struct inode *inode, umode_t mode)
{
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct path realparentpath;
struct file *realfile;
struct dentry *newdentry;
@@ -1315,7 +1315,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
int err;
old_cred = ovl_override_creds(dentry->d_sb);
- err = ovl_setup_cred_for_create(dentry, inode, mode, old_cred);
+ err = ovl_setup_cred_for_create(dentry, inode, mode, &old_cred);
if (err)
goto out_revert_creds;
@@ -1337,7 +1337,7 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry,
fput(realfile);
}
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c
index 84aad9d19bdd..55b4589e895a 100644
--- a/fs/overlayfs/file.c
+++ b/fs/overlayfs/file.c
@@ -31,7 +31,7 @@ static struct file *ovl_open_realfile(const struct file *file,
struct inode *inode = file_inode(file);
struct mnt_idmap *real_idmap;
struct file *realfile;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int flags = file->f_flags | OVL_OPEN_FLAGS;
int acc_mode = ACC_MODE(flags);
int err;
@@ -51,7 +51,7 @@ static struct file *ovl_open_realfile(const struct file *file,
realfile = backing_file_open(file, flags, realpath,
current_cred());
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
file, file, ovl_whatisit(inode, realinode), file->f_flags,
@@ -184,7 +184,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file_inode(file);
struct fd real;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
loff_t ret;
/*
@@ -215,7 +215,7 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
old_cred = ovl_override_creds(inode->i_sb);
ret = vfs_llseek(fd_file(real), offset, whence);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
file->f_pos = fd_file(real)->f_pos;
ovl_inode_unlock(inode);
@@ -392,7 +392,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct fd real;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int ret;
ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb));
@@ -407,7 +407,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
if (file_inode(fd_file(real)) == ovl_inode_upper(file_inode(file))) {
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_fsync_range(fd_file(real), start, end, datasync);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
}
fdput(real);
@@ -431,7 +431,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
{
struct inode *inode = file_inode(file);
struct fd real;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int ret;
inode_lock(inode);
@@ -447,7 +447,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_fallocate(fd_file(real), mode, offset, len);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
/* Update size */
ovl_file_modified(file);
@@ -463,7 +463,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
{
struct fd real;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int ret;
ret = ovl_real_fdget(file, &real);
@@ -472,7 +472,7 @@ static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
old_cred = ovl_override_creds(file_inode(file)->i_sb);
ret = vfs_fadvise(fd_file(real), offset, len, advice);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
fdput(real);
@@ -491,7 +491,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
{
struct inode *inode_out = file_inode(file_out);
struct fd real_in, real_out;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
loff_t ret;
inode_lock(inode_out);
@@ -531,7 +531,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
flags);
break;
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
/* Update size */
ovl_file_modified(file_out);
@@ -583,7 +583,7 @@ static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in,
static int ovl_flush(struct file *file, fl_owner_t id)
{
struct fd real;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
err = ovl_real_fdget(file, &real);
@@ -593,7 +593,7 @@ static int ovl_flush(struct file *file, fl_owner_t id)
if (fd_file(real)->f_op->flush) {
old_cred = ovl_override_creds(file_inode(file)->i_sb);
err = fd_file(real)->f_op->flush(fd_file(real), id);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
}
fdput(real);
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 8b31f44c12cd..6a8d35e8c14a 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -25,7 +25,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
bool full_copy_up = false;
struct dentry *upperdentry;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
err = setattr_prepare(&nop_mnt_idmap, dentry, attr);
if (err)
@@ -80,7 +80,7 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
inode_lock(upperdentry->d_inode);
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_do_notify_change(ofs, upperdentry, attr);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (!err)
ovl_copyattr(dentry->d_inode);
inode_unlock(upperdentry->d_inode);
@@ -159,7 +159,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
struct dentry *dentry = path->dentry;
enum ovl_path_type type;
struct path realpath;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct inode *inode = d_inode(dentry);
bool is_dir = S_ISDIR(inode->i_mode);
int fsid = 0;
@@ -280,7 +280,7 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->nlink = dentry->d_inode->i_nlink;
out:
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -291,7 +291,7 @@ int ovl_permission(struct mnt_idmap *idmap,
struct inode *upperinode = ovl_inode_upper(inode);
struct inode *realinode;
struct path realpath;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
/* Careful in RCU walk mode */
@@ -317,7 +317,7 @@ int ovl_permission(struct mnt_idmap *idmap,
mask |= MAY_READ;
}
err = inode_permission(mnt_idmap(realpath.mnt), realinode, mask);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -326,7 +326,7 @@ static const char *ovl_get_link(struct dentry *dentry,
struct inode *inode,
struct delayed_call *done)
{
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
const char *p;
if (!dentry)
@@ -334,7 +334,7 @@ static const char *ovl_get_link(struct dentry *dentry,
old_cred = ovl_override_creds(dentry->d_sb);
p = vfs_get_link(ovl_dentry_real(dentry), done);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return p;
}
@@ -465,11 +465,11 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap,
acl = get_cached_acl_rcu(realinode, type);
} else {
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(inode->i_sb);
acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
}
return acl;
@@ -481,7 +481,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
int err;
struct path realpath;
const char *acl_name;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdentry = ovl_dentry_upper(dentry);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
@@ -498,7 +498,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
old_cred = ovl_override_creds(dentry->d_sb);
real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry,
acl_name);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (IS_ERR(real_acl)) {
err = PTR_ERR(real_acl);
goto out;
@@ -523,7 +523,7 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode,
err = ovl_do_set_acl(ofs, realdentry, acl_name, acl);
else
err = ovl_do_remove_acl(ofs, realdentry, acl_name);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
@@ -590,7 +590,7 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
{
int err;
struct inode *realinode = ovl_inode_realdata(inode);
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
if (!realinode)
return -EIO;
@@ -600,7 +600,7 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
old_cred = ovl_override_creds(inode->i_sb);
err = realinode->i_op->fiemap(realinode, fieinfo, start, len);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -648,7 +648,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
{
struct inode *inode = d_inode(dentry);
struct path upperpath;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
unsigned int flags;
int err;
@@ -671,7 +671,7 @@ int ovl_fileattr_set(struct mnt_idmap *idmap,
err = ovl_set_protattr(inode, upperpath.dentry, fa);
if (!err)
err = ovl_real_fileattr_set(&upperpath, fa);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
ovl_drop_write(dentry);
/*
@@ -725,7 +725,7 @@ int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
{
struct inode *inode = d_inode(dentry);
struct path realpath;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
ovl_path_real(dentry, &realpath);
@@ -733,7 +733,7 @@ int ovl_fileattr_get(struct dentry *dentry, struct fileattr *fa)
old_cred = ovl_override_creds(inode->i_sb);
err = ovl_real_fileattr_get(&realpath, fa);
ovl_fileattr_prot_flags(inode, fa);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index efa2a072aa69..6af0712fcb83 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -969,7 +969,7 @@ static int ovl_maybe_validate_verity(struct dentry *dentry)
return err;
if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) {
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(dentry->d_sb);
@@ -977,7 +977,7 @@ static int ovl_maybe_validate_verity(struct dentry *dentry)
if (err == 0)
ovl_set_flag(OVL_VERIFIED_DIGEST, inode);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
}
ovl_inode_unlock(inode);
@@ -991,7 +991,7 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
struct inode *inode = d_inode(dentry);
const char *redirect = ovl_lowerdata_redirect(inode);
struct ovl_path datapath = {};
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
if (!redirect || ovl_dentry_lowerdata(dentry))
@@ -1011,7 +1011,7 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry)
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_lookup_data_layers(dentry, redirect, &datapath);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (err)
goto out_err;
@@ -1071,7 +1071,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct ovl_entry *oe = NULL;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct ovl_entry *poe = OVL_E(dentry->d_parent);
struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root);
@@ -1378,7 +1378,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode));
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (origin_path) {
dput(origin_path->dentry);
kfree(origin_path);
@@ -1402,7 +1402,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
kfree(d.upperredirect);
out:
kfree(d.redirect);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return ERR_PTR(err);
}
@@ -1410,7 +1410,7 @@ bool ovl_lower_positive(struct dentry *dentry)
{
struct ovl_entry *poe = OVL_E(dentry->d_parent);
const struct qstr *name = &dentry->d_name;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
unsigned int i;
bool positive = false;
bool done = false;
@@ -1459,7 +1459,7 @@ bool ovl_lower_positive(struct dentry *dentry)
dput(this);
}
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return positive;
}
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 8577c7884fb9..eb2fb473a64b 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -164,6 +164,12 @@ struct ovl_metacopy {
u8 digest[FS_VERITY_MAX_DIGEST_SIZE]; /* Only the used part on disk */
} __packed;
+/* Credential information used by overlay */
+struct ovl_creds_desc {
+ const struct cred *cred;
+ struct ve_struct *ve;
+};
+
#define OVL_METACOPY_MAX_SIZE (sizeof(struct ovl_metacopy))
#define OVL_METACOPY_MIN_SIZE (OVL_METACOPY_MAX_SIZE - FS_VERITY_MAX_DIGEST_SIZE)
#define OVL_METACOPY_INIT { 0, OVL_METACOPY_MIN_SIZE }
@@ -420,7 +426,8 @@ void ovl_end_write(struct dentry *dentry);
int ovl_want_write(struct dentry *dentry);
void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry);
-const struct cred *ovl_override_creds(struct super_block *sb);
+struct ovl_creds_desc ovl_override_creds(struct super_block *sb);
+void ovl_revert_creds(struct ovl_creds_desc *old_creds);
static inline const struct cred *ovl_creds(struct super_block *sb)
{
diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
index 0ca8af060b0c..aca2dd6d4e2f 100644
--- a/fs/overlayfs/readdir.c
+++ b/fs/overlayfs/readdir.c
@@ -273,7 +273,7 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
int err;
struct ovl_cache_entry *p;
struct dentry *dentry, *dir = path->dentry;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(rdd->dentry->d_sb);
@@ -290,7 +290,7 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
}
inode_unlock(dir->d_inode);
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -753,7 +753,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
struct dentry *dentry = file->f_path.dentry;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct ovl_cache_entry *p;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
old_cred = ovl_override_creds(dentry->d_sb);
@@ -808,7 +808,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
}
err = 0;
out:
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return err;
}
@@ -856,11 +856,11 @@ static struct file *ovl_dir_open_realfile(const struct file *file,
const struct path *realpath)
{
struct file *res;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(file_inode(file)->i_sb);
res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return res;
}
@@ -983,11 +983,11 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
int err;
struct ovl_cache_entry *p, *n;
struct rb_root root = RB_ROOT;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(dentry->d_sb);
err = ovl_dir_read_merged(dentry, list, &root);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (err)
return err;
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index f95090f0d29c..e3cbdf7259d1 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -63,11 +63,21 @@ struct dentry *ovl_workdir(struct dentry *dentry)
return ofs->workdir;
}
-const struct cred *ovl_override_creds(struct super_block *sb)
+struct ovl_creds_desc ovl_override_creds(struct super_block *sb)
{
struct ovl_fs *ofs = OVL_FS(sb);
+ struct ovl_creds_desc desc;
- return override_creds(ofs->creator_cred);
+ desc.cred = override_creds(ofs->creator_cred);
+ desc.ve = override_ve(ofs->creator_ve);
+
+ return desc;
+}
+
+void ovl_revert_creds(struct ovl_creds_desc *old_creds)
+{
+ revert_ve(old_creds->ve);
+ revert_creds(old_creds->cred);
}
/*
@@ -1150,7 +1160,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
int ovl_nlink_start(struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
int err;
if (WARN_ON(!inode))
@@ -1195,7 +1205,7 @@ int ovl_nlink_start(struct dentry *dentry)
* value relative to the upper inode nlink in an upper inode xattr.
*/
err = ovl_set_nlink_upper(dentry);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (err)
goto out_drop_write;
@@ -1216,11 +1226,11 @@ void ovl_nlink_end(struct dentry *dentry)
ovl_drop_write(dentry);
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
old_cred = ovl_override_creds(dentry->d_sb);
ovl_cleanup_index(dentry);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
}
ovl_inode_unlock(inode);
diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c
index 0ed2b049056d..0bd5835061fb 100644
--- a/fs/overlayfs/xattrs.c
+++ b/fs/overlayfs/xattrs.c
@@ -41,13 +41,13 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
struct dentry *upperdentry = ovl_i_dentry_upper(inode);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
struct path realpath;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
if (!value && !upperdentry) {
ovl_path_lower(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (err < 0)
goto out;
}
@@ -72,7 +72,7 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char
WARN_ON(flags != XATTR_REPLACE);
err = ovl_do_removexattr(ofs, realdentry, name);
}
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
ovl_drop_write(dentry);
/* copy c/mtime */
@@ -85,13 +85,13 @@ static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char
void *value, size_t size)
{
ssize_t res;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
struct path realpath;
ovl_i_path_real(inode, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
return res;
}
@@ -116,12 +116,12 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
ssize_t res;
size_t len;
char *s;
- const struct cred *old_cred;
+ struct ovl_creds_desc old_cred;
size_t prefix_len, name_len;
old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_listxattr(realdentry, list, size);
- revert_creds(old_cred);
+ ovl_revert_creds(&old_cred);
if (res <= 0 || size == 0)
return res;
--
2.52.0
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state Vladimir Riabchun
@ 2026-10-28 11:58 ` Pavel Tikhomirov
0 siblings, 0 replies; 10+ messages in thread
From: Pavel Tikhomirov @ 2026-10-28 11:58 UTC (permalink / raw)
To: Vladimir Riabchun; +Cc: devel
On Mon, 26 Oct 2026 15:52:20 +0100, Vladimir Riabchun <vladimir.riabchun@virtuozzo.com> wrote:
> We don't need extra STOPPED state. It makes us feel that CT
> could be started again from it, but it is actually not true.
>
> Separate STOPPED stated doesn't bring any value. Drop it and
> transition VE to DEAD state directly on the stop.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-142405
>
> [...]
Reviewed-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
--
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers Vladimir Riabchun
@ 2026-10-28 11:58 ` Pavel Tikhomirov
2026-11-02 17:29 ` Vladimir Riabchun
0 siblings, 1 reply; 10+ messages in thread
From: Pavel Tikhomirov @ 2026-10-28 11:58 UTC (permalink / raw)
To: Vladimir Riabchun; +Cc: devel
> These helpers mirror override_creds/revert_creds and are supposed to
> be used together in cases when VE creds may be stored and later used
> by the different VE task.
In multiple places like /proc/<pid>/status task will jump to other VE.
Also anything using get_task_ve will be tricked to believe in task VE
change. E.g.:
Small information leak, drivers/connector/cn_proc.c:209.
proc_event_connector() sends events about a task to that task's VE's
listeners.
>
> https://virtuozzo.atlassian.net/browse/VSTOR-142405
> Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
>
> diff --git a/include/linux/ve.h b/include/linux/ve.h
> index 717567775f58e..9d633717a7fe2 100644
> --- a/include/linux/ve.h
> +++ b/include/linux/ve.h
> @@ -295,6 +295,23 @@ static inline struct vdso_image *ve_get_vdso_64(struct ve_struct *ve)
> return ve->vdso_64;
> }
>
> +static inline struct ve_struct *override_ve(struct ve_struct *new_ve)
> +{
> + struct ve_struct *old_ve;
> +
> + guard(task_lock)(current);
This adds a lock to hot paths such as ovl_permission, ovl_getattr and
ovl_lookup; AI deems this worth stating, probably not that critical,
but the save check result approach has no such problem.
> + old_ve = current->task_ve;
> +
> + current->task_ve = new_ve;
> + return old_ve;
> +}
What about direction checks? ve0->veX ok and veX->ve0 is probably a
security breach and should not be allowed.
--
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds Vladimir Riabchun
@ 2026-10-28 11:58 ` Pavel Tikhomirov
2026-11-02 17:35 ` Vladimir Riabchun
0 siblings, 1 reply; 10+ messages in thread
From: Pavel Tikhomirov @ 2026-10-28 11:58 UTC (permalink / raw)
To: Vladimir Riabchun; +Cc: devel
> Host users might want to access overlayfs, owned by CT. In such case
> ve_capable checks will fail - they will perform VE cred checks against
> host user_ns.
>
> Modify ovl_override_creds so that it returns both old cred and old VE.
> This will also protect us from future rebase issues - all new users of
> old_cred and ovl_override_creds will fail with compilation error.
>
> This fixes an observed crash in overlayfs when checkpointing
> a container with a docker running inside:
What about dead creator VE? It will try to check ovl creator cred
against host userns and fail and reintroduce the crash.
AI slop:
//
ofs holds a reference on creator_ve, but after CT stop ve_drop_context()
clears ve->init_cred, and ve_capable() falls back to ve0.init_cred. With
CT creator_cred that means ns_capable(init_user_ns) -> false. So if the
superblock outlives the CT, e.g. CRIU (or any host process) has a file
open on the CT overlay while the CT dies, we again get
ovl_can_decode_fh() == true at mount time and false later, i.e. the
same NULL dereference in ovl_get_index_name_fh() this series fixes.
//
I think dieing in the middle of CRIU c/r maybe not so rare a case.
>
> [ 2300.870181] BUG: kernel NULL pointer dereference, address: 0000000000000005
> [ 2300.871537] #PF: supervisor read access in kernel mode
> [ 2300.872577] #PF: error_code(0x0000) - not-present page
> [ 2300.873595] PGD 800000010a18f067 P4D 800000010a18f067 PUD 10730c067 PMD 0
> [ 2300.874900] Oops: Oops: 0000 [#1] SMP PTI
> [ 2300.875742] CPU: 1 UID: 0 PID: 5419 Comm: criu ve: 0 Kdump: loaded Not tainted 6.12.0-211.39.1.16.6.vz10.x86_64 #1 16.6 PREEMPT(voluntary)
> [ 2300.878057] Hardware name: Acronis OpenStack Compute/Virtuozzo, BIOS 1.16.1-1.vz9.2 04/01/2014
> [ 2300.879684] RIP: 0010:ovl_get_index_name_fh+0x18/0x70 [overlay]
> [ 2300.880859] Code: ...
> [ 2300.884128] RSP: 0018:ffffd32582e0f7c0 EFLAGS: 00010246
> [ 2300.885153] RAX: ...
> [ 2300.895807] Call Trace:
> [ 2300.896397]
> [ 2300.903298] ? ovl_get_index_name_fh+0x18/0x70 [overlay]
> [ 2300.904435] ovl_create_index+0x82/0x260 [overlay]
> [ 2300.905406] ovl_copy_up_workdir+0x30a/0x330 [overlay]
> [ 2300.906427] ovl_do_copy_up+0x10d/0x2e0 [overlay]
> [ 2300.907387] ovl_copy_up_one+0x456/0x720 [overlay]
> [ 2300.910736] ovl_copy_up_flags+0x81/0x110 [overlay]
> [ 2300.911740] ovl_create_object+0x51/0x130 [overlay]
> [ 2300.912719] vfs_mkdir+0x194/0x250
> [ 2300.913470] do_mkdirat+0x14c/0x180
> [ 2300.914223] __x64_sys_mkdir+0x46/0x70
> [ 2300.915019] do_syscall_64+0x7d/0x160
> [ 2300.922814] entry_SYSCALL_64_after_hwframe+0x76/0x7e
> [ 2300.923823] RIP: 0033:0x7f5a2f8de3eb
>
> Overlayfs was created inside CT, so ovl_fill_super stored
> in-container credentials to ofs->creator_cred.
>
> Those creds were later used in ovl_override_creds to temporarily
> swich creds and perform checks using them.
>
> One of these checks happened in ovl_get_origin_fh/ovl_can_decode_fh.
> On fs creation this check passed because we had container VE
> and container creds, so overlayfs thought that we support
> file handles (ovl_can_decode_fh call in ovl_make_workdir).
>
> When we tried to do something with the overlayfs from host VE,
> ve_capable check was performed with credentials from CT
> and with host VE.
>
> ovl_get_origin_fh returned unexpected NULL and all exploded.
>
> Stroing ve_capable check result on creation is a possible fix,
> but it just masks the problem. We have other paths from overlay to
> ve_capable checks:
>
> ovl_fileattr_set
> ovl_override_creds
> ovl_real_fileattr_set
> vfs_fileattr_set
> fileattr_set_prepare
> ve_capable
>
> And storing capability check result will make rebase dangerous - we
> would have to track new added code - not funny.
All uses of ovl_creds() are not handled, they save creator_cred to
backing file:
struct backing_file_ctx {
const struct cred *cred;
...
}
--
Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers
2026-10-28 11:58 ` Pavel Tikhomirov
@ 2026-11-02 17:29 ` Vladimir Riabchun
0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Riabchun @ 2026-11-02 17:29 UTC (permalink / raw)
To: Pavel Tikhomirov; +Cc: devel
On 10/28/26 12:58, Pavel Tikhomirov wrote:
>> These helpers mirror override_creds/revert_creds and are supposed to
>> be used together in cases when VE creds may be stored and later used
>> by the different VE task.
>
> In multiple places like /proc/<pid>/status task will jump to other VE.
>
> Also anything using get_task_ve will be tricked to believe in task VE
> change. E.g.:
>
> Small information leak, drivers/connector/cn_proc.c:209.
> proc_event_connector() sends events about a task to that task's VE's
> listeners.
>
Agree. I'll instead introduce a separate field in task struct,
which will contain ve override, and will be used by ve_capable only.
>>
>> https://virtuozzo.atlassian.net/browse/VSTOR-142405
>> Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com>
>>
>> diff --git a/include/linux/ve.h b/include/linux/ve.h
>> index 717567775f58e..9d633717a7fe2 100644
>> --- a/include/linux/ve.h
>> +++ b/include/linux/ve.h
>> @@ -295,6 +295,23 @@ static inline struct vdso_image *ve_get_vdso_64(struct ve_struct *ve)
>> return ve->vdso_64;
>> }
>>
>> +static inline struct ve_struct *override_ve(struct ve_struct *new_ve)
>> +{
>> + struct ve_struct *old_ve;
>> +
>> + guard(task_lock)(current);
>
> This adds a lock to hot paths such as ovl_permission, ovl_getattr and
> ovl_lookup; AI deems this worth stating, probably not that critical,
> but the save check result approach has no such problem.
Adding a separate field for overrides will solve this problem.
Nobody should race on it.
>
>> + old_ve = current->task_ve;
>> +
>> + current->task_ve = new_ve;
>> + return old_ve;
>> +}
>
> What about direction checks? ve0->veX ok and veX->ve0 is probably a
> security breach and should not be allowed.
I'll add a guard against it. If current is a CT process, it shouldn't be
able to override VE, only ve0 -> veX is OK.
>
--
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds
2026-10-28 11:58 ` Pavel Tikhomirov
@ 2026-11-02 17:35 ` Vladimir Riabchun
0 siblings, 0 replies; 10+ messages in thread
From: Vladimir Riabchun @ 2026-11-02 17:35 UTC (permalink / raw)
To: Pavel Tikhomirov; +Cc: devel
On 10/28/26 12:58, Pavel Tikhomirov wrote:
>> Host users might want to access overlayfs, owned by CT. In such case
>> ve_capable checks will fail - they will perform VE cred checks against
>> host user_ns.
>>
>> Modify ovl_override_creds so that it returns both old cred and old VE.
>> This will also protect us from future rebase issues - all new users of
>> old_cred and ovl_override_creds will fail with compilation error.
>>
>> This fixes an observed crash in overlayfs when checkpointing
>> a container with a docker running inside:
>
> What about dead creator VE? It will try to check ovl creator cred
> against host userns and fail and reintroduce the crash.
>
> AI slop:
> //
> ofs holds a reference on creator_ve, but after CT stop ve_drop_context()
> clears ve->init_cred, and ve_capable() falls back to ve0.init_cred. With
> CT creator_cred that means ns_capable(init_user_ns) -> false. So if the
> superblock outlives the CT, e.g. CRIU (or any host process) has a file
> open on the CT overlay while the CT dies, we again get
> ovl_can_decode_fh() == true at mount time and false later, i.e. the
> same NULL dereference in ovl_get_index_name_fh() this series fixes.
> //
>
> I think dieing in the middle of CRIU c/r maybe not so rare a case.
The simplest solution I see is to delay init_cred/ve_nsproxy destruction
until ve_destroy. So stale users will still have access to user_ns/etc.
>
>>
>> [ 2300.870181] BUG: kernel NULL pointer dereference, address: 0000000000000005
>> [ 2300.871537] #PF: supervisor read access in kernel mode
>> [ 2300.872577] #PF: error_code(0x0000) - not-present page
>> [ 2300.873595] PGD 800000010a18f067 P4D 800000010a18f067 PUD 10730c067 PMD 0
>> [ 2300.874900] Oops: Oops: 0000 [#1] SMP PTI
>> [ 2300.875742] CPU: 1 UID: 0 PID: 5419 Comm: criu ve: 0 Kdump: loaded Not tainted 6.12.0-211.39.1.16.6.vz10.x86_64 #1 16.6 PREEMPT(voluntary)
>> [ 2300.878057] Hardware name: Acronis OpenStack Compute/Virtuozzo, BIOS 1.16.1-1.vz9.2 04/01/2014
>> [ 2300.879684] RIP: 0010:ovl_get_index_name_fh+0x18/0x70 [overlay]
>> [ 2300.880859] Code: ...
>> [ 2300.884128] RSP: 0018:ffffd32582e0f7c0 EFLAGS: 00010246
>> [ 2300.885153] RAX: ...
>> [ 2300.895807] Call Trace:
>> [ 2300.896397]
>> [ 2300.903298] ? ovl_get_index_name_fh+0x18/0x70 [overlay]
>> [ 2300.904435] ovl_create_index+0x82/0x260 [overlay]
>> [ 2300.905406] ovl_copy_up_workdir+0x30a/0x330 [overlay]
>> [ 2300.906427] ovl_do_copy_up+0x10d/0x2e0 [overlay]
>> [ 2300.907387] ovl_copy_up_one+0x456/0x720 [overlay]
>> [ 2300.910736] ovl_copy_up_flags+0x81/0x110 [overlay]
>> [ 2300.911740] ovl_create_object+0x51/0x130 [overlay]
>> [ 2300.912719] vfs_mkdir+0x194/0x250
>> [ 2300.913470] do_mkdirat+0x14c/0x180
>> [ 2300.914223] __x64_sys_mkdir+0x46/0x70
>> [ 2300.915019] do_syscall_64+0x7d/0x160
>> [ 2300.922814] entry_SYSCALL_64_after_hwframe+0x76/0x7e
>> [ 2300.923823] RIP: 0033:0x7f5a2f8de3eb
>>
>> Overlayfs was created inside CT, so ovl_fill_super stored
>> in-container credentials to ofs->creator_cred.
>>
>> Those creds were later used in ovl_override_creds to temporarily
>> swich creds and perform checks using them.
>>
>> One of these checks happened in ovl_get_origin_fh/ovl_can_decode_fh.
>> On fs creation this check passed because we had container VE
>> and container creds, so overlayfs thought that we support
>> file handles (ovl_can_decode_fh call in ovl_make_workdir).
>>
>> When we tried to do something with the overlayfs from host VE,
>> ve_capable check was performed with credentials from CT
>> and with host VE.
>>
>> ovl_get_origin_fh returned unexpected NULL and all exploded.
>>
>> Stroing ve_capable check result on creation is a possible fix,
>> but it just masks the problem. We have other paths from overlay to
>> ve_capable checks:
>>
>> ovl_fileattr_set
>> ovl_override_creds
>> ovl_real_fileattr_set
>> vfs_fileattr_set
>> fileattr_set_prepare
>> ve_capable
>>
>> And storing capability check result will make rebase dangerous - we
>> would have to track new added code - not funny.
>
> All uses of ovl_creds() are not handled, they save creator_cred to
> backing file:
>
> struct backing_file_ctx {
> const struct cred *cred;
> ...
> }
Yep, but this is a different problem.
I think it should be handled in a separate task - require
override_cred number matching override_ve when doing ve_capable.
Here I think it is not distinguishable from underlying fs issues,
so we probably won't crash the system.
>
--
Best regards, Riabchun Vladimir
Linux Kernel Developer, Virtuozzo
_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-11-02 17:37 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 14:52 [Devel] [PATCH hci-8.1 v2 0/4] Fix kernel crash in overlayfs Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 1/4] ve: Remove STOPPED state Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 2/4] ve: Introduce override_ve/revert_ve helpers Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
2026-11-02 17:29 ` Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 3/4] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun
2026-10-26 14:52 ` [Devel] [PATCH hci-8.1 v2 4/4] ve/overlayfs: Override VE together with creds Vladimir Riabchun
2026-10-28 11:58 ` Pavel Tikhomirov
2026-11-02 17:35 ` Vladimir Riabchun
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox