| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/11 07:30 | flow | patch-triage |
2mArgs:null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_BACKED_BY_FILE"
],
"FocusSymbols": [
"erofs_fc_test_file_super",
"erofs_fc_get_tree_file",
"get_tree_super"
],
"KMSANReasoning": "The patch introduces a new mount option `superblock_share` for EROFS file-backed mounts and adds logic to share superblocks when compatible options are used. The comparison function `erofs_fc_test_file_super` uses `memcmp` to compare `struct erofs_mount_opts`. While this struct contains padding, instances of it are embedded within `struct erofs_sb_info`, which is always allocated via `kzalloc` (in `erofs_init_fs_context`). Consequently, the padding is initialized to zero and remains zero, meaning `memcmp` safely reads initialized memory without triggering KMSAN or causing undefined behavior.\n\nThe patch does not introduce any structures that are copied to user space, nor does it allocate complex data structures where uninitialized fields could be read later. There is no conditional logic depending on uninitialized variables. Any potential bugs introduced by these changes (such as use-after-free, memory leaks, or locking issues related to superblock sharing) would be effectively caught by KASAN, LOCKDEP, and other standard bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces a new mount option `superblock_share` for the EROFS filesystem, allowing superblock sharing when the same backing file is mounted multiple times. This modifies the core mount logic for EROFS and introduces new functions like `erofs_fc_test_file_super` and `erofs_fc_get_tree_file`. This is a functional change in the filesystem mount path that is reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/11 07:30 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit f1fe446af35de9317fc46222ef240df0698e703a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Aug 11 07:30:25 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst\nindex d301d9ac946a9..49c4e7dbc5d61 100644\n--- a/Documentation/filesystems/erofs.rst\n+++ b/Documentation/filesystems/erofs.rst\n@@ -139,6 +139,9 @@ inode_share Enable inode page sharing for this filesystem. Inodes wi\n page cache.\n source=%s (For file-backed mounts) Specify the backing image as a path\n or as an already-opened file descriptor.\n+superblock_share (For file-backed mounts) Share the superblock when the same\n+ backing file is mounted more than once with compatible\n+ options. Remount is not allowed on shared superblocks.\n =================== =========================================================\n \n File-backed mounts\n@@ -156,6 +159,11 @@ Only regular files are accepted as backing files; to mount an image that\n resides on a block device, use the traditional block device mount path\n instead.\n \n+When superblock_share is specified and the same backing file (identified\n+by inode and fsoffset) is mounted more than once with compatible mount\n+options, the kernel reuses the existing superblock instead of creating a\n+new one. Remount is not allowed on shared superblocks.\n+\n Sysfs Entries\n =============\n \ndiff --git a/fs/erofs/internal.h b/fs/erofs/internal.h\nindex 57bd21859c65d..86da219f8f4ec 100644\n--- a/fs/erofs/internal.h\n+++ b/fs/erofs/internal.h\n@@ -156,6 +156,7 @@ struct erofs_sb_info {\n #define EROFS_MOUNT_DAX_NEVER\t\t0x00000080\n #define EROFS_MOUNT_DIRECT_IO\t\t0x00000100\n #define EROFS_MOUNT_INODE_SHARE\t\t0x00000200\n+#define EROFS_MOUNT_SUPERBLOCK_SHARE\t0x00000400\n \n #define clear_opt(opt, option)\t((opt)-\u003emount_opt \u0026= ~EROFS_MOUNT_##option)\n #define set_opt(opt, option)\t((opt)-\u003emount_opt |= EROFS_MOUNT_##option)\ndiff --git a/fs/erofs/super.c b/fs/erofs/super.c\nindex ddc9719e6d18e..457e067447d5f 100644\n--- a/fs/erofs/super.c\n+++ b/fs/erofs/super.c\n@@ -386,7 +386,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi)\n enum {\n \tOpt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,\n \tOpt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,\n-\tOpt_source,\n+\tOpt_source, Opt_superblock_share,\n };\n \n static const struct constant_table erofs_param_cache_strategy[] = {\n@@ -415,6 +415,7 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = {\n \tfsparam_u64(\"fsoffset\",\t\t\tOpt_fsoffset),\n \tfsparam_flag(\"inode_share\",\t\tOpt_inode_share),\n \tfsparam_file_or_string(\"source\",\tOpt_source),\n+\tfsparam_flag(\"superblock_share\",\tOpt_superblock_share),\n \t{}\n };\n \n@@ -560,6 +561,12 @@ static int erofs_fc_parse_param(struct fs_context *fc,\n \t\telse\n \t\t\tset_opt(\u0026sbi-\u003eopt, INODE_SHARE);\n \t\tbreak;\n+\tcase Opt_superblock_share:\n+\t\tif (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE))\n+\t\t\terrorfc(fc, \"%s option not supported\", erofs_fs_parameters[opt].name);\n+\t\telse\n+\t\t\tset_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE);\n+\t\tbreak;\n \tcase Opt_source:\n \t\treturn erofs_fc_parse_source(fc, param);\n \t}\n@@ -652,6 +659,17 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)\n \t\terrorfc(fc, \"FSDAX is not allowed when inode_share is on\");\n \t\treturn -EINVAL;\n \t}\n+\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE) \u0026\u0026\n+\t test_opt(\u0026sbi-\u003eopt, INODE_SHARE)) {\n+\t\terrorfc(fc, \"superblock_share is not allowed when inode_share is on\");\n+\t\treturn -EINVAL;\n+\t}\n+\t/* Extra devices are not yet supported with superblock sharing. */\n+\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE) \u0026\u0026\n+\t sbi-\u003edevs-\u003eextra_devices) {\n+\t\terrorfc(fc, \"superblock_share does not support extra devices\");\n+\t\treturn -EINVAL;\n+\t}\n \n \tsbi-\u003eblkszbits = PAGE_SHIFT;\n \tif (!sb-\u003es_bdev) {\n@@ -779,6 +797,36 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)\n \treturn 0;\n }\n \n+static int erofs_fc_test_file_super(struct super_block *sb,\n+\t\t\t\t struct fs_context *fc)\n+{\n+\t/*\n+\t * The assumption is that the function is used only with a sbi that\n+\t * has SUPERBLOCK_SHARE set, so that the memcmp later ensures\n+\t * new_sbi also is shareable.\n+\t */\n+\tstruct erofs_sb_info *sbi = EROFS_SB(sb);\n+\tstruct erofs_sb_info *new_sbi = fc-\u003es_fs_info;\n+\n+\tif (sb-\u003es_iflags \u0026 SB_I_RETIRED)\n+\t\treturn 0;\n+\tif (!sbi-\u003edif0.file || !new_sbi-\u003edif0.file)\n+\t\treturn 0;\n+\treturn file_inode(sbi-\u003edif0.file) == file_inode(new_sbi-\u003edif0.file) \u0026\u0026\n+\t sbi-\u003edif0.fsoff == new_sbi-\u003edif0.fsoff \u0026\u0026\n+\t !memcmp(\u0026sbi-\u003eopt, \u0026new_sbi-\u003eopt, sizeof(sbi-\u003eopt));\n+}\n+\n+static int erofs_fc_get_tree_file(struct fs_context *fc)\n+{\n+\tstruct erofs_sb_info *sbi = fc-\u003es_fs_info;\n+\n+\tif (!test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE))\n+\t\treturn get_tree_nodev(fc, erofs_fc_fill_super);\n+\n+\treturn get_tree_super(fc, erofs_fc_test_file_super, erofs_fc_fill_super);\n+}\n+\n static int erofs_fc_get_tree(struct fs_context *fc)\n {\n \tstruct erofs_sb_info *sbi = fc-\u003es_fs_info;\n@@ -794,7 +842,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)\n \t\t\terrorfc(fc, \"source is unsupported\");\n \t\t\treturn -EINVAL;\n \t\t}\n-\t\treturn get_tree_nodev(fc, erofs_fc_fill_super);\n+\t\treturn erofs_fc_get_tree_file(fc);\n \t}\n \n \tret = get_tree_bdev_flags(fc, erofs_fc_fill_super,\n@@ -812,7 +860,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)\n \n \t\tif (S_ISREG(file_inode(sbi-\u003edif0.file)-\u003ei_mode) \u0026\u0026\n \t\t sbi-\u003edif0.file-\u003ef_mapping-\u003ea_ops-\u003eread_folio)\n-\t\t\treturn get_tree_nodev(fc, erofs_fc_fill_super);\n+\t\t\treturn erofs_fc_get_tree_file(fc);\n \t}\n \treturn ret;\n }\n@@ -825,6 +873,10 @@ static int erofs_fc_reconfigure(struct fs_context *fc)\n \n \tDBG_BUGON(!sb_rdonly(sb));\n \n+\t/* Shared superblocks must not be reconfigured. */\n+\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE))\n+\t\treturn -EBUSY;\n+\n \tif (new_sbi-\u003edomain_id)\n \t\terofs_info(sb, \"ignoring reconfiguration for domain_id.\");\n \ndiff --git a/fs/super.c b/fs/super.c\nindex 5feecf5d9038d..71cd2dbade5b3 100644\n--- a/fs/super.c\n+++ b/fs/super.c\n@@ -1389,7 +1389,17 @@ static int test_single_super(struct super_block *s, struct fs_context *fc)\n \treturn 1;\n }\n \n-static int vfs_get_super(struct fs_context *fc,\n+/**\n+ * get_tree_super - Get a superblock, optionally sharing an existing one\n+ * @fc: The filesystem context holding the parameters\n+ * @test: Comparison function to find a matching existing superblock, or NULL\n+ * @fill_super: Helper to initialise a new superblock\n+ *\n+ * If @test is non-NULL and matches an existing superblock, that superblock is\n+ * reused; otherwise a new anonymous superblock is created and initialised with\n+ * @fill_super. Passing NULL for @test always creates a new superblock.\n+ */\n+int get_tree_super(struct fs_context *fc,\n \t\tint (*test)(struct super_block *, struct fs_context *),\n \t\tint (*fill_super)(struct super_block *sb,\n \t\t\t\t struct fs_context *fc))\n@@ -1416,12 +1426,13 @@ static int vfs_get_super(struct fs_context *fc,\n \tdeactivate_locked_super(sb);\n \treturn err;\n }\n+EXPORT_SYMBOL(get_tree_super);\n \n int get_tree_nodev(struct fs_context *fc,\n \t\t int (*fill_super)(struct super_block *sb,\n \t\t\t\t struct fs_context *fc))\n {\n-\treturn vfs_get_super(fc, NULL, fill_super);\n+\treturn get_tree_super(fc, NULL, fill_super);\n }\n EXPORT_SYMBOL(get_tree_nodev);\n \n@@ -1429,7 +1440,7 @@ int get_tree_single(struct fs_context *fc,\n \t\t int (*fill_super)(struct super_block *sb,\n \t\t\t\t struct fs_context *fc))\n {\n-\treturn vfs_get_super(fc, test_single_super, fill_super);\n+\treturn get_tree_super(fc, test_single_super, fill_super);\n }\n EXPORT_SYMBOL(get_tree_single);\n \n@@ -1439,7 +1450,7 @@ int get_tree_keyed(struct fs_context *fc,\n \t\tvoid *key)\n {\n \tfc-\u003es_fs_info = key;\n-\treturn vfs_get_super(fc, test_keyed_super, fill_super);\n+\treturn get_tree_super(fc, test_keyed_super, fill_super);\n }\n EXPORT_SYMBOL(get_tree_keyed);\n \ndiff --git a/include/linux/fs_context.h b/include/linux/fs_context.h\nindex 0d6c8a6d7be21..c920aba5177c3 100644\n--- a/include/linux/fs_context.h\n+++ b/include/linux/fs_context.h\n@@ -150,6 +150,10 @@ extern int vfs_parse_fs_param_source(struct fs_context *fc,\n \t\t\t\t struct fs_parameter *param);\n extern void fc_drop_locked(struct fs_context *fc);\n \n+extern int get_tree_super(struct fs_context *fc,\n+\t\t\t int (*test)(struct super_block *, struct fs_context *),\n+\t\t\t int (*fill_super)(struct super_block *sb,\n+\t\t\t\t\t struct fs_context *fc));\n extern int get_tree_nodev(struct fs_context *fc,\n \t\t\t int (*fill_super)(struct super_block *sb,\n \t\t\t\t\t struct fs_context *fc));\n"
} |
| 2/1 | 2026/08/11 07:30 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_BACKED_BY_FILE"
],
"FocusSymbols": [
"erofs_fc_test_file_super",
"erofs_fc_get_tree_file",
"get_tree_super"
],
"Reasoning": "The patch introduces a new mount option `superblock_share` for the EROFS filesystem, allowing superblock sharing when the same backing file is mounted multiple times. This modifies the core mount logic for EROFS and introduces new functions like `erofs_fc_test_file_super` and `erofs_fc_get_tree_file`. This is a functional change in the filesystem mount path that is reachable and worth fuzzing.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit f1fe446af35de9317fc46222ef240df0698e703a
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 11 07:30:25 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst
index d301d9ac946a9..49c4e7dbc5d61 100644
--- a/Documentation/filesystems/erofs.rst
+++ b/Documentation/filesystems/erofs.rst
@@ -139,6 +139,9 @@ inode_share Enable inode page sharing for this filesystem. Inodes wi
page cache.
source=%s (For file-backed mounts) Specify the backing image as a path
or as an already-opened file descriptor.
+superblock_share (For file-backed mounts) Share the superblock when the same
+ backing file is mounted more than once with compatible
+ options. Remount is not allowed on shared superblocks.
=================== =========================================================
File-backed mounts
@@ -156,6 +159,11 @@ Only regular files are accepted as backing files; to mount an image that
resides on a block device, use the traditional block device mount path
instead.
+When superblock_share is specified and the same backing file (identified
+by inode and fsoffset) is mounted more than once with compatible mount
+options, the kernel reuses the existing superblock instead of creating a
+new one. Remount is not allowed on shared superblocks.
+
Sysfs Entries
=============
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 57bd21859c65d..86da219f8f4ec 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -156,6 +156,7 @@ struct erofs_sb_info {
#define EROFS_MOUNT_DAX_NEVER 0x00000080
#define EROFS_MOUNT_DIRECT_IO 0x00000100
#define EROFS_MOUNT_INODE_SHARE 0x00000200
+#define EROFS_MOUNT_SUPERBLOCK_SHARE 0x00000400
#define clear_opt(opt, option) ((opt)->mount_opt &= ~EROFS_MOUNT_##option)
#define set_opt(opt, option) ((opt)->mount_opt |= EROFS_MOUNT_##option)
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index ddc9719e6d18e..457e067447d5f 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -386,7 +386,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi)
enum {
Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,
Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,
- Opt_source,
+ Opt_source, Opt_superblock_share,
};
static const struct constant_table erofs_param_cache_strategy[] = {
@@ -415,6 +415,7 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = {
fsparam_u64("fsoffset", Opt_fsoffset),
fsparam_flag("inode_share", Opt_inode_share),
fsparam_file_or_string("source", Opt_source),
+ fsparam_flag("superblock_share", Opt_superblock_share),
{}
};
@@ -560,6 +561,12 @@ static int erofs_fc_parse_param(struct fs_context *fc,
else
set_opt(&sbi->opt, INODE_SHARE);
break;
+ case Opt_superblock_share:
+ if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE))
+ errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
+ else
+ set_opt(&sbi->opt, SUPERBLOCK_SHARE);
+ break;
case Opt_source:
return erofs_fc_parse_source(fc, param);
}
@@ -652,6 +659,17 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
errorfc(fc, "FSDAX is not allowed when inode_share is on");
return -EINVAL;
}
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE) &&
+ test_opt(&sbi->opt, INODE_SHARE)) {
+ errorfc(fc, "superblock_share is not allowed when inode_share is on");
+ return -EINVAL;
+ }
+ /* Extra devices are not yet supported with superblock sharing. */
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE) &&
+ sbi->devs->extra_devices) {
+ errorfc(fc, "superblock_share does not support extra devices");
+ return -EINVAL;
+ }
sbi->blkszbits = PAGE_SHIFT;
if (!sb->s_bdev) {
@@ -779,6 +797,36 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
return 0;
}
+static int erofs_fc_test_file_super(struct super_block *sb,
+ struct fs_context *fc)
+{
+ /*
+ * The assumption is that the function is used only with a sbi that
+ * has SUPERBLOCK_SHARE set, so that the memcmp later ensures
+ * new_sbi also is shareable.
+ */
+ struct erofs_sb_info *sbi = EROFS_SB(sb);
+ struct erofs_sb_info *new_sbi = fc->s_fs_info;
+
+ if (sb->s_iflags & SB_I_RETIRED)
+ return 0;
+ if (!sbi->dif0.file || !new_sbi->dif0.file)
+ return 0;
+ return file_inode(sbi->dif0.file) == file_inode(new_sbi->dif0.file) &&
+ sbi->dif0.fsoff == new_sbi->dif0.fsoff &&
+ !memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt));
+}
+
+static int erofs_fc_get_tree_file(struct fs_context *fc)
+{
+ struct erofs_sb_info *sbi = fc->s_fs_info;
+
+ if (!test_opt(&sbi->opt, SUPERBLOCK_SHARE))
+ return get_tree_nodev(fc, erofs_fc_fill_super);
+
+ return get_tree_super(fc, erofs_fc_test_file_super, erofs_fc_fill_super);
+}
+
static int erofs_fc_get_tree(struct fs_context *fc)
{
struct erofs_sb_info *sbi = fc->s_fs_info;
@@ -794,7 +842,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)
errorfc(fc, "source is unsupported");
return -EINVAL;
}
- return get_tree_nodev(fc, erofs_fc_fill_super);
+ return erofs_fc_get_tree_file(fc);
}
ret = get_tree_bdev_flags(fc, erofs_fc_fill_super,
@@ -812,7 +860,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)
if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) &&
sbi->dif0.file->f_mapping->a_ops->read_folio)
- return get_tree_nodev(fc, erofs_fc_fill_super);
+ return erofs_fc_get_tree_file(fc);
}
return ret;
}
@@ -825,6 +873,10 @@ static int erofs_fc_reconfigure(struct fs_context *fc)
DBG_BUGON(!sb_rdonly(sb));
+ /* Shared superblocks must not be reconfigured. */
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE))
+ return -EBUSY;
+
if (new_sbi->domain_id)
erofs_info(sb, "ignoring reconfiguration for domain_id.");
diff --git a/fs/super.c b/fs/super.c
index 5feecf5d9038d..71cd2dbade5b3 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1389,7 +1389,17 @@ static int test_single_super(struct super_block *s, struct fs_context *fc)
return 1;
}
-static int vfs_get_super(struct fs_context *fc,
+/**
+ * get_tree_super - Get a superblock, optionally sharing an existing one
+ * @fc: The filesystem context holding the parameters
+ * @test: Comparison function to find a matching existing superblock, or NULL
+ * @fill_super: Helper to initialise a new superblock
+ *
+ * If @test is non-NULL and matches an existing superblock, that superblock is
+ * reused; otherwise a new anonymous superblock is created and initialised with
+ * @fill_super. Passing NULL for @test always creates a new superblock.
+ */
+int get_tree_super(struct fs_context *fc,
int (*test)(struct super_block *, struct fs_context *),
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
@@ -1416,12 +1426,13 @@ static int vfs_get_super(struct fs_context *fc,
deactivate_locked_super(sb);
return err;
}
+EXPORT_SYMBOL(get_tree_super);
int get_tree_nodev(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
{
- return vfs_get_super(fc, NULL, fill_super);
+ return get_tree_super(fc, NULL, fill_super);
}
EXPORT_SYMBOL(get_tree_nodev);
@@ -1429,7 +1440,7 @@ int get_tree_single(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
{
- return vfs_get_super(fc, test_single_super, fill_super);
+ return get_tree_super(fc, test_single_super, fill_super);
}
EXPORT_SYMBOL(get_tree_single);
@@ -1439,7 +1450,7 @@ int get_tree_keyed(struct fs_context *fc,
void *key)
{
fc->s_fs_info = key;
- return vfs_get_super(fc, test_keyed_super, fill_super);
+ return get_tree_super(fc, test_keyed_super, fill_super);
}
EXPORT_SYMBOL(get_tree_keyed);
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 0d6c8a6d7be21..c920aba5177c3 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -150,6 +150,10 @@ extern int vfs_parse_fs_param_source(struct fs_context *fc,
struct fs_parameter *param);
extern void fc_drop_locked(struct fs_context *fc);
+extern int get_tree_super(struct fs_context *fc,
+ int (*test)(struct super_block *, struct fs_context *),
+ int (*fill_super)(struct super_block *sb,
+ struct fs_context *fc));
extern int get_tree_nodev(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc));
|
| 3/2 | 2026/08/11 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5046 output: 33 thoughts: 396 Thoughts: **Analyzing the EROFS Filesystem Patch for Fuzzing Potential** Okay, so I've got this patch introducing a new mount option, `superblock_share`, for the EROFS filesystem. The goal seems to be to enable superblock sharing when the same backing file is mounted multiple times with compatible settings. It looks like the changes are spread across `fs/erofs/super.c`, `fs/erofs/internal.h`, `fs/super.c`, and `include/linux/fs_context.h`. The core modification in `fs/super.c` seems to be renaming `vfs_get_super` to `get_tree_super` and exporting it. In `fs/erofs/super.c` specifically, new functions `erofs_fc_test_file_super` and `erofs_fc_get_tree_file` are added. Now, the important question is: is this reachable, and is it worth targeting with fuzzing? Definitely. It's a *new* mount option for EROFS. Mount options are practically tailor-made for fuzzing with something like syzkaller. They're a direct entry point for manipulating the filesystem's behavior. Next, I need to figure out the necessary config options. Clearly, `CONFIG_EROFS_FS` is the big one for enabling the EROFS filesystem itself. The patch code itself, specifically the code snippet in `fs/erofs/super.c`, includes a check for `CONFIG_EROFS_FS_BACKED_BY_FILE`. That's important; if that option isn't enabled, the `superblock_share` option throws an error. So, I'll need to enable both `EROFS_FS` and `EROFS_FS_BACKED_BY_FILE` to properly exercise this feature. Finally, I should take a quick look at `fs/erofs/Kconfig` to ensure there aren't any other hidden dependencies or conditional requirements that I might be missing. Gotta cover all the bases before I start crafting fuzzing targets. |
| 4/2 | 2026/08/11 07:30 | tool | grepper |
0mArgs:{
"Expression": "config EROFS_FS",
"PathPrefix": "fs/erofs/Kconfig"
}Results: {
"Output": "fs/erofs/Kconfig-2-\nfs/erofs/Kconfig:3:config EROFS_FS\nfs/erofs/Kconfig-4-\ttristate \"EROFS filesystem support\"\n--\nfs/erofs/Kconfig-49-\nfs/erofs/Kconfig:50:config EROFS_FS_DEBUG\nfs/erofs/Kconfig-51-\tbool \"EROFS debugging feature\"\n--\nfs/erofs/Kconfig-59-\nfs/erofs/Kconfig:60:config EROFS_FS_XATTR\nfs/erofs/Kconfig-61-\tbool \"EROFS extended attributes\"\n--\nfs/erofs/Kconfig-70-\nfs/erofs/Kconfig:71:config EROFS_FS_POSIX_ACL\nfs/erofs/Kconfig-72-\tbool \"EROFS Access Control Lists\"\n--\nfs/erofs/Kconfig-84-\nfs/erofs/Kconfig:85:config EROFS_FS_SECURITY\nfs/erofs/Kconfig-86-\tbool \"EROFS Security Labels\"\n--\nfs/erofs/Kconfig-97-\nfs/erofs/Kconfig:98:config EROFS_FS_BACKED_BY_FILE\nfs/erofs/Kconfig-99-\tbool \"File-backed EROFS filesystem support\"\n--\nfs/erofs/Kconfig-111-\nfs/erofs/Kconfig:112:config EROFS_FS_ZIP\nfs/erofs/Kconfig-113-\tbool \"EROFS Data Compression Support\"\n--\nfs/erofs/Kconfig-121-\nfs/erofs/Kconfig:122:config EROFS_FS_ZIP_LZMA\nfs/erofs/Kconfig-123-\tbool \"EROFS LZMA compressed data support\"\n--\nfs/erofs/Kconfig-133-\nfs/erofs/Kconfig:134:config EROFS_FS_ZIP_LZMA_DEFAULT_MAX_STREAMS\nfs/erofs/Kconfig-135-\tint \"EROFS LZMA default maximum decompression streams\"\n--\nfs/erofs/Kconfig-147-\nfs/erofs/Kconfig:148:config EROFS_FS_ZIP_DEFLATE\nfs/erofs/Kconfig-149-\tbool \"EROFS DEFLATE compressed data support\"\n--\nfs/erofs/Kconfig-158-\nfs/erofs/Kconfig:159:config EROFS_FS_ZIP_ZSTD\nfs/erofs/Kconfig-160-\tbool \"EROFS Zstandard compressed data support\"\n--\nfs/erofs/Kconfig-169-\nfs/erofs/Kconfig:170:config EROFS_FS_ZIP_ACCEL\nfs/erofs/Kconfig-171-\tbool \"EROFS hardware decompression support\"\n--\nfs/erofs/Kconfig-183-\nfs/erofs/Kconfig:184:config EROFS_FS_PCPU_KTHREAD\nfs/erofs/Kconfig-185-\tbool \"EROFS per-cpu decompression kthread workers\"\n--\nfs/erofs/Kconfig-192-\nfs/erofs/Kconfig:193:config EROFS_FS_PCPU_KTHREAD_HIPRI\nfs/erofs/Kconfig-194-\tbool \"EROFS high priority per-CPU kthread workers\"\n--\nfs/erofs/Kconfig-202-\nfs/erofs/Kconfig:203:config EROFS_FS_PAGE_CACHE_SHARE\nfs/erofs/Kconfig-204-\tbool \"EROFS page cache share support (experimental)\"\n"
} |
| 5/2 | 2026/08/11 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6847 output: 34 thoughts: 21 Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts: **Checking `EROFS_FS_BACKED_BY_FILE` Dependencies** Alright, time to get my hands dirty. I need to understand the implications of `EROFS_FS_BACKED_BY_FILE`. Before I even touch anything, I need to understand the architecture. Let's start with a dependency analysis of `EROFS_FS_BACKED_BY_FILE`. What other configurations, modules, or features hinge on this one? Are there any upstream or downstream impacts I need to consider? What does this enable or disable, and how does that affect the overall system functionality? I'll probably start digging into the kernel's Kconfig system and cross-reference relevant source files to build a comprehensive picture. Understanding these dependencies is absolutely crucial before I can even begin to assess the stability and impact of this config option. I need to be sure that changing this doesn't accidentally break something else down the line. I'll need to know which features it enables and disables. I need a clear understanding of what it is connected to. Let's do this right. |
| 6/2 | 2026/08/11 07:30 | tool | read-file |
0mArgs:{
"File": "fs/erofs/Kconfig",
"FirstLine": 95,
"LineCount": 20
}Results: {
"Contents": " 95:\t\n 96:\t\t If you are not using a security module, say N.\n 97:\t\n 98:\tconfig EROFS_FS_BACKED_BY_FILE\n 99:\t\tbool \"File-backed EROFS filesystem support\"\n 100:\t\tdepends on EROFS_FS\n 101:\t\tdefault y\n 102:\t\thelp\n 103:\t\t This allows EROFS to use filesystem image files directly, without\n 104:\t\t the intercession of loopback block devices or likewise. It is\n 105:\t\t particularly useful for container images with numerous blobs and\n 106:\t\t other sandboxes, where loop devices behave intricately. It can also\n 107:\t\t be used to simplify error-prone lifetime management of unnecessary\n 108:\t\t virtual block devices.\n 109:\t\n 110:\t\t If you don't want to enable this feature, say N.\n 111:\t\n 112:\tconfig EROFS_FS_ZIP\n 113:\t\tbool \"EROFS Data Compression Support\"\n 114:\t\tdepends on EROFS_FS\n"
} |
| 7/2 | 2026/08/11 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7459 output: 30 thoughts: 98 Thoughts: Here's my thought process, as it stands: **Analyzing EROFS and Its File-Backed Configuration** Alright, so `EROFS_FS_BACKED_BY_FILE` is, as I suspected, contingent on the core `EROFS_FS` functionality. That makes sense, a file-backed system builds on the foundations of the basic EROFS structure. Now, I need to see what's changed or been added to accommodate this specific configuration. Let's list those functions: `erofs_fc_test_file_super`, `erofs_fc_get_tree_file`, and `get_tree_super`. The last one, `get_tree_super`, is particularly interesting because it's exported from `fs/super.c`. That hints at a potential integration point with the core VFS super block management. I'll shift my attention to `fs/super.c` and specifically examine the `get_tree_super` function. I need to understand how this function is being leveraged, or potentially extended, within this `EROFS_FS_BACKED_BY_FILE` context. That will likely provide insight into how the file-backed EROFS is mounting and integrating with the kernel's file system infrastructure. |
| 8/2 | 2026/08/11 07:30 | tool | grepper |
0mArgs:{
"Expression": "get_tree_super",
"PathPrefix": "fs/super.c"
}Results: {
"Output": "fs/super.c=1387=static int test_single_super(struct super_block *s, struct fs_context *fc)\n--\nfs/super.c-1392-/**\nfs/super.c:1393: * get_tree_super - Get a superblock, optionally sharing an existing one\nfs/super.c-1394- * @fc: The filesystem context holding the parameters\n--\nfs/super.c-1401- */\nfs/super.c:1402:int get_tree_super(struct fs_context *fc,\nfs/super.c-1403-\t\tint (*test)(struct super_block *, struct fs_context *),\n--\nfs/super.c-1428-}\nfs/super.c:1429:EXPORT_SYMBOL(get_tree_super);\nfs/super.c-1430-\nfs/super.c=1431=int get_tree_nodev(struct fs_context *fc,\n--\nfs/super.c-1434-{\nfs/super.c:1435:\treturn get_tree_super(fc, NULL, fill_super);\nfs/super.c-1436-}\n--\nfs/super.c=1439=int get_tree_single(struct fs_context *fc,\n--\nfs/super.c-1442-{\nfs/super.c:1443:\treturn get_tree_super(fc, test_single_super, fill_super);\nfs/super.c-1444-}\n--\nfs/super.c=1447=int get_tree_keyed(struct fs_context *fc,\n--\nfs/super.c-1452-\tfc-\u003es_fs_info = key;\nfs/super.c:1453:\treturn get_tree_super(fc, test_keyed_super, fill_super);\nfs/super.c-1454-}\n"
} |
| 9/2 | 2026/08/11 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8349 output: 38 thoughts: 109 Thoughts: **Analyzing EROFS Filesystem Mounting** Okay, so I'm looking at how the EROFS filesystem mounts, and I'm breaking down the key functions here. `get_tree_super` seems to be a general Virtual File System (VFS) function, nothing particularly EROFS-specific about that. My real interest lies in how EROFS hooks into this general framework. It's clear that `erofs_fc_test_file_super` and `erofs_fc_get_tree_file` are the EROFS-specific pieces that actually handle the file system's details. And `erofs_fc_get_tree` looks to be the main entry point – the starting point for mounting EROFS volumes. My next step will be to dive into `fs/erofs/super.c`, as that's where I expect to find these two EROFS-specific functions, `erofs_fc_test_file_super` and `erofs_fc_get_tree_file`, in detail. This will allow me to understand how these parts interact, particularly with the generic VFS components. |
| 10/2 | 2026/08/11 07:30 | tool | grepper |
0mArgs:{
"Expression": "erofs_fc_test_file_super",
"PathPrefix": "fs/erofs/super.c"
}Results: {
"Output": "fs/erofs/super.c=643=static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)\n--\nfs/erofs/super.c-799-\nfs/erofs/super.c:800:static int erofs_fc_test_file_super(struct super_block *sb,\nfs/erofs/super.c-801-\t\t\t\t struct fs_context *fc)\n--\nfs/erofs/super.c=820=static int erofs_fc_get_tree_file(struct fs_context *fc)\n--\nfs/erofs/super.c-826-\nfs/erofs/super.c:827:\treturn get_tree_super(fc, erofs_fc_test_file_super, erofs_fc_fill_super);\nfs/erofs/super.c-828-}\n"
} |
| 11/2 | 2026/08/11 07:30 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8974 output: 174 thoughts: 245 Thoughts: **My Assessment of the EROFS Filesystem Patch** Okay, let's break this down. From what I see, this patch is introducing a `superblock_share` mount option for the EROFS filesystem. This is a pretty significant change, allowing for shared superblocks when the same backing file is mounted multiple times, which is a neat optimization, if it works correctly. The critical functions this patch touches are `erofs_fc_test_file_super`, `erofs_fc_get_tree_file`, and `get_tree_super`. These are the key areas where the new logic is integrated into the core mount process of the EROFS filesystem. I need to keep those in mind. Now, as far as configuration, it looks like we're dealing with `EROFS_FS` and `EROFS_FS_BACKED_BY_FILE`. I'll quickly double-check the dependencies. Yup, `EROFS_FS_BACKED_BY_FILE` depends on `EROFS_FS`, but nothing else seems relevant. Those two should cover what's needed to test the core functionality this patch introduces. I don't see any other required configurations at a glance, so those seem sufficient. From a testing perspective, the rationale is clear. This is a functional change in the filesystem mount path, and it’s reachable, meaning it will be triggered when mounting. Given that it's altering the fundamental mount process, this definitely warrants fuzzing to ensure robustness. The new mount option and related logic could potentially introduce subtle bugs or unexpected behavior. |
| 12/2 | 2026/08/11 07:30 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_BACKED_BY_FILE"
],
"FocusSymbols": [
"erofs_fc_test_file_super",
"erofs_fc_get_tree_file",
"get_tree_super"
],
"Reasoning": "The patch introduces a new mount option `superblock_share` for the EROFS filesystem, allowing superblock sharing when the same backing file is mounted multiple times. This modifies the core mount logic for EROFS and introduces new functions like `erofs_fc_test_file_super` and `erofs_fc_get_tree_file`. This is a functional change in the filesystem mount path that is reachable and worth fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"EROFS_FS",
"EROFS_FS_BACKED_BY_FILE"
],
"FocusSymbols": [
"erofs_fc_test_file_super",
"erofs_fc_get_tree_file",
"get_tree_super"
],
"Reasoning": "The patch introduces a new mount option `superblock_share` for the EROFS filesystem, allowing superblock sharing when the same backing file is mounted multiple times. This modifies the core mount logic for EROFS and introduces new functions like `erofs_fc_test_file_super` and `erofs_fc_get_tree_file`. This is a functional change in the filesystem mount path that is reachable and worth fuzzing.",
"WorthFuzzing": true
} |
| 13/1 | 2026/08/11 07:30 | action | If |
1mArgs:{
"WorthFuzzing": true
}Results: null |
| 14/2 | 2026/08/11 07:30 | agent | kmsan-evaluator |
1mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch introduces a new mount option `superblock_share` for EROFS file-backed mounts and adds logic to share superblocks when compatible options are used. The comparison function `erofs_fc_test_file_super` uses `memcmp` to compare `struct erofs_mount_opts`. While this struct contains padding, instances of it are embedded within `struct erofs_sb_info`, which is always allocated via `kzalloc` (in `erofs_init_fs_context`). Consequently, the padding is initialized to zero and remains zero, meaning `memcmp` safely reads initialized memory without triggering KMSAN or causing undefined behavior.\n\nThe patch does not introduce any structures that are copied to user space, nor does it allocate complex data structures where uninitialized fields could be read later. There is no conditional logic depending on uninitialized variables. Any potential bugs introduced by these changes (such as use-after-free, memory leaks, or locking issues related to superblock sharing) would be effectively caught by KASAN, LOCKDEP, and other standard bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Instruction: You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.
CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
a comprehensive suite of debugging tools and sanitizers, including KASAN
(out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
(locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
or page allocations) and kernel-to-user memory info-leaks.
Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.
Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.
Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
attributes, ioctl output arguments, socket options, or BPF buffers) where fields
or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
uninitialized bytes of existing buffers.
Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
dereferences, locking deadlocks, or use-after-free bugs (these are already caught
by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.
Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: For your convenience, here is the diff of the changes:
commit f1fe446af35de9317fc46222ef240df0698e703a
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 11 07:30:25 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/filesystems/erofs.rst b/Documentation/filesystems/erofs.rst
index d301d9ac946a9..49c4e7dbc5d61 100644
--- a/Documentation/filesystems/erofs.rst
+++ b/Documentation/filesystems/erofs.rst
@@ -139,6 +139,9 @@ inode_share Enable inode page sharing for this filesystem. Inodes wi
page cache.
source=%s (For file-backed mounts) Specify the backing image as a path
or as an already-opened file descriptor.
+superblock_share (For file-backed mounts) Share the superblock when the same
+ backing file is mounted more than once with compatible
+ options. Remount is not allowed on shared superblocks.
=================== =========================================================
File-backed mounts
@@ -156,6 +159,11 @@ Only regular files are accepted as backing files; to mount an image that
resides on a block device, use the traditional block device mount path
instead.
+When superblock_share is specified and the same backing file (identified
+by inode and fsoffset) is mounted more than once with compatible mount
+options, the kernel reuses the existing superblock instead of creating a
+new one. Remount is not allowed on shared superblocks.
+
Sysfs Entries
=============
diff --git a/fs/erofs/internal.h b/fs/erofs/internal.h
index 57bd21859c65d..86da219f8f4ec 100644
--- a/fs/erofs/internal.h
+++ b/fs/erofs/internal.h
@@ -156,6 +156,7 @@ struct erofs_sb_info {
#define EROFS_MOUNT_DAX_NEVER 0x00000080
#define EROFS_MOUNT_DIRECT_IO 0x00000100
#define EROFS_MOUNT_INODE_SHARE 0x00000200
+#define EROFS_MOUNT_SUPERBLOCK_SHARE 0x00000400
#define clear_opt(opt, option) ((opt)->mount_opt &= ~EROFS_MOUNT_##option)
#define set_opt(opt, option) ((opt)->mount_opt |= EROFS_MOUNT_##option)
diff --git a/fs/erofs/super.c b/fs/erofs/super.c
index ddc9719e6d18e..457e067447d5f 100644
--- a/fs/erofs/super.c
+++ b/fs/erofs/super.c
@@ -386,7 +386,7 @@ static void erofs_default_options(struct erofs_sb_info *sbi)
enum {
Opt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,
Opt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,
- Opt_source,
+ Opt_source, Opt_superblock_share,
};
static const struct constant_table erofs_param_cache_strategy[] = {
@@ -415,6 +415,7 @@ static const struct fs_parameter_spec erofs_fs_parameters[] = {
fsparam_u64("fsoffset", Opt_fsoffset),
fsparam_flag("inode_share", Opt_inode_share),
fsparam_file_or_string("source", Opt_source),
+ fsparam_flag("superblock_share", Opt_superblock_share),
{}
};
@@ -560,6 +561,12 @@ static int erofs_fc_parse_param(struct fs_context *fc,
else
set_opt(&sbi->opt, INODE_SHARE);
break;
+ case Opt_superblock_share:
+ if (!IS_ENABLED(CONFIG_EROFS_FS_BACKED_BY_FILE))
+ errorfc(fc, "%s option not supported", erofs_fs_parameters[opt].name);
+ else
+ set_opt(&sbi->opt, SUPERBLOCK_SHARE);
+ break;
case Opt_source:
return erofs_fc_parse_source(fc, param);
}
@@ -652,6 +659,17 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
errorfc(fc, "FSDAX is not allowed when inode_share is on");
return -EINVAL;
}
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE) &&
+ test_opt(&sbi->opt, INODE_SHARE)) {
+ errorfc(fc, "superblock_share is not allowed when inode_share is on");
+ return -EINVAL;
+ }
+ /* Extra devices are not yet supported with superblock sharing. */
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE) &&
+ sbi->devs->extra_devices) {
+ errorfc(fc, "superblock_share does not support extra devices");
+ return -EINVAL;
+ }
sbi->blkszbits = PAGE_SHIFT;
if (!sb->s_bdev) {
@@ -779,6 +797,36 @@ static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)
return 0;
}
+static int erofs_fc_test_file_super(struct super_block *sb,
+ struct fs_context *fc)
+{
+ /*
+ * The assumption is that the function is used only with a sbi that
+ * has SUPERBLOCK_SHARE set, so that the memcmp later ensures
+ * new_sbi also is shareable.
+ */
+ struct erofs_sb_info *sbi = EROFS_SB(sb);
+ struct erofs_sb_info *new_sbi = fc->s_fs_info;
+
+ if (sb->s_iflags & SB_I_RETIRED)
+ return 0;
+ if (!sbi->dif0.file || !new_sbi->dif0.file)
+ return 0;
+ return file_inode(sbi->dif0.file) == file_inode(new_sbi->dif0.file) &&
+ sbi->dif0.fsoff == new_sbi->dif0.fsoff &&
+ !memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt));
+}
+
+static int erofs_fc_get_tree_file(struct fs_context *fc)
+{
+ struct erofs_sb_info *sbi = fc->s_fs_info;
+
+ if (!test_opt(&sbi->opt, SUPERBLOCK_SHARE))
+ return get_tree_nodev(fc, erofs_fc_fill_super);
+
+ return get_tree_super(fc, erofs_fc_test_file_super, erofs_fc_fill_super);
+}
+
static int erofs_fc_get_tree(struct fs_context *fc)
{
struct erofs_sb_info *sbi = fc->s_fs_info;
@@ -794,7 +842,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)
errorfc(fc, "source is unsupported");
return -EINVAL;
}
- return get_tree_nodev(fc, erofs_fc_fill_super);
+ return erofs_fc_get_tree_file(fc);
}
ret = get_tree_bdev_flags(fc, erofs_fc_fill_super,
@@ -812,7 +860,7 @@ static int erofs_fc_get_tree(struct fs_context *fc)
if (S_ISREG(file_inode(sbi->dif0.file)->i_mode) &&
sbi->dif0.file->f_mapping->a_ops->read_folio)
- return get_tree_nodev(fc, erofs_fc_fill_super);
+ return erofs_fc_get_tree_file(fc);
}
return ret;
}
@@ -825,6 +873,10 @@ static int erofs_fc_reconfigure(struct fs_context *fc)
DBG_BUGON(!sb_rdonly(sb));
+ /* Shared superblocks must not be reconfigured. */
+ if (test_opt(&sbi->opt, SUPERBLOCK_SHARE))
+ return -EBUSY;
+
if (new_sbi->domain_id)
erofs_info(sb, "ignoring reconfiguration for domain_id.");
diff --git a/fs/super.c b/fs/super.c
index 5feecf5d9038d..71cd2dbade5b3 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1389,7 +1389,17 @@ static int test_single_super(struct super_block *s, struct fs_context *fc)
return 1;
}
-static int vfs_get_super(struct fs_context *fc,
+/**
+ * get_tree_super - Get a superblock, optionally sharing an existing one
+ * @fc: The filesystem context holding the parameters
+ * @test: Comparison function to find a matching existing superblock, or NULL
+ * @fill_super: Helper to initialise a new superblock
+ *
+ * If @test is non-NULL and matches an existing superblock, that superblock is
+ * reused; otherwise a new anonymous superblock is created and initialised with
+ * @fill_super. Passing NULL for @test always creates a new superblock.
+ */
+int get_tree_super(struct fs_context *fc,
int (*test)(struct super_block *, struct fs_context *),
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
@@ -1416,12 +1426,13 @@ static int vfs_get_super(struct fs_context *fc,
deactivate_locked_super(sb);
return err;
}
+EXPORT_SYMBOL(get_tree_super);
int get_tree_nodev(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
{
- return vfs_get_super(fc, NULL, fill_super);
+ return get_tree_super(fc, NULL, fill_super);
}
EXPORT_SYMBOL(get_tree_nodev);
@@ -1429,7 +1440,7 @@ int get_tree_single(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc))
{
- return vfs_get_super(fc, test_single_super, fill_super);
+ return get_tree_super(fc, test_single_super, fill_super);
}
EXPORT_SYMBOL(get_tree_single);
@@ -1439,7 +1450,7 @@ int get_tree_keyed(struct fs_context *fc,
void *key)
{
fc->s_fs_info = key;
- return vfs_get_super(fc, test_keyed_super, fill_super);
+ return get_tree_super(fc, test_keyed_super, fill_super);
}
EXPORT_SYMBOL(get_tree_keyed);
diff --git a/include/linux/fs_context.h b/include/linux/fs_context.h
index 0d6c8a6d7be21..c920aba5177c3 100644
--- a/include/linux/fs_context.h
+++ b/include/linux/fs_context.h
@@ -150,6 +150,10 @@ extern int vfs_parse_fs_param_source(struct fs_context *fc,
struct fs_parameter *param);
extern void fc_drop_locked(struct fs_context *fc);
+extern int get_tree_super(struct fs_context *fc,
+ int (*test)(struct super_block *, struct fs_context *),
+ int (*fill_super)(struct super_block *sb,
+ struct fs_context *fc));
extern int get_tree_nodev(struct fs_context *fc,
int (*fill_super)(struct super_block *sb,
struct fs_context *fc));
|
| 15/3 | 2026/08/11 07:30 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5043 output: 22 thoughts: 227 Thoughts: **EROFS Mount Option Concern** Okay, so this patch introduces a new mount option, `superblock_share`, for EROFS file-backed mounts. It seems straightforward enough: it modifies `fs/erofs/super.c` to handle the parsing of this new option and, crucially, to switch from using `get_tree_nodev()` to `get_tree_super()` when the option is enabled. The patch also exposes `get_tree_super()` from `fs/super.c`, renaming the existing `vfs_get_super` to `get_tree_super` in the process. I see that the `erofs_fc_test_file_super` function now compares the `sbi->opt` and `new_sbi->opt` structures using `memcmp`. *Wait a second...* `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))`? That comparison's got my attention. My initial concern is whether or not `sbi->opt` is fully initialized, and if there's any padding within that structure. A byte-wise comparison like that is always a bit risky if you're not absolutely certain about the contents. I need to take a look at the definition of `struct erofs_mount_opts` in `fs/erofs/internal.h` to make sure there aren't any potential gotchas there. I'll need to confirm that everything in `sbi->opt` is initialized to a reliable value and that there isn't any undefined padding that could lead to unexpected behavior during the `memcmp`. |
| 16/3 | 2026/08/11 07:30 | tool | grepper |
0mArgs:{
"Expression": "struct erofs_mount_opts \\{"
}Results: {
"Output": "fs/erofs/internal.h=54=enum {\n--\nfs/erofs/internal.h-59-\nfs/erofs/internal.h:60:struct erofs_mount_opts {\nfs/erofs/internal.h-61-\t/* current strategy of how to use managed cache */\n"
} |
| 17/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5727 output: 35 thoughts: 20 Thoughts: Here's a summary of my thought process, as requested: **Diving into `erofs/internal.h`** Okay, I need to get a better handle on how EROFS is structured internally. I'm going to jump into the `fs/erofs/internal.h` file. Specifically, I think looking around line 60 might give me a good starting point. I'm hoping to understand some of the core data structures or definitions there that are critical to the file system's operation. I'll be looking for clues about the underlying principles, perhaps focusing on the critical datatypes. Understanding this header file, especially near that line, will likely give me insights into how the EROFS layer interacts with other kernel components and, ultimately, how it manages its read-only nature effectively. |
| 18/3 | 2026/08/11 07:31 | tool | read-file |
0mArgs:{
"File": "fs/erofs/internal.h",
"FirstLine": 54,
"LineCount": 20
}Results: {
"Contents": " 54:\tenum {\n 55:\t\tEROFS_SYNC_DECOMPRESS_AUTO,\n 56:\t\tEROFS_SYNC_DECOMPRESS_FORCE_ON,\n 57:\t\tEROFS_SYNC_DECOMPRESS_FORCE_OFF\n 58:\t};\n 59:\t\n 60:\tstruct erofs_mount_opts {\n 61:\t\t/* current strategy of how to use managed cache */\n 62:\t\tunsigned char cache_strategy;\n 63:\t\tunsigned int mount_opt;\n 64:\t};\n 65:\t\n 66:\tstruct erofs_dev_context {\n 67:\t\tstruct idr tree;\n 68:\t\tstruct rw_semaphore rwsem;\n 69:\t\n 70:\t\tunsigned int extra_devices;\n 71:\t\tbool flatdev;\n 72:\t};\n 73:\t\n"
} |
| 19/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6188 output: 33 thoughts: 136 Thoughts: **Padding Woes in `erofs_mount_opts`** Okay, so I'm looking at the `struct erofs_mount_opts` definition and immediately I see something that's making my internal compiler whimper. An `unsigned char` followed by an `unsigned int`? That screams padding to me. Let's see... a byte followed by four bytes. That's three bytes wasted! We're talking about a structure that's probably frequently accessed, so this could translate to a performance hit, even if minor, in some scenarios, right? Alright, before I dive too deep into optimization considerations, I want to confirm something. I need to make sure `sbi->opt` is initialized to zero when it's allocated. That will influence how I think about this potential padding issue. Let me quickly jump over to `erofs_fc_get_tree` and `erofs_init_fs_context` because I'm pretty sure that's where `sbi` is allocated. Let's just double-check where that allocation happens and verify the initialization. |
| 20/3 | 2026/08/11 07:31 | tool | grepper |
0mArgs:{
"Expression": "fc-\u003es_fs_info =",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/erofs/super.c=951=static int erofs_init_fs_context(struct fs_context *fc)\n--\nfs/erofs/super.c-963-\t}\nfs/erofs/super.c:964:\tfc-\u003es_fs_info = sbi;\nfs/erofs/super.c-965-\n"
} |
| 21/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6691 output: 36 thoughts: 25 Thoughts: Here's my take on those thoughts, tailored for an expert audience: **Diving into the EROFS Filesystem Context Initialization** Okay, time to get my hands dirty. Let's dig into the `erofs_init_fs_context` function. I want to understand exactly how the filesystem context for EROFS is set up – specifically, what parameters are being handled, any checks or validations, and how this context ultimately influences the mount process. I'm assuming this is the core of establishing the environment for an EROFS filesystem instance, so a solid understanding here is crucial for troubleshooting or optimization later. Time to navigate `fs/erofs/super.c`. Let's see what's what. |
| 22/3 | 2026/08/11 07:31 | tool | read-file |
0mArgs:{
"File": "fs/erofs/super.c",
"FirstLine": 951,
"LineCount": 20
}Results: {
"Contents": " 951:\tstatic int erofs_init_fs_context(struct fs_context *fc)\n 952:\t{\n 953:\t\tstruct erofs_sb_info *sbi;\n 954:\t\n 955:\t\tsbi = kzalloc_obj(*sbi);\n 956:\t\tif (!sbi)\n 957:\t\t\treturn -ENOMEM;\n 958:\t\n 959:\t\tsbi-\u003edevs = kzalloc_obj(struct erofs_dev_context);\n 960:\t\tif (!sbi-\u003edevs) {\n 961:\t\t\tkfree(sbi);\n 962:\t\t\treturn -ENOMEM;\n 963:\t\t}\n 964:\t\tfc-\u003es_fs_info = sbi;\n 965:\t\n 966:\t\tidr_init(\u0026sbi-\u003edevs-\u003etree);\n 967:\t\tinit_rwsem(\u0026sbi-\u003edevs-\u003erwsem);\n 968:\t\terofs_default_options(sbi);\n 969:\t\tfc-\u003eops = \u0026erofs_context_ops;\n 970:\t\treturn 0;\n"
} |
| 23/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7215 output: 27 thoughts: 143 Thoughts: **My Thought Process on `sbi` Allocation and `sbi->opt` Initialization** Okay, so `sbi` is allocated using `kzalloc_obj(*sbi)`. Right off the bat, my intuition tells me that `kzalloc_obj` is likely a wrapper function for `kzalloc`, specifically to allocate memory for the size of the structure `sbi` and initializes it to zero. But wait, `kzalloc_obj`? I should probably double-check that definition to be absolutely sure. Yep, just as I suspected, it's just a direct call to `kzalloc(sizeof(*sbi), GFP_KERNEL)` or something nearly identical. That confirms my initial assumption: since `kzalloc` is used, the padding within `sbi` *should* be initialized to zero. That's good, predictable behavior. But this brings up another important point: what happens if `sbi->opt` is subsequently *modified*? Specifically, what if it's copied or assigned a value? The initial zeroing by `kzalloc` is only guaranteed at allocation, and any later assignment could overwrite that zero value. So, the next thing I need to do is thoroughly check the code. I need to make sure that `sbi->opt` is *never* assigned by value anywhere else in the code. I need to know where and how the `sbi->opt` member is updated. This will determine if I can trust that the padding remains zeroed throughout the lifetime of the `sbi` structure. |
| 24/3 | 2026/08/11 07:31 | tool | grepper |
0mArgs:{
"Expression": "opt =",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/erofs/super.c=477=static int erofs_fc_parse_param(struct fs_context *fc,\n--\nfs/erofs/super.c-484-\nfs/erofs/super.c:485:\topt = fs_parse(fc, erofs_fs_parameters, param, \u0026result);\nfs/erofs/super.c-486-\tif (opt \u003c 0)\n--\nfs/erofs/super.c=868=static int erofs_fc_reconfigure(struct fs_context *fc)\n--\nfs/erofs/super.c-887-\nfs/erofs/super.c:888:\tsbi-\u003eopt = new_sbi-\u003eopt;\nfs/erofs/super.c-889-\n--\nfs/erofs/super.c=1136=static int erofs_show_options(struct seq_file *seq, struct dentry *root)\n--\nfs/erofs/super.c-1138-\tstruct erofs_sb_info *sbi = EROFS_SB(root-\u003ed_sb);\nfs/erofs/super.c:1139:\tstruct erofs_mount_opts *opt = \u0026sbi-\u003eopt;\nfs/erofs/super.c-1140-\n"
} |
| 25/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8044 output: 31 thoughts: 260 Thoughts: **Analyzing the EROFS Filesystem Reconfiguration** Okay, so I'm looking at this `erofs_fc_reconfigure` function and immediately I see this line: `sbi->opt = new_sbi->opt;`. That jumps out at me. I'm thinking, hang on... `new_sbi` is allocated using `erofs_init_fs_context` which calls `kzalloc`. Right, so `kzalloc` zeroes out the memory. Then, the parameters are parsed into `new_sbi->opt`. Now, that assignment... it's a struct assignment, isn't it? Right, the key question is how C handles struct assignment. It copies the *fields*, but what about padding? That's the potential gotcha. It's often implemented as a `memcpy` if the struct is large, or even just field-by-field or word-by-word. Now, if it's *word*-by-word, then the padding *is* copied! But hold on... `new_sbi` was allocated with `kzalloc`, so all the padding *in* `new_sbi` is zeroed out initially. So, if `new_sbi->opt` has zero padding to begin with, and it's assigned to `sbi->opt`, then `sbi->opt`'s padding should *remain* zero, or become zero, which is likely the desired behavior. I'd need to confirm the compiler's behavior, but it's likely safe. Just to be thorough, I should look at all the places `opt` is modified. I'm going to quickly check `erofs_default_options` to ensure that there aren't any potential issues with padding being set there. |
| 26/3 | 2026/08/11 07:31 | tool | grepper |
0mArgs:{
"Expression": "erofs_default_options",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/erofs/super.c=250=static int erofs_read_superblock(struct super_block *sb)\n--\nfs/erofs/super.c-373-\nfs/erofs/super.c:374:static void erofs_default_options(struct erofs_sb_info *sbi)\nfs/erofs/super.c-375-{\n--\nfs/erofs/super.c=951=static int erofs_init_fs_context(struct fs_context *fc)\n--\nfs/erofs/super.c-967-\tinit_rwsem(\u0026sbi-\u003edevs-\u003erwsem);\nfs/erofs/super.c:968:\terofs_default_options(sbi);\nfs/erofs/super.c-969-\tfc-\u003eops = \u0026erofs_context_ops;\n"
} |
| 27/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8941 output: 36 thoughts: 12 Thoughts: Okay, let's dive into this. Here's my take on those thoughts, as if they're my own: **Analyzing `erofs_default_options`** Alright, I'm going to start by getting a handle on the `erofs_default_options`. Let's see what's being set as defaults for EROFS. I'm especially interested in things like compression settings, any specific block sizes, and any unusual flags that might be influencing performance or behavior. I'll need to remember to cross-reference this with the documentation to see if these defaults are inline with best practices for different use cases, and how they relate to the underlying hardware the filesystem might be used on. I'll also want to watch out for anything that screams "legacy" - might be a sign we should be considering changes to maintain optimal performance. Let's see what this reveals. |
| 28/3 | 2026/08/11 07:31 | tool | read-file |
0mArgs:{
"File": "fs/erofs/super.c",
"FirstLine": 374,
"LineCount": 15
}Results: {
"Contents": " 374:\tstatic void erofs_default_options(struct erofs_sb_info *sbi)\n 375:\t{\n 376:\t#ifdef CONFIG_EROFS_FS_ZIP\n 377:\t\tsbi-\u003eopt.cache_strategy = EROFS_ZIP_CACHE_READAROUND;\n 378:\t\tsbi-\u003esync_decompress = EROFS_SYNC_DECOMPRESS_AUTO;\n 379:\t#endif\n 380:\t\tif (IS_ENABLED(CONFIG_EROFS_FS_XATTR))\n 381:\t\t\tset_opt(\u0026sbi-\u003eopt, XATTR_USER);\n 382:\t\tif (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))\n 383:\t\t\tset_opt(\u0026sbi-\u003eopt, POSIX_ACL);\n 384:\t}\n 385:\t\n 386:\tenum {\n 387:\t\tOpt_user_xattr, Opt_acl, Opt_cache_strategy, Opt_dax, Opt_dax_enum,\n 388:\t\tOpt_device, Opt_domain_id, Opt_directio, Opt_fsoffset, Opt_inode_share,\n"
} |
| 29/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9489 output: 35 thoughts: 780 Thoughts: **Analyzing Potential KMSAN Issues with EROFS Mount Options**
Okay, let's break this down. My initial thought was about how `sbi->opt` is handled. I noticed that it's modified field by field, like with the cache strategy, and also through `set_opt`. My first instinct was that this wouldn't leave uninitialized padding because `sbi` is allocated using `kzalloc`. That's important.
The patch introduces `erofs_fc_test_file_super`. The key assumption here seems to be about shared superblocks, which is relevant, but what really grabs my attention is this `memcmp` on `&sbi->opt`. That's where the KMSAN question arises.
My concern is whether `sbi->opt` could contain uninitialized padding. If it's `kzalloc`'d, the padding *should* be zeroed, right? But what if `sbi->opt` is later assigned as a whole using `sbi->opt = new_sbi->opt;`? C struct assignment, in some cases, *might* not copy padding. If it doesn't, that padding could retain its original, uninitialized state. Then, when `memcmp` is called, KMSAN could potentially flag that as a problem. Although, if the original `sbi` was initialized via `kzalloc`, then padding would have been initialized to zero to begin with.
Wait, the next thing I thought about was if `sbi->opt` was passed by value anywhere. But no, it's just a field within the `sbi` struct, so that's not a concern.
Then it hits me: *yes*, `memcmp` on a struct with padding *could* trigger KMSAN! KMSAN is very granular; it tracks initialization at the bit/byte level. If padding exists and isn't initialized, a `memcmp` operation could read uninitialized memory, even if the user isn't directly accessing it. However, if the struct was initialized via `kzalloc`, the padding is already initialized to zero, making this a non-issue.
So the core question is, if the struct is copied via assignment, does the compiler read the padding? If the assignment is implemented as a word-sized read/write, it *will* read the padding. But, because that padding was zeroed by the `kzalloc` call, the read is safe.
Now, KMSAN checks for memory that is uninitialized. If the memory was zeroed by `kzalloc`, it is initialized.
But wait, what if `sbi->opt` isn't fully initialized *somewhere*? I need to look at the `struct erofs_mount_opts` definition to be sure. Let's see...
```c
struct erofs_mount_opts {
/* current strategy of how to use managed cache */
unsigned char cache_strategy;
unsigned int mount_opt;
};
```
Okay, it looks like there aren't any other fields. The `kzalloc` initialization of `sbi` should fully initialize this structure.
|
| 30/3 | 2026/08/11 07:31 | tool | read-file |
0mArgs:{
"File": "fs/erofs/internal.h",
"FirstLine": 60,
"LineCount": 20
}Results: {
"Contents": " 60:\tstruct erofs_mount_opts {\n 61:\t\t/* current strategy of how to use managed cache */\n 62:\t\tunsigned char cache_strategy;\n 63:\t\tunsigned int mount_opt;\n 64:\t};\n 65:\t\n 66:\tstruct erofs_dev_context {\n 67:\t\tstruct idr tree;\n 68:\t\tstruct rw_semaphore rwsem;\n 69:\t\n 70:\t\tunsigned int extra_devices;\n 71:\t\tbool flatdev;\n 72:\t};\n 73:\t\n 74:\t/* all filesystem-wide lz4 configurations */\n 75:\tstruct erofs_sb_lz4_info {\n 76:\t\t/* # of pages needed for EROFS lz4 rolling decompression */\n 77:\t\tu16 max_distance_pages;\n 78:\t\t/* maximum possible blocks for pclusters in the filesystem */\n 79:\t\tu16 max_pclusterblks;\n"
} |
| 31/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11235 output: 33 thoughts: 157 Thoughts: **Analyzing the erofs_mount_opts Structure** Okay, so I'm focusing on these two fields. Right, the `struct erofs_mount_opts` is only 8 bytes total, broken down as a single character, some padding, and then an integer. Now, I'm thinking about this `memcmp` call: `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))`. It's comparing 8 bytes. Since both `sbi` and `new_sbi` are allocated with `kzalloc`, the padding should be zeroed out. So, `memcmp` is reading initialized memory, which is good. That makes sense. But I need to be absolutely certain. Is there any scenario where `sbi->opt` *isn't* allocated via `kzalloc`, or maybe gets placed on the stack? I need to track down where `erofs_sb_info` is actually allocated to confirm there's no way to end up with uninitialized memory. Let's see... I'll check where the `erofs_sb_info` is allocated. |
| 32/3 | 2026/08/11 07:31 | tool | grepper |
0mError:no matches Args: {
"Expression": "alloc.*erofs_sb_info",
"PathPrefix": "fs/erofs/"
}Results: {
"error": "no matches"
} |
| 33/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11685 output: 30 thoughts: 17 Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective: **Diving into EROFS Superblock Information** Alright, time to get down to the nitty-gritty. Let's start by scrutinizing the allocation of memory for the `erofs_sb_info` structure using `kzalloc`. I want to immediately understand how the superblock information for the EROFS filesystem is being initialized. Specifically, I'm thinking about the implications of using `kzalloc` – zeroing the memory – right from the outset. This could be crucial for ensuring a clean slate, particularly when dealing with potentially sensitive data or pointers that need to be initialized to a known state. Knowing that this is happening allows me to infer certain things about how the EROFS driver is designed, what assumptions it makes about the state of the data, and how it handles potential garbage values. I'm going to look deeper. |
| 34/3 | 2026/08/11 07:31 | tool | grepper |
0mArgs:{
"Expression": "kzalloc.*sbi",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/erofs/super.c=951=static int erofs_init_fs_context(struct fs_context *fc)\n--\nfs/erofs/super.c-954-\nfs/erofs/super.c:955:\tsbi = kzalloc_obj(*sbi);\nfs/erofs/super.c-956-\tif (!sbi)\n--\nfs/erofs/xattr.c=494=int erofs_xattr_prefixes_init(struct super_block *sb)\n--\nfs/erofs/xattr.c-505-\nfs/erofs/xattr.c:506:\tpfs = kzalloc_objs(*pfs, sbi-\u003exattr_prefix_count);\nfs/erofs/xattr.c-507-\tif (!pfs)\n"
} |
| 35/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12144 output: 30 thoughts: 12 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing `kzalloc_obj`** Okay, so I'm looking at this code and immediately something pops out. `kzalloc_obj`... wait, that's a macro. Right. I need to remember exactly what that expands to in this context. Is it being used correctly here? Is there a subtle performance penalty or, worse, a potential memory leak if it's not handled with care? I need to quickly recall the typical usage patterns of this macro to assess its appropriateness here. Is there any obvious way this could lead to fragmentation, or inefficient zeroing? Time to dig a little deeper. |
| 36/3 | 2026/08/11 07:31 | tool | grepper |
0mError:no matches Args: {
"Expression": "define kzalloc_obj",
"PathPrefix": "fs/erofs/"
}Results: {
"error": "no matches"
} |
| 37/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12346 output: 19 thoughts: 14 Thoughts: Here's my thought process, as if I were thinking it: **Tracing `kzalloc_obj`** Okay, so I need to understand this `kzalloc_obj` function better. The first logical step is to pinpoint its definition. Where is it actually declared and implemented within this codebase? I need to see the function signature, any documentation comments if they exist, and most importantly, the actual code within it. That's the only way to get a solid grasp of its purpose and how it operates at a fundamental level. I'll need to use my usual tools to track it down. |
| 38/3 | 2026/08/11 07:31 | tool | grepper |
0mArgs:{
"Expression": "define kzalloc_obj"
}Results: {
"Output": "include/linux/slab.h=1073=void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node);\n--\ninclude/linux/slab.h-1154-/* All kzalloc aliases for kmalloc_(obj|objs|flex). */\ninclude/linux/slab.h:1155:#define kzalloc_obj(P, ...) \\\ninclude/linux/slab.h-1156-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\ninclude/linux/slab.h:1157:#define kzalloc_objs(P, COUNT, ...) \\\ninclude/linux/slab.h-1158-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT)\n--\ntools/include/linux/slab.h=200=static inline unsigned int kmem_cache_sheaf_size(struct slab_sheaf *sheaf)\n--\ntools/include/linux/slab.h-210-\ntools/include/linux/slab.h:211:#define kzalloc_obj(P, ...) \\\ntools/include/linux/slab.h-212-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\n"
} |
| 39/3 | 2026/08/11 07:31 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12840 output: 28 thoughts: 331 Thoughts: **Analyzing the Zero-Initialization of `sbi->opt` in EROFS** Okay, let's break this down. So, `kzalloc_obj(*sbi)` allocates memory for `sbi` and, importantly, zeroes it. This means `sbi->opt`, which is part of that `sbi` struct, is initialized to all zeros. The question is, where else might `sbi->opt` be modified? I remember seeing `erofs_fc_parse_param` and `erofs_fc_reconfigure`. `erofs_fc_reconfigure` is where it seems like `sbi->opt` gets directly updated: `sbi->opt = new_sbi->opt;`. Crucially, `new_sbi->opt` is initialized as zero, then fields are set. So, the padding within `new_sbi->opt` remains zero. The assignment `sbi->opt = new_sbi->opt;` is a struct copy. Even if the compiler optimizes this with 64-bit reads/writes, it's copying initialized memory (zeroes in the padding) to the destination. Therefore, the padding in `sbi->opt` should also be zero after the copy. That's critical because then the `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))` compares all 8 bytes. Since the padding is zero in both, the `memcmp` will compare the fields' values, and the padding bytes should match. Now, wait a minute, I need to cover my bases. Is there any scenario where `sbi->opt` *could* end up with uninitialized padding? Let's rule out some things. Could `sbi->opt` be copied from user space? No, it's parsed from the mount options, so the initialization should be controlled. Could some other function modify `sbi->opt`? I need to be certain about that. Time to do a quick search for all occurrences of `opt` in the `fs/erofs/` directory to be absolutely sure. |
| 40/3 | 2026/08/11 07:32 | tool | grepper |
0mArgs:{
"Expression": "\\-\u003eopt",
"PathPrefix": "fs/erofs/"
}Results: {
"Output": "fs/erofs/fileio.c=44=static void erofs_fileio_rq_submit(struct erofs_fileio_rq *rq)\n--\nfs/erofs/fileio.c-53-\trq-\u003eiocb.ki_complete = erofs_fileio_ki_complete;\nfs/erofs/fileio.c:54:\tif (test_opt(\u0026EROFS_SB(rq-\u003esb)-\u003eopt, DIRECT_IO) \u0026\u0026\nfs/erofs/fileio.c-55-\t rq-\u003eiocb.ki_filp-\u003ef_mode \u0026 FMODE_CAN_ODIRECT)\n--\nfs/erofs/inode.c=36=static int erofs_read_inode(struct inode *inode)\n--\nfs/erofs/inode.c-214-\tinode-\u003ei_flags \u0026= ~S_DAX;\nfs/erofs/inode.c:215:\tif (test_opt(\u0026sbi-\u003eopt, DAX_ALWAYS) \u0026\u0026 S_ISREG(inode-\u003ei_mode) \u0026\u0026\nfs/erofs/inode.c-216-\t (vi-\u003edatalayout == EROFS_INODE_FLAT_PLAIN ||\n--\nfs/erofs/super.c=125=static int erofs_init_device(struct erofs_buf *buf, struct super_block *sb,\n--\nfs/erofs/super.c-164-\t\t}\nfs/erofs/super.c:165:\t\tif (!dif-\u003edax_dev \u0026\u0026 test_opt(\u0026sbi-\u003eopt, DAX_ALWAYS)) {\nfs/erofs/super.c-166-\t\t\terofs_info(sb, \"DAX unsupported by %s. Turning off DAX.\",\nfs/erofs/super.c-167-\t\t\t\t dif-\u003epath);\nfs/erofs/super.c:168:\t\t\tclear_opt(\u0026sbi-\u003eopt, DAX_ALWAYS);\nfs/erofs/super.c-169-\t\t}\n--\nfs/erofs/super.c=183=static int erofs_scan_devices(struct super_block *sb,\n--\nfs/erofs/super.c-205-\nfs/erofs/super.c:206:\tif (test_opt(\u0026sbi-\u003eopt, DAX_ALWAYS) \u0026\u0026 !sbi-\u003edif0.dax_dev) {\nfs/erofs/super.c-207-\t\terofs_info(sb, \"DAX unsupported by block device. Turning off DAX.\");\nfs/erofs/super.c:208:\t\tclear_opt(\u0026sbi-\u003eopt, DAX_ALWAYS);\nfs/erofs/super.c-209-\t}\n--\nfs/erofs/super.c=374=static void erofs_default_options(struct erofs_sb_info *sbi)\n--\nfs/erofs/super.c-376-#ifdef CONFIG_EROFS_FS_ZIP\nfs/erofs/super.c:377:\tsbi-\u003eopt.cache_strategy = EROFS_ZIP_CACHE_READAROUND;\nfs/erofs/super.c-378-\tsbi-\u003esync_decompress = EROFS_SYNC_DECOMPRESS_AUTO;\n--\nfs/erofs/super.c-380-\tif (IS_ENABLED(CONFIG_EROFS_FS_XATTR))\nfs/erofs/super.c:381:\t\tset_opt(\u0026sbi-\u003eopt, XATTR_USER);\nfs/erofs/super.c-382-\tif (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))\nfs/erofs/super.c:383:\t\tset_opt(\u0026sbi-\u003eopt, POSIX_ACL);\nfs/erofs/super.c-384-}\n--\nfs/erofs/super.c=422=static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode)\n--\nfs/erofs/super.c-427-\t\tif (mode == EROFS_MOUNT_DAX_ALWAYS) {\nfs/erofs/super.c:428:\t\t\tset_opt(\u0026sbi-\u003eopt, DAX_ALWAYS);\nfs/erofs/super.c:429:\t\t\tclear_opt(\u0026sbi-\u003eopt, DAX_NEVER);\nfs/erofs/super.c-430-\t\t\treturn true;\nfs/erofs/super.c-431-\t\t} else if (mode == EROFS_MOUNT_DAX_NEVER) {\nfs/erofs/super.c:432:\t\t\tset_opt(\u0026sbi-\u003eopt, DAX_NEVER);\nfs/erofs/super.c:433:\t\t\tclear_opt(\u0026sbi-\u003eopt, DAX_ALWAYS);\nfs/erofs/super.c-434-\t\t\treturn true;\n--\nfs/erofs/super.c=477=static int erofs_fc_parse_param(struct fs_context *fc,\n--\nfs/erofs/super.c-493-\t\telse if (result.boolean)\nfs/erofs/super.c:494:\t\t\tset_opt(\u0026sbi-\u003eopt, XATTR_USER);\nfs/erofs/super.c-495-\t\telse\nfs/erofs/super.c:496:\t\t\tclear_opt(\u0026sbi-\u003eopt, XATTR_USER);\nfs/erofs/super.c-497-\t\tbreak;\n--\nfs/erofs/super.c-501-\t\telse if (result.boolean)\nfs/erofs/super.c:502:\t\t\tset_opt(\u0026sbi-\u003eopt, POSIX_ACL);\nfs/erofs/super.c-503-\t\telse\nfs/erofs/super.c:504:\t\t\tclear_opt(\u0026sbi-\u003eopt, POSIX_ACL);\nfs/erofs/super.c-505-\t\tbreak;\n--\nfs/erofs/super.c-509-\t\telse\nfs/erofs/super.c:510:\t\t\tsbi-\u003eopt.cache_strategy = result.uint_32;\nfs/erofs/super.c-511-\t\tbreak;\n--\nfs/erofs/super.c-550-\t\telse if (result.boolean)\nfs/erofs/super.c:551:\t\t\tset_opt(\u0026sbi-\u003eopt, DIRECT_IO);\nfs/erofs/super.c-552-\t\telse\nfs/erofs/super.c:553:\t\t\tclear_opt(\u0026sbi-\u003eopt, DIRECT_IO);\nfs/erofs/super.c-554-\t\tbreak;\n--\nfs/erofs/super.c-561-\t\telse\nfs/erofs/super.c:562:\t\t\tset_opt(\u0026sbi-\u003eopt, INODE_SHARE);\nfs/erofs/super.c-563-\t\tbreak;\n--\nfs/erofs/super.c-567-\t\telse\nfs/erofs/super.c:568:\t\t\tset_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE);\nfs/erofs/super.c-569-\t\tbreak;\n--\nfs/erofs/super.c=643=static int erofs_fc_fill_super(struct super_block *sb, struct fs_context *fc)\n--\nfs/erofs/super.c-653-\nfs/erofs/super.c:654:\tif (!sbi-\u003edomain_id \u0026\u0026 test_opt(\u0026sbi-\u003eopt, INODE_SHARE)) {\nfs/erofs/super.c-655-\t\terrorfc(fc, \"domain_id is needed when inode_share is on\");\n--\nfs/erofs/super.c-657-\t}\nfs/erofs/super.c:658:\tif (test_opt(\u0026sbi-\u003eopt, DAX_ALWAYS) \u0026\u0026 test_opt(\u0026sbi-\u003eopt, INODE_SHARE)) {\nfs/erofs/super.c-659-\t\terrorfc(fc, \"FSDAX is not allowed when inode_share is on\");\n--\nfs/erofs/super.c-661-\t}\nfs/erofs/super.c:662:\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE) \u0026\u0026\nfs/erofs/super.c:663:\t test_opt(\u0026sbi-\u003eopt, INODE_SHARE)) {\nfs/erofs/super.c-664-\t\terrorfc(fc, \"superblock_share is not allowed when inode_share is on\");\n--\nfs/erofs/super.c-667-\t/* Extra devices are not yet supported with superblock sharing. */\nfs/erofs/super.c:668:\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE) \u0026\u0026\nfs/erofs/super.c-669-\t sbi-\u003edevs-\u003eextra_devices) {\n--\nfs/erofs/super.c-733-\nfs/erofs/super.c:734:\tif (test_opt(\u0026sbi-\u003eopt, DAX_ALWAYS) \u0026\u0026 sbi-\u003eblkszbits != PAGE_SHIFT) {\nfs/erofs/super.c-735-\t\terofs_info(sb, \"unsupported blocksize for DAX\");\nfs/erofs/super.c:736:\t\tclear_opt(\u0026sbi-\u003eopt, DAX_ALWAYS);\nfs/erofs/super.c-737-\t}\nfs/erofs/super.c:738:\tif (test_opt(\u0026sbi-\u003eopt, INODE_SHARE) \u0026\u0026 !erofs_sb_has_ishare_xattrs(sbi)) {\nfs/erofs/super.c-739-\t\terofs_info(sb, \"on-disk ishare xattrs not found. Turning off inode_share.\");\nfs/erofs/super.c:740:\t\tclear_opt(\u0026sbi-\u003eopt, INODE_SHARE);\nfs/erofs/super.c-741-\t}\nfs/erofs/super.c:742:\tif (test_opt(\u0026sbi-\u003eopt, INODE_SHARE))\nfs/erofs/super.c-743-\t\terofs_info(sb, \"EXPERIMENTAL EROFS page cache share support in use. Use at your own risk!\");\n--\nfs/erofs/super.c-748-\nfs/erofs/super.c:749:\tif (test_opt(\u0026sbi-\u003eopt, POSIX_ACL))\nfs/erofs/super.c-750-\t\tsb-\u003es_flags |= SB_POSIXACL;\n--\nfs/erofs/super.c=800=static int erofs_fc_test_file_super(struct super_block *sb,\n--\nfs/erofs/super.c-816-\t sbi-\u003edif0.fsoff == new_sbi-\u003edif0.fsoff \u0026\u0026\nfs/erofs/super.c:817:\t !memcmp(\u0026sbi-\u003eopt, \u0026new_sbi-\u003eopt, sizeof(sbi-\u003eopt));\nfs/erofs/super.c-818-}\n--\nfs/erofs/super.c=820=static int erofs_fc_get_tree_file(struct fs_context *fc)\n--\nfs/erofs/super.c-823-\nfs/erofs/super.c:824:\tif (!test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE))\nfs/erofs/super.c-825-\t\treturn get_tree_nodev(fc, erofs_fc_fill_super);\n--\nfs/erofs/super.c=868=static int erofs_fc_reconfigure(struct fs_context *fc)\n--\nfs/erofs/super.c-876-\t/* Shared superblocks must not be reconfigured. */\nfs/erofs/super.c:877:\tif (test_opt(\u0026sbi-\u003eopt, SUPERBLOCK_SHARE))\nfs/erofs/super.c-878-\t\treturn -EBUSY;\n--\nfs/erofs/super.c-882-\nfs/erofs/super.c:883:\tif (test_opt(\u0026new_sbi-\u003eopt, POSIX_ACL))\nfs/erofs/super.c-884-\t\tfc-\u003esb_flags |= SB_POSIXACL;\n--\nfs/erofs/super.c-887-\nfs/erofs/super.c:888:\tsbi-\u003eopt = new_sbi-\u003eopt;\nfs/erofs/super.c-889-\n--\nfs/erofs/super.c=1136=static int erofs_show_options(struct seq_file *seq, struct dentry *root)\n--\nfs/erofs/super.c-1138-\tstruct erofs_sb_info *sbi = EROFS_SB(root-\u003ed_sb);\nfs/erofs/super.c:1139:\tstruct erofs_mount_opts *opt = \u0026sbi-\u003eopt;\nfs/erofs/super.c-1140-\n--\nfs/erofs/sysfs.c=117=static unsigned char *__struct_ptr(struct erofs_sb_info *sbi,\n--\nfs/erofs/sysfs.c-122-\tif (struct_type == struct_erofs_mount_opts)\nfs/erofs/sysfs.c:123:\t\treturn (unsigned char *)\u0026sbi-\u003eopt + offset;\nfs/erofs/sysfs.c-124-\treturn NULL;\n--\nfs/erofs/xattr.c=406=static bool erofs_xattr_user_list(struct dentry *dentry)\nfs/erofs/xattr.c-407-{\nfs/erofs/xattr.c:408:\treturn test_opt(\u0026EROFS_SB(dentry-\u003ed_sb)-\u003eopt, XATTR_USER);\nfs/erofs/xattr.c-409-}\n--\nfs/erofs/xattr.c=416=static int erofs_xattr_generic_get(const struct xattr_handler *handler,\n--\nfs/erofs/xattr.c-420-\tif (handler-\u003eflags == EROFS_XATTR_INDEX_USER \u0026\u0026\nfs/erofs/xattr.c:421:\t !test_opt(\u0026EROFS_I_SB(inode)-\u003eopt, XATTR_USER))\nfs/erofs/xattr.c-422-\t\treturn -EOPNOTSUPP;\n--\nfs/erofs/xattr.c=618=int erofs_xattr_fill_inode_fingerprint(struct erofs_inode_fingerprint *fp,\n--\nfs/erofs/xattr.c-625-\nfs/erofs/xattr.c:626:\tif (!test_opt(\u0026sbi-\u003eopt, INODE_SHARE))\nfs/erofs/xattr.c-627-\t\treturn -EOPNOTSUPP;\n--\nfs/erofs/zdata.c=516=static bool z_erofs_should_alloc_cache(struct z_erofs_frontend *fe)\nfs/erofs/zdata.c-517-{\nfs/erofs/zdata.c:518:\tunsigned int cachestrategy = EROFS_I_SB(fe-\u003einode)-\u003eopt.cache_strategy;\nfs/erofs/zdata.c-519-\n"
} |
| 41/3 | 2026/08/11 07:32 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17469 output: 37 thoughts: 1274 Thoughts: **Unraveling Potential Uninitialized Memory Issues in EROFS Mount Option Handling** Okay, so I'm digging into this EROFS code, specifically concerning the handling of mount options and potential uninitialized memory reads. My initial thought was triggered by the exposure of `sbi->opt` via sysfs – that's often a red flag. But then I realized, sysfs attributes are defined field-by-field, so they wouldn't expose the entire structure, padding and all. Good, that's one avenue closed. The core of my concern here seems to be `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))`. Is it possible that `new_sbi->opt` could contain uninitialized padding when it's allocated via `erofs_init_fs_context` and zeroed using `kzalloc_obj`? No. Since it is initialized by `kzalloc`, the structure and its padding is zeroed. Later modifications within `erofs_fc_parse_param` only touch specific fields, leaving the padding untouched and remaining zeroed. So, the `memcmp` will be comparing initialized memory, which is exactly what we want. Then, I started wondering if `sbi->opt` was copied from somewhere else that *wasn't* zeroed. But a quick glance at the code shows that `sbi->opt` is assigned using `sbi->opt = new_sbi->opt;` in `erofs_fc_reconfigure`. Given that `new_sbi` is always allocated with `kzalloc`, we're guaranteed to be safe from uninitialized reads. Then, there's the possibility that `erofs_fc_parse_param` might involve a read-modify-write operation on the whole word, including padding. However, the padding will be zero. Therefore, even if a compiler generates a read-modify-write operation on the whole word, it reads initialized (zero) memory, does something, and writes the padding back as zero. That's still safe. Now, let's look for other potential issues. This patch introduces `superblock_share` and its use in `erofs_fc_test_file_super`. The comparison there looks at `sbi->dif0.file`, `sbi->dif0.fsoff`, `new_sbi->dif0.file`, and `new_sbi->dif0.fsoff`, along with `memcmp` of the options. Are those initialized? I'm pretty sure `dif0` is part of `sbi` which is initialized by `kzalloc`, meaning the `fsoff` is always initialized. Even if it's not provided via a mount option, it remains zero. Then `sbi->dif0.file` is set from the `Opt_source` option, and it's handled properly if it's NULL (unspecified) with the conditional check. I did a mental check of `get_tree_super`. This is a pretty standard VFS function. There's nothing unusual there. Is anything being sent to user space? No, this is just internal mount option handling and superblock sharing. Is there any conditional logic that relies on uninitialized variables? Nope, everything's initialized. No allocation of complex data structures with potentially uninitialized fields that could be read later? Nope, the structures are straightforward. Just to be absolutely sure, I'll review again: `sbi->opt` and its padding are zeroed by `kzalloc`. If `sbi->opt` is assigned from a `new_sbi->opt`, that also gets initialized by `kzalloc`. The modifications via `erofs_default_options` and `erofs_fc_parse_param` do not touch the padding. There's no copying to user space except for printing the options field-by-field in `erofs_show_options`. The options are printed via a seq_file, which is sent to user space, but this happens field-by-field. Therefore, there's no struct copying, no padding, and no problem. Okay, I think I've thoroughly vetted the code, and I'm confident there are no uninitialized memory issues related to mount option handling in this patch. I'm satisfied. |
| 42/3 | 2026/08/11 07:32 | tool | read-file |
0mArgs:{
"File": "fs/erofs/super.c",
"FirstLine": 1136,
"LineCount": 20
}Results: {
"Contents": "1136:\tstatic int erofs_show_options(struct seq_file *seq, struct dentry *root)\n1137:\t{\n1138:\t\tstruct erofs_sb_info *sbi = EROFS_SB(root-\u003ed_sb);\n1139:\t\tstruct erofs_mount_opts *opt = \u0026sbi-\u003eopt;\n1140:\t\n1141:\t\tif (IS_ENABLED(CONFIG_EROFS_FS_XATTR))\n1142:\t\t\tseq_puts(seq, test_opt(opt, XATTR_USER) ?\n1143:\t\t\t\t\t\",user_xattr\" : \",nouser_xattr\");\n1144:\t\tif (IS_ENABLED(CONFIG_EROFS_FS_POSIX_ACL))\n1145:\t\t\tseq_puts(seq, test_opt(opt, POSIX_ACL) ? \",acl\" : \",noacl\");\n1146:\t\tif (IS_ENABLED(CONFIG_EROFS_FS_ZIP) \u0026\u0026 sbi-\u003eavailable_compr_algs)\n1147:\t\t\tseq_printf(seq, \",cache_strategy=%s\",\n1148:\t\t\t\t erofs_param_cache_strategy[opt-\u003ecache_strategy].name);\n1149:\t\tif (test_opt(opt, DAX_ALWAYS))\n1150:\t\t\tseq_puts(seq, \",dax=always\");\n1151:\t\tif (test_opt(opt, DAX_NEVER))\n1152:\t\t\tseq_puts(seq, \",dax=never\");\n1153:\t\tif (erofs_is_fileio_mode(sbi) \u0026\u0026 test_opt(opt, DIRECT_IO))\n1154:\t\t\tseq_puts(seq, \",directio\");\n1155:\t\tif (sbi-\u003edif0.fsoff)\n"
} |
| 43/3 | 2026/08/11 07:32 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 20165 output: 262 thoughts: 1731 Thoughts: **KMSAN Assessment of EROFS Superblock Sharing Patch** Okay, let's break this down. First, yes, the code *does* seem to print field by field. I'm focusing on whether there's any uninitialized memory usage. I see that the only use of `sbi->opt` as a whole is in `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))`. This is the critical point. Both `sbi` and `new_sbi` are `kzalloc`'d, which means their padding is *zeroed* by default. So even if `memcmp` reads that padding, it's reading *initialized* memory (specifically, zeros). KMSAN shouldn't complain. Now, could the `sbi->opt` struct, in particular, have bitfields? No, it doesn't. It's got `unsigned char cache_strategy` and `unsigned int mount_opt`. There is 3 bytes of padding between these two fields, due to the structure layout. But since it's `kzalloc`'d, that padding is zero. Any write to `cache_strategy` or `mount_opt` generates byte or word writes, and it never touches the padding. That padding *remains* zero. The `memcmp` call will compare the padding bytes, and they are both zero, so they match. This is perfectly safe. It's not uninitialized memory. Let's look more broadly at the patch. It introduces `superblock_share` mount option. Now, it does modify `erofs_fc_parse_param` to set `SUPERBLOCK_SHARE`. This just sets a flag in `sbi->opt.mount_opt`. `erofs_fc_fill_super` checks flags that have been initialized. The new function `erofs_fc_test_file_super` seems the most likely place where it could fail. But let's see. Here, we see comparisons against `sb->s_iflags`, the `sbi->dif0` members, and finally `memcmp(&sbi->opt, &new_sbi->opt, sizeof(sbi->opt))`. All these fields are explicitly initialized. The conditional logic depends on initialized flags. The patch also introduces `erofs_fc_get_tree_file`, which just calls another function depending on a flag. And `erofs_fc_reconfigure` just checks a flag, too. Finally, the `fs/super.c` change just exports `get_tree_super`. There is absolutely no risk of uninitialized memory here. `memcmp` on a struct with padding is safe because of `kzalloc`. The padding is never modified, and remains zero. KMSAN sees the padding as being initialized to zero. Therefore, `NeedsKMSAN` should be false. The changes are all related to mount options and superblock sharing. Any bugs introduced will be logic errors, memory leaks, use-after-free, or locking issues, and those are caught by KASAN, LOCKDEP, or standard detectors. There are no structures sent to user space, no complex data structures with uninitialized fields, and no bounds checks that could expose uninitialized memory. So, here's my reasoning, in summary: * The patch introduces `superblock_share` for EROFS file-backed mounts. * It adds a comparison `erofs_fc_test_file_super` that uses `memcmp` on `struct erofs_mount_opts`. * `struct erofs_mount_opts` has padding, but instances are allocated via `kzalloc`, so padding is initialized to zero and *remains* zero. `memcmp` safely reads initialized memory. * No structures are copied to userspace. * No uninitialized memory is exposed in control flow. * Potential bugs (e.g., use-after-free, memory leaks, logic errors) are caught by KASAN, LOCKDEP, or testing. * Therefore, KMSAN fuzzing isn't justified. This should be good to go. |
| 44/3 | 2026/08/11 07:32 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces a new mount option `superblock_share` for EROFS file-backed mounts and adds logic to share superblocks when compatible options are used. The comparison function `erofs_fc_test_file_super` uses `memcmp` to compare `struct erofs_mount_opts`. While this struct contains padding, instances of it are embedded within `struct erofs_sb_info`, which is always allocated via `kzalloc` (in `erofs_init_fs_context`). Consequently, the padding is initialized to zero and remains zero, meaning `memcmp` safely reads initialized memory without triggering KMSAN or causing undefined behavior.\n\nThe patch does not introduce any structures that are copied to user space, nor does it allocate complex data structures where uninitialized fields could be read later. There is no conditional logic depending on uninitialized variables. Any potential bugs introduced by these changes (such as use-after-free, memory leaks, or locking issues related to superblock sharing) would be effectively caught by KASAN, LOCKDEP, and other standard bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces a new mount option `superblock_share` for EROFS file-backed mounts and adds logic to share superblocks when compatible options are used. The comparison function `erofs_fc_test_file_super` uses `memcmp` to compare `struct erofs_mount_opts`. While this struct contains padding, instances of it are embedded within `struct erofs_sb_info`, which is always allocated via `kzalloc` (in `erofs_init_fs_context`). Consequently, the padding is initialized to zero and remains zero, meaning `memcmp` safely reads initialized memory without triggering KMSAN or causing undefined behavior.\n\nThe patch does not introduce any structures that are copied to user space, nor does it allocate complex data structures where uninitialized fields could be read later. There is no conditional logic depending on uninitialized variables. Any potential bugs introduced by these changes (such as use-after-free, memory leaks, or locking issues related to superblock sharing) would be effectively caught by KASAN, LOCKDEP, and other standard bug detectors. Therefore, a dedicated KMSAN fuzzing session is not justified.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|