[Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock
Vladimir Riabchun
vladimir.riabchun at virtuozzo.com
Sun Sep 20 20:45:43 MSK 2026
We observed a crash in overlayfs when checkpointing a container
with 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: 0000000000000010 RBX: 0000000000000000 RCX: ffff8ba4497480a8
[ 2300.886480] RDX: ffff8ba45535dc00 RSI: 0000000000000dc0 RDI: 0000000000000000
[ 2300.887824] RBP: ffff8ba68f9daa80 R08: ffff8ba4497480a8 R09: 000000000000030e
[ 2300.889139] R10: 0000000000000000 R11: ffff8ba44862d6d0 R12: ffffd32582e0f800
[ 2300.890498] R13: 0000000000000000 R14: ffff8ba497cb0110 R15: ffff8ba517548cc0
[ 2300.891838] FS: 00007f5a2f492880(0000) GS:ffff8bab9fc80000(0000) knlGS:0000000000000000
[ 2300.893376] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2300.894476] CR2: 0000000000000005 CR3: 000000033403a005 CR4: 00000000003726f0
[ 2300.895807] Call Trace:
[ 2300.896397] <TASK>
[ 2300.896938] ? show_trace_log_lvl+0x1b0/0x2f0
[ 2300.897854] ? show_trace_log_lvl+0x1b0/0x2f0
[ 2300.898748] ? ovl_create_index+0x82/0x260 [overlay]
[ 2300.899766] ? __die_body.cold+0x8/0x12
[ 2300.900582] ? page_fault_oops+0x148/0x160
[ 2300.901562] ? exc_page_fault+0x7f/0x150
[ 2300.902406] ? asm_exc_page_fault+0x26/0x30
[ 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.908376] ? xa_load+0x7b/0xb0
[ 2300.909090] ? _raw_spin_unlock+0xe/0x30
[ 2300.909913] ? list_lru_add+0x11b/0x160
[ 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.915798] ? do_sys_openat2+0x9c/0xe0
[ 2300.916672] ? syscall_exit_work+0xf3/0x120
[ 2300.917540] ? syscall_exit_to_user_mode+0x32/0x190
[ 2300.918518] ? do_syscall_64+0x89/0x160
[ 2300.919316] ? syscall_exit_work+0xf3/0x120
[ 2300.920181] ? syscall_exit_to_user_mode+0x32/0x190
[ 2300.921173] ? do_syscall_64+0x89/0x160
[ 2300.921991] ? exc_page_fault+0x7f/0x150
[ 2300.922814] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 2300.923823] RIP: 0033:0x7f5a2f8de3eb
The root cause is VE namespace mismatch. Overlayfs was created
inside CT, so ovl_fill_super stored in-container credentials
to ofs->creator_cred.
Those creds are 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.
Store creator VE next to creator creds, so we can perform all
ve_capable checks with the correct VE.
https://virtuozzo.atlassian.net/browse/VSTOR-142405
Signed-off-by: Vladimir Riabchun <vladimir.riabchun at virtuozzo.com>
---
fs/overlayfs/copy_up.c | 2 +-
fs/overlayfs/namei.c | 2 +-
fs/overlayfs/overlayfs.h | 2 +-
fs/overlayfs/ovl_entry.h | 1 +
fs/overlayfs/params.c | 3 +++
fs/overlayfs/super.c | 7 +++++--
fs/overlayfs/util.c | 4 ++--
7 files changed, 14 insertions(+), 7 deletions(-)
diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 4388004a319d..b7c06ae4beb4 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -478,7 +478,7 @@ struct ovl_fh *ovl_get_origin_fh(struct ovl_fs *ofs, struct dentry *origin)
* so we can use the overlay.origin xattr to distignuish between a copy
* up and a pure upper inode.
*/
- if (!ovl_can_decode_fh(origin->d_sb))
+ if (!ovl_can_decode_fh(origin->d_sb, ofs))
return NULL;
return ovl_encode_real_fh(ofs, d_inode(origin), false);
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index efa2a072aa69..e33debf8b1ed 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -166,7 +166,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
struct dentry *real;
int bytes;
- if (!ve_capable(CAP_DAC_READ_SEARCH))
+ if (!__ve_capable(ofs->creator_ve, CAP_DAC_READ_SEARCH))
return NULL;
/*
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 8577c7884fb9..9c8dd2f9cac6 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -427,7 +427,7 @@ static inline const struct cred *ovl_creds(struct super_block *sb)
return OVL_FS(sb)->creator_cred;
}
-int ovl_can_decode_fh(struct super_block *sb);
+int ovl_can_decode_fh(struct super_block *sb, struct ovl_fs *ofs);
struct dentry *ovl_indexdir(struct super_block *sb);
bool ovl_index_all(struct super_block *sb);
bool ovl_verify_lower(struct super_block *sb);
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..e87b6684ec70 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"
@@ -393,7 +394,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
* The inodes index feature and NFS export need to encode and decode
* file handles, so they require that all layers support them.
*/
- fh_type = ovl_can_decode_fh(path->dentry->d_sb);
+ fh_type = ovl_can_decode_fh(path->dentry->d_sb, ofs);
if ((ofs->config.nfs_export ||
(ofs->config.index && ofs->config.upperdir)) && !fh_type) {
ofs->config.index = false;
@@ -757,7 +758,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
}
/* Check if upper/work fs supports file handles */
- fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
+ fh_type = ovl_can_decode_fh(ofs->workdir->d_sb, ofs);
if (ofs->config.index && !fh_type) {
ofs->config.index = false;
pr_warn("upper fs does not support file handles, falling back to index=off.\n");
@@ -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)
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index f95090f0d29c..047b618ad296 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -78,9 +78,9 @@ const struct cred *ovl_override_creds(struct super_block *sb)
* Return 1 (FILEID_INO32_GEN) if fs uses the default 32bit inode encoding.
* Return -1 if fs uses a non default encoding with unknown inode size.
*/
-int ovl_can_decode_fh(struct super_block *sb)
+int ovl_can_decode_fh(struct super_block *sb, struct ovl_fs *ofs)
{
- if (!ve_capable(CAP_DAC_READ_SEARCH))
+ if (!__ve_capable(ofs->creator_ve, CAP_DAC_READ_SEARCH))
return 0;
if (!exportfs_can_decode_fh(sb->s_export_op))
--
2.47.1
More information about the Devel
mailing list