* [Devel] [PATCH hci-8.1 0/2] Fix kernel crash in overlayfs
@ 2026-09-20 17:45 Vladimir Riabchun
2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 1/2] capability: Add new helper to check specific VE capabilities Vladimir Riabchun
2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun
0 siblings, 2 replies; 5+ messages in thread
From: Vladimir Riabchun @ 2026-09-20 17:45 UTC (permalink / raw)
To: Konstantin Khorenko; +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.
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>
---
Vladimir Riabchun (2):
capability: Add new helper to check specific VE capabilities
ovl: Keep creator VE in overlayfs superblock
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 ++--
include/linux/capability.h | 3 +++
kernel/capability.c | 15 +++++++++++++--
9 files changed, 30 insertions(+), 9 deletions(-)
---
base-commit: 3851feefb321d7dee9e57cbe7926410aa6dd043f
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] 5+ messages in thread* [Devel] [PATCH hci-8.1 1/2] capability: Add new helper to check specific VE capabilities 2026-09-20 17:45 [Devel] [PATCH hci-8.1 0/2] Fix kernel crash in overlayfs Vladimir Riabchun @ 2026-09-20 17:45 ` Vladimir Riabchun 2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun 1 sibling, 0 replies; 5+ messages in thread From: Vladimir Riabchun @ 2026-09-20 17:45 UTC (permalink / raw) To: Konstantin Khorenko; +Cc: devel Currently ve_capable checks only current VE caps. It is useful to be able to check capabilities of VEs other than current. https://virtuozzo.atlassian.net/browse/VSTOR-142405 Signed-off-by: Vladimir Riabchun <vladimir.riabchun@virtuozzo.com> --- include/linux/capability.h | 3 +++ kernel/capability.c | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/linux/capability.h b/include/linux/capability.h index e1cc7133b16c..443fc2331cac 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -191,6 +191,9 @@ bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode); bool capable_wrt_inode_uidgid(struct mnt_idmap *idmap, const struct inode *inode, int cap); + +struct ve_struct; +extern bool __ve_capable(struct ve_struct *ve, int cap); extern bool ve_capable(int cap); extern bool feature_capable(int feature, int cap); extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); diff --git a/kernel/capability.c b/kernel/capability.c index 34716847c6b8..1dd88645a6a3 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -386,13 +386,13 @@ bool ns_capable(struct user_namespace *ns, int cap) EXPORT_SYMBOL(ns_capable); #ifdef CONFIG_VE -bool ve_capable(int cap) +bool __ve_capable(struct ve_struct *ve, int cap) { struct cred *cred; bool ret; rcu_read_lock(); - cred = get_exec_env()->init_cred; + cred = ve->init_cred; if (cred == NULL) /* ve isn't running */ cred = ve0.init_cred; @@ -403,6 +403,11 @@ bool ve_capable(int cap) return ret; } +bool ve_capable(int cap) +{ + return __ve_capable(get_exec_env(), cap); +} + bool ve_capable_noaudit(int cap) { struct cred *cred; @@ -428,6 +433,11 @@ bool feature_capable(int feature, int cap) return capable(cap); } #else +bool __ve_capable(struct ve_struct *ve, int cap) +{ + return capable(cap); +} + bool ve_capable(int cap) { return capable(cap); @@ -443,6 +453,7 @@ bool feature_capable(int feature, int cap) return capable(cap); } #endif +EXPORT_SYMBOL_GPL(__ve_capable); EXPORT_SYMBOL_GPL(ve_capable); EXPORT_SYMBOL_GPL(ve_capable_noaudit); -- 2.47.1 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock 2026-09-20 17:45 [Devel] [PATCH hci-8.1 0/2] Fix kernel crash in overlayfs Vladimir Riabchun 2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 1/2] capability: Add new helper to check specific VE capabilities Vladimir Riabchun @ 2026-09-20 17:45 ` Vladimir Riabchun 2026-09-25 17:58 ` Konstantin Khorenko 1 sibling, 1 reply; 5+ messages in thread From: Vladimir Riabchun @ 2026-09-20 17:45 UTC (permalink / raw) To: Konstantin Khorenko; +Cc: devel 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@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 _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock 2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun @ 2026-09-25 17:58 ` Konstantin Khorenko 2026-10-19 9:00 ` Pavel Tikhomirov 0 siblings, 1 reply; 5+ messages in thread From: Konstantin Khorenko @ 2026-09-25 17:58 UTC (permalink / raw) To: Vladimir Riabchun; +Cc: devel > 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@virtuozzo.com> > > diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c > index 4388004a319d0..b7c06ae4beb4c 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 efa2a072aa693..e33debf8b1ed8 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 8577c7884fb92..9c8dd2f9cac62 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 ab78001635e7c..a6ad8b8de48c0 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 32c58fcfe52f0..c047f1265b8ef 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 fb47711c8fb9a..e87b6684ec70f 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()); > + get_ve() pins the ve_struct, but does not keep the VE running: on CT stop ve_drop_context() sets ve->init_cred = NULL, so __ve_capable() falls back to ve0.init_cred, and ns_capable(&init_user_ns, cap) with the in-CT creator creds is false. The check flips after mount again, permanently this time, and the same NULL deref in ovl_create_index() is reachable whenever the sb outlives the VE (host-side bind mount, a held fd, NFS export). After this series both inputs of the check are fixed at mount time (creator_cred, creator_ve), so its result is a mount-time constant. Why not compute ve_capable(CAP_DAC_READ_SEARCH) once in ovl_fill_super() and store a bool in ovl_fs, next to the existing nofh? That closes the stopped-VE window and makes patch 1, creator_ve and the get_ve/put_ve pairing unnecessary. Draft patch instead of both in thise series: diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -77,6 +77,8 @@ struct ovl_fs { struct ovl_config config; /* creds of process who forced instantiation of super block */ const struct cred *creator_cred; + /* ve_capable(CAP_DAC_READ_SEARCH) frozen at mount time */ + bool fh_capable; bool tmpfile; bool noxattr; bool nofh; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -393,7 +393,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 +757,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 +1328,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_d_op = &ovl_dentry_operations; + ofs->fh_capable = ve_capable(CAP_DAC_READ_SEARCH); + err = -ENOMEM; ofs->creator_cred = cred = prepare_creds(); if (!cred) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c --- 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 (!ofs->fh_capable) return 0; if (!exportfs_can_decode_fh(sb->s_export_op)) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c --- 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 (!ofs->fh_capable) return NULL; /* diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c --- 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/overlayfs.h b/fs/overlayfs/overlayfs.h --- 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); -- Konstantin Khorenko <khorenko@virtuozzo.com> _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock 2026-09-25 17:58 ` Konstantin Khorenko @ 2026-10-19 9:00 ` Pavel Tikhomirov 0 siblings, 0 replies; 5+ messages in thread From: Pavel Tikhomirov @ 2026-10-19 9:00 UTC (permalink / raw) To: Konstantin Khorenko, Vladimir Riabchun; +Cc: devel On 9/25/26 19:58, Konstantin Khorenko wrote: >> 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@virtuozzo.com> >> >> diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c >> index 4388004a319d0..b7c06ae4beb4c 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 efa2a072aa693..e33debf8b1ed8 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 8577c7884fb92..9c8dd2f9cac62 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 ab78001635e7c..a6ad8b8de48c0 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 32c58fcfe52f0..c047f1265b8ef 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 fb47711c8fb9a..e87b6684ec70f 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()); >> + > > get_ve() pins the ve_struct, but does not keep the VE running: on CT > stop ve_drop_context() sets ve->init_cred = NULL, so __ve_capable() > falls back to ve0.init_cred, and ns_capable(&init_user_ns, cap) with > the in-CT creator creds is false. The check flips after mount again, > permanently this time, and the same NULL deref in ovl_create_index() > is reachable whenever the sb outlives the VE (host-side bind mount, a > held fd, NFS export). > > After this series both inputs of the check are fixed at mount time > (creator_cred, creator_ve), so its result is a mount-time constant. > Why not compute ve_capable(CAP_DAC_READ_SEARCH) once in > ovl_fill_super() and store a bool in ovl_fs, next to the existing > nofh? That closes the stopped-VE window and makes patch 1, creator_ve > and the get_ve/put_ve pairing unnecessary. > > Draft patch instead of both in thise series: Sounds good. AI suggests also: diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 444aeeccb6daf..5a6160f8c18ed 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -802,6 +802,14 @@ static struct dentry *ovl_fh_to_dentry(struct super_block *sb, struct fid *fid, unsigned int flags = 0; int err; + /* + * Unlike lookup, this runs with the caller's creds and not under + * creator_cred, so ofs->fh_capable is not enough: the caller has to + * be allowed to decode file handles too. + */ + if (!ve_capable(CAP_DAC_READ_SEARCH)) + return ERR_PTR(-ESTALE); + fh = ovl_fid_to_fh(fid, len, fh_type); err = PTR_ERR(fh); if (IS_ERR(fh)) This path is not under overlay cred override so it should not fallback to fh_capable. AI comment: We didn't need a separate check, because the ve_capable() inside ovl_decode_real_fh() already covered ovl_fh_to_dentry(). Every export decode goes through it: ovl_fh_to_dentry() ovl_upper_fh_to_d() -> ovl_decode_real_fh() /* NULL -> NULL dentry -> ESTALE */ ovl_lower_fh_to_d() -> ovl_check_origin_fh() /* error -> returned at once */ -> ovl_decode_real_fh() That one check meant two different things depending on how it was reached: - Lookup and index paths (ovl_lookup(), ovl_verify_index(), ovl_index_upper()) run under ovl_override_creds(). There it checks the mounter, which is what we now freeze in fh_capable. - Export path (ovl_fh_to_dentry()) never overrides credentials. There the same line checks the caller of open_by_handle_at() or nfsd. > > diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h > --- a/fs/overlayfs/ovl_entry.h > +++ b/fs/overlayfs/ovl_entry.h > @@ -77,6 +77,8 @@ struct ovl_fs { > struct ovl_config config; > /* creds of process who forced instantiation of super block */ > const struct cred *creator_cred; > + /* ve_capable(CAP_DAC_READ_SEARCH) frozen at mount time */ > + bool fh_capable; > bool tmpfile; > bool noxattr; > bool nofh; > diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c > --- a/fs/overlayfs/super.c > +++ b/fs/overlayfs/super.c > @@ -393,7 +393,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 +757,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 +1328,8 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) > > sb->s_d_op = &ovl_dentry_operations; > > + ofs->fh_capable = ve_capable(CAP_DAC_READ_SEARCH); > + > err = -ENOMEM; > ofs->creator_cred = cred = prepare_creds(); > if (!cred) > diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c > --- 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 (!ofs->fh_capable) > return 0; > > if (!exportfs_can_decode_fh(sb->s_export_op)) > diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c > --- 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 (!ofs->fh_capable) > return NULL; > > /* > diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c > --- 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/overlayfs.h b/fs/overlayfs/overlayfs.h > --- 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); > -- Best regards, Pavel Tikhomirov Senior Software Developer, Virtuozzo. _______________________________________________ Devel mailing list Devel@openvz.org https://lists.openvz.org/mailman/listinfo/devel ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-19 9:02 UTC | newest] Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-09-20 17:45 [Devel] [PATCH hci-8.1 0/2] Fix kernel crash in overlayfs Vladimir Riabchun 2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 1/2] capability: Add new helper to check specific VE capabilities Vladimir Riabchun 2026-09-20 17:45 ` [Devel] [PATCH hci-8.1 2/2] ovl: Keep creator VE in overlayfs superblock Vladimir Riabchun 2026-09-25 17:58 ` Konstantin Khorenko 2026-10-19 9:00 ` Pavel Tikhomirov
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox