| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/24 16:31 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"AFS_FS"
],
"FocusSymbols": [
"vfs_inode_is_owned_by_me",
"vfs_inode_and_dir_have_same_owner",
"vfs_inodes_have_same_owner",
"afs_is_owned_by_me",
"afs_have_same_owner",
"chown_ok",
"chgrp_ok",
"may_create_in_sticky",
"__check_sticky",
"may_dedupe_file"
],
"Reasoning": "The patch introduces two new inode operations `is_owned_by_me` and `have_same_owner` to override the default VFS ownership checks. It implements these operations in the AFS filesystem and modifies core VFS functions (like `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `may_dedupe_file`, etc.) to use them. This is a functional change in core VFS and AFS that should be fuzzed.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/24 16:31 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit d340543c2bdc34100e2035ae644f3598efed551f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Jul 24 16:31:09 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst\nindex 7c753148af8883..893419e003eaf7 100644\n--- a/Documentation/filesystems/vfs.rst\n+++ b/Documentation/filesystems/vfs.rst\n@@ -445,6 +445,9 @@ As of kernel 2.6.22, the following members are defined:\n \t\t\t\t struct dentry *dentry, struct file_kattr *fa);\n \t\tint (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);\n \t struct offset_ctx *(*get_offset_ctx)(struct inode *inode);\n+\t\tint (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode);\n+\t\tint (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode,\n+\t\t\t\t struct dentry *dentry);\n \t};\n \n Again, all methods are called without any locks being held, unless\n@@ -634,6 +637,24 @@ otherwise noted.\n filesystem must define this operation to use\n simple_offset_dir_operations.\n \n+``is_owned_by_me``\n+\tcalled to determine if the file can be considered to be 'owned' by\n+\tthe owner of the process or if the process has a token that grants\n+\tit ownership privileges. If unset, the default is to compare i_uid\n+\tto current_fsuid() - but this may give incorrect results for some\n+\tnetwork or plug-in block filesystems. For example, AFS determines\n+\townership entirely according to an obtained token and i_uid may not\n+\teven be from the same ID space as current_uid().\n+\n+``have_same_owner``\n+\tcalled to determine if an inode has the same owner as its immediate\n+\tparent on the path walked. If unset, the default is to simply\n+\tcompare the i_uid of both. For example, AFS compares the owner IDs\n+\tof both - but these are a 64-bit values on some variants that might\n+\tnot fit into a kuid_t and cifs has GUIDs that cannot be compared to\n+\tkuid_t.\n+\n+\n The Address Space Object\n ========================\n \ndiff --git a/fs/afs/dir.c b/fs/afs/dir.c\nindex 6df56fe9163fd7..ee2f5ec0b2c67d 100644\n--- a/fs/afs/dir.c\n+++ b/fs/afs/dir.c\n@@ -69,6 +69,8 @@ const struct inode_operations afs_dir_inode_operations = {\n \t.permission\t= afs_permission,\n \t.getattr\t= afs_getattr,\n \t.setattr\t= afs_setattr,\n+\t.is_owned_by_me\t= afs_is_owned_by_me,\n+\t.have_same_owner = afs_have_same_owner,\n };\n \n const struct address_space_operations afs_dir_aops = {\ndiff --git a/fs/afs/file.c b/fs/afs/file.c\nindex 0467742bfeee34..ba44493ccaf6ee 100644\n--- a/fs/afs/file.c\n+++ b/fs/afs/file.c\n@@ -49,6 +49,8 @@ const struct inode_operations afs_file_inode_operations = {\n \t.getattr\t= afs_getattr,\n \t.setattr\t= afs_setattr,\n \t.permission\t= afs_permission,\n+\t.is_owned_by_me\t= afs_is_owned_by_me,\n+\t.have_same_owner = afs_have_same_owner,\n };\n \n const struct address_space_operations afs_file_aops = {\ndiff --git a/fs/afs/inode.c b/fs/afs/inode.c\nindex 14f39a9bea6cfe..af85a88d943dd1 100644\n--- a/fs/afs/inode.c\n+++ b/fs/afs/inode.c\n@@ -91,6 +91,7 @@ static int afs_inode_init_from_status(struct afs_operation *op,\n \tinode-\u003ei_flags |= S_NOATIME;\n \tinode-\u003ei_uid = make_kuid(\u0026init_user_ns, status-\u003eowner);\n \tinode-\u003ei_gid = make_kgid(\u0026init_user_ns, status-\u003egroup);\n+\tinode-\u003ei_opflags |= IOP_OWNERSHIP_OVERRIDE;\n \tset_nlink(\u0026vnode-\u003enetfs.inode, status-\u003enlink);\n \n \ti_size_write(inode, status-\u003esize);\ndiff --git a/fs/afs/internal.h b/fs/afs/internal.h\nindex 601f01e5c15fcc..e10cf7636f65b6 100644\n--- a/fs/afs/internal.h\n+++ b/fs/afs/internal.h\n@@ -1530,6 +1530,9 @@ extern struct key *afs_request_key(struct afs_cell *);\n extern struct key *afs_request_key_rcu(struct afs_cell *);\n extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *);\n extern int afs_permission(struct mnt_idmap *, struct inode *, int);\n+int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);\n+int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,\n+\t\t\tstruct inode *inode2);\n extern void __exit afs_clean_up_permit_cache(void);\n \n /*\ndiff --git a/fs/afs/security.c b/fs/afs/security.c\nindex 6d00d62a65edf6..7092f5acdcd6ac 100644\n--- a/fs/afs/security.c\n+++ b/fs/afs/security.c\n@@ -510,6 +510,52 @@ int afs_permission(struct mnt_idmap *idmap, struct inode *inode,\n \treturn ret;\n }\n \n+/*\n+ * Determine if an inode is owned by 'me' - whatever that means for the\n+ * filesystem. In the case of AFS, this means that the file is owned by the\n+ * AFS user represented by the Rx Security Class token held in a key. Returns\n+ * 0 if owned by me, 1 if not; can also return an error.\n+ */\n+int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)\n+{\n+\tstruct afs_vnode *vnode = AFS_FS_I(inode);\n+\tafs_access_t access;\n+\tstruct key *key;\n+\tint ret;\n+\n+\tif (S_ISDIR(inode-\u003ei_mode))\n+\t\treturn 1; /* The ADMIN right check doesn't work for directories. */\n+\n+\tkey = afs_request_key(vnode-\u003evolume-\u003ecell);\n+\tif (IS_ERR(key))\n+\t\treturn PTR_ERR(key);\n+\n+\t/* Get the access rights for the key on this file. */\n+\tret = afs_check_permit(vnode, key, \u0026access);\n+\tif (ret \u003c 0)\n+\t\tgoto error;\n+\n+\t/* We get the ADMINISTER bit if we own the file. */\n+\tret = (access \u0026 AFS_ACE_ADMINISTER) ? 0 : 1;\n+error:\n+\tkey_put(key);\n+\treturn ret;\n+}\n+\n+/*\n+ * Determine if a file has the same owner as its parent - whatever that means\n+ * for the filesystem. In the case of AFS, this means comparing their AFS\n+ * UIDs. Returns 0 if same, 1 if not same; can also return an error.\n+ */\n+int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,\n+\t\t\tstruct inode *inode2)\n+{\n+\tconst struct afs_vnode *vnode1 = AFS_FS_I(inode1);\n+\tconst struct afs_vnode *vnode2 = AFS_FS_I(inode2);\n+\n+\treturn vnode1-\u003estatus.owner != vnode2-\u003estatus.owner;\n+}\n+\n void __exit afs_clean_up_permit_cache(void)\n {\n \tint i;\ndiff --git a/fs/attr.c b/fs/attr.c\nindex 4f437fabb7f0f5..b7e8b5d49a9fe6 100644\n--- a/fs/attr.c\n+++ b/fs/attr.c\n@@ -16,6 +16,7 @@\n #include \u003clinux/fcntl.h\u003e\n #include \u003clinux/filelock.h\u003e\n #include \u003clinux/security.h\u003e\n+#include \"internal.h\"\n \n /**\n * setattr_should_drop_sgid - determine whether the setgid bit needs to be\n@@ -91,19 +92,21 @@ EXPORT_SYMBOL(setattr_should_drop_suidgid);\n * permissions. On non-idmapped mounts or if permission checking is to be\n * performed on the raw inode simply pass @nop_mnt_idmap.\n */\n-static bool chown_ok(struct mnt_idmap *idmap,\n-\t\t const struct inode *inode, vfsuid_t ia_vfsuid)\n+static int chown_ok(struct mnt_idmap *idmap,\n+\t\t struct inode *inode, vfsuid_t ia_vfsuid)\n {\n \tvfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);\n-\tif (vfsuid_eq_kuid(vfsuid, current_fsuid()) \u0026\u0026\n-\t vfsuid_eq(ia_vfsuid, vfsuid))\n-\t\treturn true;\n+\tint ret;\n+\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \tif (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN))\n-\t\treturn true;\n+\t\treturn 0;\n \tif (!vfsuid_valid(vfsuid) \u0026\u0026\n \t ns_capable(inode-\u003ei_sb-\u003es_user_ns, CAP_CHOWN))\n-\t\treturn true;\n-\treturn false;\n+\t\treturn 0;\n+\treturn -EPERM;\n }\n \n /**\n@@ -118,23 +121,27 @@ static bool chown_ok(struct mnt_idmap *idmap,\n * permissions. On non-idmapped mounts or if permission checking is to be\n * performed on the raw inode simply pass @nop_mnt_idmap.\n */\n-static bool chgrp_ok(struct mnt_idmap *idmap,\n-\t\t const struct inode *inode, vfsgid_t ia_vfsgid)\n+static int chgrp_ok(struct mnt_idmap *idmap,\n+\t\t struct inode *inode, vfsgid_t ia_vfsgid)\n {\n \tvfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);\n-\tvfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);\n-\tif (vfsuid_eq_kuid(vfsuid, current_fsuid())) {\n+\tint ret;\n+\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\tif (ret == 0) {\n \t\tif (vfsgid_eq(ia_vfsgid, vfsgid))\n-\t\t\treturn true;\n+\t\t\treturn 0;\n \t\tif (vfsgid_in_group_p(ia_vfsgid))\n-\t\t\treturn true;\n+\t\t\treturn 0;\n \t}\n \tif (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN))\n-\t\treturn true;\n+\t\treturn 0;\n \tif (!vfsgid_valid(vfsgid) \u0026\u0026\n \t ns_capable(inode-\u003ei_sb-\u003es_user_ns, CAP_CHOWN))\n-\t\treturn true;\n-\treturn false;\n+\t\treturn 0;\n+\treturn -EPERM;\n }\n \n /**\n@@ -163,6 +170,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,\n {\n \tstruct inode *inode = d_inode(dentry);\n \tunsigned int ia_valid = attr-\u003eia_valid;\n+\tint ret;\n \n \t/*\n \t * First check size constraints. These can't be overridden using\n@@ -189,14 +197,18 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,\n \t\tgoto kill_priv;\n \n \t/* Make sure a caller can chown. */\n-\tif ((ia_valid \u0026 ATTR_UID) \u0026\u0026\n-\t !chown_ok(idmap, inode, attr-\u003eia_vfsuid))\n-\t\treturn -EPERM;\n+\tif (ia_valid \u0026 ATTR_UID) {\n+\t\tret = chown_ok(idmap, inode, attr-\u003eia_vfsuid);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\t}\n \n \t/* Make sure caller can chgrp. */\n-\tif ((ia_valid \u0026 ATTR_GID) \u0026\u0026\n-\t !chgrp_ok(idmap, inode, attr-\u003eia_vfsgid))\n-\t\treturn -EPERM;\n+\tif (ia_valid \u0026 ATTR_GID) {\n+\t\tret = chgrp_ok(idmap, inode, attr-\u003eia_vfsgid);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\t}\n \n \t/* Make sure a caller can chmod. */\n \tif (ia_valid \u0026 ATTR_MODE) {\ndiff --git a/fs/coredump.c b/fs/coredump.c\nindex e68a76ff92a386..3f32e73aaeda28 100644\n--- a/fs/coredump.c\n+++ b/fs/coredump.c\n@@ -954,7 +954,7 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,\n \t * filesystem.\n \t */\n \tidmap = file_mnt_idmap(file);\n-\tif (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) {\n+\tif (vfs_inode_is_owned_by_me(idmap, inode) != 0) {\n \t\tcoredump_report_failure(\"Core dump to %s aborted: cannot preserve file owner\", cn-\u003ecorename);\n \t\treturn false;\n \t}\ndiff --git a/fs/inode.c b/fs/inode.c\nindex 31c5b9ee3a81df..a7ec830ab683f0 100644\n--- a/fs/inode.c\n+++ b/fs/inode.c\n@@ -2750,16 +2750,19 @@ EXPORT_SYMBOL(inode_init_owner);\n * On non-idmapped mounts or if permission checking is to be performed on the\n * raw inode simply pass @nop_mnt_idmap.\n */\n-bool inode_owner_or_capable(struct mnt_idmap *idmap,\n-\t\t\t const struct inode *inode)\n+bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode)\n {\n \tvfsuid_t vfsuid;\n \tstruct user_namespace *ns;\n+\tint ret;\n \n-\tvfsuid = i_uid_into_vfsuid(idmap, inode);\n-\tif (vfsuid_eq_kuid(vfsuid, current_fsuid()))\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret == 0)\n \t\treturn true;\n+\tif (ret \u003c 0)\n+\t\treturn false;\n \n+\tvfsuid = i_uid_into_vfsuid(idmap, inode);\n \tns = current_user_ns();\n \tif (vfsuid_has_mapping(ns, vfsuid) \u0026\u0026 ns_capable(ns, CAP_FOWNER))\n \t\treturn true;\ndiff --git a/fs/internal.h b/fs/internal.h\nindex 355d93f9220861..be45cc63886192 100644\n--- a/fs/internal.h\n+++ b/fs/internal.h\n@@ -51,6 +51,7 @@ extern int finish_clean_context(struct fs_context *fc);\n /*\n * namei.c\n */\n+int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);\n extern int filename_lookup(int dfd, struct filename *name, unsigned flags,\n \t\t\t struct path *path, const struct path *root);\n int filename_rmdir(int dfd, struct filename *name);\ndiff --git a/fs/locks.c b/fs/locks.c\nindex 6e4ff7fcec053e..02f810dc9e7e7a 100644\n--- a/fs/locks.c\n+++ b/fs/locks.c\n@@ -68,6 +68,7 @@\n #include \u003ctrace/events/filelock.h\u003e\n \n #include \u003clinux/uaccess.h\u003e\n+#include \"internal.h\"\n \n static struct file_lock *file_lock(struct file_lock_core *flc)\n {\n@@ -2136,10 +2137,12 @@ int\n vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)\n {\n \tstruct inode *inode = file_inode(filp);\n-\tvfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_idmap(filp), inode);\n \tint error;\n \n-\tif ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) \u0026\u0026 !capable(CAP_LEASE))\n+\terror = vfs_inode_is_owned_by_me(file_mnt_idmap(filp), inode);\n+\tif (error \u003c 0)\n+\t\treturn error;\n+\tif (error != 0 \u0026\u0026 !capable(CAP_LEASE))\n \t\treturn -EACCES;\n \terror = security_file_lock(filp, arg);\n \tif (error)\ndiff --git a/fs/namei.c b/fs/namei.c\nindex 19ce43c9a6e662..72f94d204f93f4 100644\n--- a/fs/namei.c\n+++ b/fs/namei.c\n@@ -55,8 +55,8 @@\n * The new code replaces the old recursive symlink resolution with\n * an iterative one (in case of non-nested symlink chains). It does\n * this with calls to \u003cfs\u003e_follow_link().\n- * As a side effect, dir_namei(), _namei() and follow_link() are now \n- * replaced with a single function lookup_dentry() that can handle all \n+ * As a side effect, dir_namei(), _namei() and follow_link() are now\n+ * replaced with a single function lookup_dentry() that can handle all\n * the special cases of the former code.\n *\n * With the new dcache, the pathname is stored at each inode, at least as\n@@ -1255,6 +1255,72 @@ fs_initcall(init_fs_namei_sysctls);\n \n #endif /* CONFIG_SYSCTL */\n \n+/*\n+ * Determine if an inode is owned by the process (allowing for fsuid override),\n+ * returning 0 if so, 1 if not and a negative error code if there was a problem\n+ * making the determination.\n+ */\n+int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)\n+{\n+\tif (unlikely(inode-\u003ei_opflags \u0026 IOP_OWNERSHIP_OVERRIDE))\n+\t\treturn inode-\u003ei_op-\u003eis_owned_by_me(idmap, inode);\n+\tif (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid()))\n+\t\treturn 0;\n+\treturn 1; /* Not same. */\n+}\n+\n+/*\n+ * Determine if an inode has the same owner as its parent directory, returning\n+ * 0 if so, 1 if not and a negative error code if there was a problem making\n+ * the determination.\n+ */\n+static int vfs_inode_and_dir_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,\n+\t\t\t\t\t const struct nameidata *nd)\n+{\n+\tif (unlikely(inode-\u003ei_opflags \u0026 IOP_OWNERSHIP_OVERRIDE)) {\n+\t\tstruct dentry *parent;\n+\t\tstruct inode *dir;\n+\t\tint ret;\n+\n+\t\tif (inode != nd-\u003einode) {\n+\t\t\tdir = nd-\u003einode;\n+\t\t\tret = inode-\u003ei_op-\u003ehave_same_owner(idmap, inode, dir);\n+\t\t} else if (nd-\u003eflags \u0026 LOOKUP_RCU) {\n+\t\t\tparent = READ_ONCE(nd-\u003epath.dentry);\n+\t\t\tdir = READ_ONCE(parent-\u003ed_inode);\n+\t\t\tif (!dir)\n+\t\t\t\treturn -ECHILD;\n+\t\t\tret = inode-\u003ei_op-\u003ehave_same_owner(idmap, inode, dir);\n+\t\t} else {\n+\t\t\tparent = dget_parent(nd-\u003epath.dentry);\n+\t\t\tdir = parent-\u003ed_inode;\n+\t\t\tret = inode-\u003ei_op-\u003ehave_same_owner(idmap, inode, dir);\n+\t\t\tdput(parent);\n+\t\t}\n+\t\treturn ret;\n+\t}\n+\n+\tif (vfsuid_valid(nd-\u003edir_vfsuid) \u0026\u0026\n+\t vfsuid_eq(i_uid_into_vfsuid(idmap, inode), nd-\u003edir_vfsuid))\n+\t\treturn 0;\n+\treturn 1; /* Not same. */\n+}\n+\n+/*\n+ * Determine if two inodes have the same owner, returning 0 if so, 1 if not and\n+ * a negative error code if there was a problem making the determination.\n+ */\n+static int vfs_inodes_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,\n+\t\t\t\t struct inode *dir)\n+{\n+\tif (unlikely(inode-\u003ei_opflags \u0026 IOP_OWNERSHIP_OVERRIDE))\n+\t\treturn inode-\u003ei_op-\u003ehave_same_owner(idmap, inode, dir);\n+\tif (vfsuid_eq(i_uid_into_vfsuid(idmap, inode),\n+\t\t i_uid_into_vfsuid(idmap, dir)))\n+\t\treturn 0;\n+\treturn 1; /* Not same. */\n+}\n+\n /**\n * may_follow_link - Check symlink following for unsafe situations\n * @nd: nameidata pathwalk data\n@@ -1271,27 +1337,28 @@ fs_initcall(init_fs_namei_sysctls);\n *\n * Returns 0 if following the symlink is allowed, -ve on error.\n */\n-static inline int may_follow_link(struct nameidata *nd, const struct inode *inode)\n+static inline int may_follow_link(struct nameidata *nd, struct inode *inode)\n {\n \tstruct mnt_idmap *idmap;\n-\tvfsuid_t vfsuid;\n+\tint ret;\n \n \tif (!sysctl_protected_symlinks)\n \t\treturn 0;\n \n-\tidmap = mnt_idmap(nd-\u003epath.mnt);\n-\tvfsuid = i_uid_into_vfsuid(idmap, inode);\n-\t/* Allowed if owner and follower match. */\n-\tif (vfsuid_eq_kuid(vfsuid, current_fsuid()))\n-\t\treturn 0;\n-\n \t/* Allowed if parent directory not sticky and world-writable. */\n \tif ((nd-\u003edir_mode \u0026 (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))\n \t\treturn 0;\n \n+\tidmap = mnt_idmap(nd-\u003epath.mnt);\n+\t/* Allowed if owner and follower match. */\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\n \t/* Allowed if parent directory and link owner match. */\n-\tif (vfsuid_valid(nd-\u003edir_vfsuid) \u0026\u0026 vfsuid_eq(nd-\u003edir_vfsuid, vfsuid))\n-\t\treturn 0;\n+\tret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \n \tif (nd-\u003eflags \u0026 LOOKUP_RCU)\n \t\treturn -ECHILD;\n@@ -1389,12 +1456,12 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link)\n * @inode: the inode of the file to open\n *\n * Block an O_CREAT open of a FIFO (or a regular file) when:\n- * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled\n- * - the file already exists\n- * - we are in a sticky directory\n- * - we don't own the file\n+ * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled,\n+ * - the file already exists,\n+ * - we are in a sticky directory,\n+ * - the directory is world writable,\n+ * - we don't own the file and\n * - the owner of the directory doesn't own the file\n- * - the directory is world writable\n * If the sysctl_protected_fifos (or sysctl_protected_regular) is set to 2\n * the directory doesn't have to be world writable: being group writable will\n * be enough.\n@@ -1405,13 +1472,45 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link)\n * On non-idmapped mounts or if permission checking is to be performed on the\n * raw inode simply pass @nop_mnt_idmap.\n *\n+ * For a filesystem (e.g. a network filesystem) that has a separate ID space\n+ * and has foreign IDs (maybe even non-integer IDs), i_uid cannot be compared\n+ * to current_fsuid() and may not be directly comparable to another i_uid.\n+ * Instead, the filesystem is asked to perform the comparisons. With network\n+ * filesystems, there also exists the possibility of doing anonymous\n+ * operations and having anonymously-owned objects.\n+ *\n+ * We have the following scenarios:\n+ *\n+ *\tUSER\tDIR\tFILE\tFILE\tALLOWED\n+ *\t\tOWNER\tOWNER\tSTATE\n+ *\t=======\t=======\t=======\t=======\t=======\n+ *\tA\tA\t-\tNew\tYes\n+ *\tA\tA\tA\tExists\tYes\n+ *\tA\tA\tC\tExists\tNo\n+ *\tA\tB\t-\tNew\tYes\n+ *\tA\tB\tA\tExists\tYes, FO==U\n+ *\tA\tB\tB\tExists\tYes, FO==DO\n+ *\tA\tB\tC\tExists\tNo\n+ *\tA\tanon[1]\t-\tNew\tYes\n+ *\tA\tanon[1]\tA\tExists\tYes\n+ *\tA\tanon[1]\tC\tExists\tNo\n+ *\tanon\tA\t-\tNew\tYes\n+ *\tanon\tA\tA\tExists\tYes, FO==DO\n+ *\tanon\tanon[1]\t-\tNew\tYes\n+ *\tanon\tanon[1]\t-\tExists\tNo\n+ *\tanon\tA\tA\tExists\tYes, FO==DO\n+ *\tanon\tA\tC\tExists\tNo\n+ *\tanon\tA\tanon\tExists\tNo\n+ *\n+ * [1] Can anonymously-owned dirs be sticky?\n+ *\n * Returns 0 if the open is allowed, -ve on error.\n */\n static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,\n-\t\t\t\tstruct inode *const inode)\n+\t\t\t\tstruct inode *inode)\n {\n \tumode_t dir_mode = nd-\u003edir_mode;\n-\tvfsuid_t dir_vfsuid = nd-\u003edir_vfsuid, i_vfsuid;\n+\tint ret;\n \n \tif (likely(!(dir_mode \u0026 S_ISVTX)))\n \t\treturn 0;\n@@ -1422,13 +1521,13 @@ static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,\n \tif (S_ISFIFO(inode-\u003ei_mode) \u0026\u0026 !sysctl_protected_fifos)\n \t\treturn 0;\n \n-\ti_vfsuid = i_uid_into_vfsuid(idmap, inode);\n-\n-\tif (vfsuid_eq(i_vfsuid, dir_vfsuid))\n-\t\treturn 0;\n+\tret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \n-\tif (vfsuid_eq_kuid(i_vfsuid, current_fsuid()))\n-\t\treturn 0;\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \n \tif (likely(dir_mode \u0026 0002)) {\n \t\taudit_log_path_denied(AUDIT_ANOM_CREAT, \"sticky_create\");\n@@ -3645,12 +3744,14 @@ EXPORT_SYMBOL(user_path_at);\n int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,\n \t\t struct inode *inode)\n {\n-\tkuid_t fsuid = current_fsuid();\n+\tint ret;\n \n-\tif (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), fsuid))\n-\t\treturn 0;\n-\tif (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, dir), fsuid))\n-\t\treturn 0;\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n+\tret = vfs_inodes_have_same_owner(idmap, inode, dir);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \treturn !capable_wrt_inode_uidgid(idmap, inode, CAP_FOWNER);\n }\n EXPORT_SYMBOL(__check_sticky);\ndiff --git a/fs/remap_range.c b/fs/remap_range.c\nindex 26afbbbfb10c2e..9eee93c2700125 100644\n--- a/fs/remap_range.c\n+++ b/fs/remap_range.c\n@@ -413,20 +413,22 @@ loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in,\n EXPORT_SYMBOL(vfs_clone_file_range);\n \n /* Check whether we are allowed to dedupe the destination file */\n-static bool may_dedupe_file(struct file *file)\n+static int may_dedupe_file(struct file *file)\n {\n \tstruct mnt_idmap *idmap = file_mnt_idmap(file);\n \tstruct inode *inode = file_inode(file);\n+\tint ret;\n \n \tif (capable(CAP_SYS_ADMIN))\n-\t\treturn true;\n+\t\treturn 0;\n \tif (file-\u003ef_mode \u0026 FMODE_WRITE)\n-\t\treturn true;\n-\tif (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid()))\n-\t\treturn true;\n+\t\treturn 0;\n+\tret = vfs_inode_is_owned_by_me(idmap, inode);\n+\tif (ret \u003c= 0)\n+\t\treturn ret;\n \tif (!inode_permission(idmap, inode, MAY_WRITE))\n-\t\treturn true;\n-\treturn false;\n+\t\treturn 0;\n+\treturn -EPERM;\n }\n \n loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,\n@@ -459,8 +461,8 @@ loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,\n \tif (ret)\n \t\treturn ret;\n \n-\tret = -EPERM;\n-\tif (!may_dedupe_file(dst_file))\n+\tret = may_dedupe_file(dst_file);\n+\tif (ret \u003c 0)\n \t\tgoto out_drop_write;\n \n \tret = -EXDEV;\ndiff --git a/include/linux/fs.h b/include/linux/fs.h\nindex 50ce731a2b78f1..8013c13a353d67 100644\n--- a/include/linux/fs.h\n+++ b/include/linux/fs.h\n@@ -628,6 +628,7 @@ is_uncached_acl(struct posix_acl *acl)\n #define IOP_CACHED_LINK\t\t0x0040\n #define IOP_FASTPERM_MAY_EXEC\t0x0080\n #define IOP_FLCTX\t\t0x0100\n+#define IOP_OWNERSHIP_OVERRIDE\t0x0200\t/* Use -\u003eis_owned_by_me() and -\u003ehave_same_owner() */\n \n /*\n * Inode state bits. Protected by inode-\u003ei_lock\n@@ -1755,8 +1756,7 @@ static inline bool file_write_not_started(const struct file *file)\n \treturn sb_write_not_started(file_inode(file)-\u003ei_sb);\n }\n \n-bool inode_owner_or_capable(struct mnt_idmap *idmap,\n-\t\t\t const struct inode *inode);\n+bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode);\n \n /*\n * VFS helper functions..\n@@ -2036,6 +2036,9 @@ struct inode_operations {\n \t\t\t struct dentry *dentry, struct file_kattr *fa);\n \tint (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);\n \tstruct offset_ctx *(*get_offset_ctx)(struct inode *inode);\n+\tint (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode);\n+\tint (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode1,\n+\t\t\t struct inode *inode2);\n } ____cacheline_aligned;\n \n /* Did the driver provide valid mmap hook configuration? */\n"
} |
| 2/1 | 2026/07/24 16:31 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"AFS_FS"
],
"FocusSymbols": [
"vfs_inode_is_owned_by_me",
"vfs_inode_and_dir_have_same_owner",
"vfs_inodes_have_same_owner",
"afs_is_owned_by_me",
"afs_have_same_owner",
"chown_ok",
"chgrp_ok",
"may_create_in_sticky",
"__check_sticky",
"may_dedupe_file"
],
"Reasoning": "The patch introduces two new inode operations `is_owned_by_me` and `have_same_owner` to override the default VFS ownership checks. It implements these operations in the AFS filesystem and modifies core VFS functions (like `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `may_dedupe_file`, etc.) to use them. This is a functional change in core VFS and AFS that should be fuzzed.",
"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). 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. 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. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. 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 d340543c2bdc34100e2035ae644f3598efed551f
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Jul 24 16:31:09 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/filesystems/vfs.rst b/Documentation/filesystems/vfs.rst
index 7c753148af8883..893419e003eaf7 100644
--- a/Documentation/filesystems/vfs.rst
+++ b/Documentation/filesystems/vfs.rst
@@ -445,6 +445,9 @@ As of kernel 2.6.22, the following members are defined:
struct dentry *dentry, struct file_kattr *fa);
int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
+ int (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode);
+ int (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode,
+ struct dentry *dentry);
};
Again, all methods are called without any locks being held, unless
@@ -634,6 +637,24 @@ otherwise noted.
filesystem must define this operation to use
simple_offset_dir_operations.
+``is_owned_by_me``
+ called to determine if the file can be considered to be 'owned' by
+ the owner of the process or if the process has a token that grants
+ it ownership privileges. If unset, the default is to compare i_uid
+ to current_fsuid() - but this may give incorrect results for some
+ network or plug-in block filesystems. For example, AFS determines
+ ownership entirely according to an obtained token and i_uid may not
+ even be from the same ID space as current_uid().
+
+``have_same_owner``
+ called to determine if an inode has the same owner as its immediate
+ parent on the path walked. If unset, the default is to simply
+ compare the i_uid of both. For example, AFS compares the owner IDs
+ of both - but these are a 64-bit values on some variants that might
+ not fit into a kuid_t and cifs has GUIDs that cannot be compared to
+ kuid_t.
+
+
The Address Space Object
========================
diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 6df56fe9163fd7..ee2f5ec0b2c67d 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -69,6 +69,8 @@ const struct inode_operations afs_dir_inode_operations = {
.permission = afs_permission,
.getattr = afs_getattr,
.setattr = afs_setattr,
+ .is_owned_by_me = afs_is_owned_by_me,
+ .have_same_owner = afs_have_same_owner,
};
const struct address_space_operations afs_dir_aops = {
diff --git a/fs/afs/file.c b/fs/afs/file.c
index 0467742bfeee34..ba44493ccaf6ee 100644
--- a/fs/afs/file.c
+++ b/fs/afs/file.c
@@ -49,6 +49,8 @@ const struct inode_operations afs_file_inode_operations = {
.getattr = afs_getattr,
.setattr = afs_setattr,
.permission = afs_permission,
+ .is_owned_by_me = afs_is_owned_by_me,
+ .have_same_owner = afs_have_same_owner,
};
const struct address_space_operations afs_file_aops = {
diff --git a/fs/afs/inode.c b/fs/afs/inode.c
index 14f39a9bea6cfe..af85a88d943dd1 100644
--- a/fs/afs/inode.c
+++ b/fs/afs/inode.c
@@ -91,6 +91,7 @@ static int afs_inode_init_from_status(struct afs_operation *op,
inode->i_flags |= S_NOATIME;
inode->i_uid = make_kuid(&init_user_ns, status->owner);
inode->i_gid = make_kgid(&init_user_ns, status->group);
+ inode->i_opflags |= IOP_OWNERSHIP_OVERRIDE;
set_nlink(&vnode->netfs.inode, status->nlink);
i_size_write(inode, status->size);
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index 601f01e5c15fcc..e10cf7636f65b6 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -1530,6 +1530,9 @@ extern struct key *afs_request_key(struct afs_cell *);
extern struct key *afs_request_key_rcu(struct afs_cell *);
extern int afs_check_permit(struct afs_vnode *, struct key *, afs_access_t *);
extern int afs_permission(struct mnt_idmap *, struct inode *, int);
+int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);
+int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,
+ struct inode *inode2);
extern void __exit afs_clean_up_permit_cache(void);
/*
diff --git a/fs/afs/security.c b/fs/afs/security.c
index 6d00d62a65edf6..7092f5acdcd6ac 100644
--- a/fs/afs/security.c
+++ b/fs/afs/security.c
@@ -510,6 +510,52 @@ int afs_permission(struct mnt_idmap *idmap, struct inode *inode,
return ret;
}
+/*
+ * Determine if an inode is owned by 'me' - whatever that means for the
+ * filesystem. In the case of AFS, this means that the file is owned by the
+ * AFS user represented by the Rx Security Class token held in a key. Returns
+ * 0 if owned by me, 1 if not; can also return an error.
+ */
+int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)
+{
+ struct afs_vnode *vnode = AFS_FS_I(inode);
+ afs_access_t access;
+ struct key *key;
+ int ret;
+
+ if (S_ISDIR(inode->i_mode))
+ return 1; /* The ADMIN right check doesn't work for directories. */
+
+ key = afs_request_key(vnode->volume->cell);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+
+ /* Get the access rights for the key on this file. */
+ ret = afs_check_permit(vnode, key, &access);
+ if (ret < 0)
+ goto error;
+
+ /* We get the ADMINISTER bit if we own the file. */
+ ret = (access & AFS_ACE_ADMINISTER) ? 0 : 1;
+error:
+ key_put(key);
+ return ret;
+}
+
+/*
+ * Determine if a file has the same owner as its parent - whatever that means
+ * for the filesystem. In the case of AFS, this means comparing their AFS
+ * UIDs. Returns 0 if same, 1 if not same; can also return an error.
+ */
+int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,
+ struct inode *inode2)
+{
+ const struct afs_vnode *vnode1 = AFS_FS_I(inode1);
+ const struct afs_vnode *vnode2 = AFS_FS_I(inode2);
+
+ return vnode1->status.owner != vnode2->status.owner;
+}
+
void __exit afs_clean_up_permit_cache(void)
{
int i;
diff --git a/fs/attr.c b/fs/attr.c
index 4f437fabb7f0f5..b7e8b5d49a9fe6 100644
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -16,6 +16,7 @@
#include <linux/fcntl.h>
#include <linux/filelock.h>
#include <linux/security.h>
+#include "internal.h"
/**
* setattr_should_drop_sgid - determine whether the setgid bit needs to be
@@ -91,19 +92,21 @@ EXPORT_SYMBOL(setattr_should_drop_suidgid);
* permissions. On non-idmapped mounts or if permission checking is to be
* performed on the raw inode simply pass @nop_mnt_idmap.
*/
-static bool chown_ok(struct mnt_idmap *idmap,
- const struct inode *inode, vfsuid_t ia_vfsuid)
+static int chown_ok(struct mnt_idmap *idmap,
+ struct inode *inode, vfsuid_t ia_vfsuid)
{
vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
- if (vfsuid_eq_kuid(vfsuid, current_fsuid()) &&
- vfsuid_eq(ia_vfsuid, vfsuid))
- return true;
+ int ret;
+
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret <= 0)
+ return ret;
if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN))
- return true;
+ return 0;
if (!vfsuid_valid(vfsuid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
- return true;
- return false;
+ return 0;
+ return -EPERM;
}
/**
@@ -118,23 +121,27 @@ static bool chown_ok(struct mnt_idmap *idmap,
* permissions. On non-idmapped mounts or if permission checking is to be
* performed on the raw inode simply pass @nop_mnt_idmap.
*/
-static bool chgrp_ok(struct mnt_idmap *idmap,
- const struct inode *inode, vfsgid_t ia_vfsgid)
+static int chgrp_ok(struct mnt_idmap *idmap,
+ struct inode *inode, vfsgid_t ia_vfsgid)
{
vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
- vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
- if (vfsuid_eq_kuid(vfsuid, current_fsuid())) {
+ int ret;
+
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret < 0)
+ return ret;
+ if (ret == 0) {
if (vfsgid_eq(ia_vfsgid, vfsgid))
- return true;
+ return 0;
if (vfsgid_in_group_p(ia_vfsgid))
- return true;
+ return 0;
}
if (capable_wrt_inode_uidgid(idmap, inode, CAP_CHOWN))
- return true;
+ return 0;
if (!vfsgid_valid(vfsgid) &&
ns_capable(inode->i_sb->s_user_ns, CAP_CHOWN))
- return true;
- return false;
+ return 0;
+ return -EPERM;
}
/**
@@ -163,6 +170,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
{
struct inode *inode = d_inode(dentry);
unsigned int ia_valid = attr->ia_valid;
+ int ret;
/*
* First check size constraints. These can't be overridden using
@@ -189,14 +197,18 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,
goto kill_priv;
/* Make sure a caller can chown. */
- if ((ia_valid & ATTR_UID) &&
- !chown_ok(idmap, inode, attr->ia_vfsuid))
- return -EPERM;
+ if (ia_valid & ATTR_UID) {
+ ret = chown_ok(idmap, inode, attr->ia_vfsuid);
+ if (ret < 0)
+ return ret;
+ }
/* Make sure caller can chgrp. */
- if ((ia_valid & ATTR_GID) &&
- !chgrp_ok(idmap, inode, attr->ia_vfsgid))
- return -EPERM;
+ if (ia_valid & ATTR_GID) {
+ ret = chgrp_ok(idmap, inode, attr->ia_vfsgid);
+ if (ret < 0)
+ return ret;
+ }
/* Make sure a caller can chmod. */
if (ia_valid & ATTR_MODE) {
diff --git a/fs/coredump.c b/fs/coredump.c
index e68a76ff92a386..3f32e73aaeda28 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -954,7 +954,7 @@ static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,
* filesystem.
*/
idmap = file_mnt_idmap(file);
- if (!vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid())) {
+ if (vfs_inode_is_owned_by_me(idmap, inode) != 0) {
coredump_report_failure("Core dump to %s aborted: cannot preserve file owner", cn->corename);
return false;
}
diff --git a/fs/inode.c b/fs/inode.c
index 31c5b9ee3a81df..a7ec830ab683f0 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -2750,16 +2750,19 @@ EXPORT_SYMBOL(inode_init_owner);
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply pass @nop_mnt_idmap.
*/
-bool inode_owner_or_capable(struct mnt_idmap *idmap,
- const struct inode *inode)
+bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode)
{
vfsuid_t vfsuid;
struct user_namespace *ns;
+ int ret;
- vfsuid = i_uid_into_vfsuid(idmap, inode);
- if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret == 0)
return true;
+ if (ret < 0)
+ return false;
+ vfsuid = i_uid_into_vfsuid(idmap, inode);
ns = current_user_ns();
if (vfsuid_has_mapping(ns, vfsuid) && ns_capable(ns, CAP_FOWNER))
return true;
diff --git a/fs/internal.h b/fs/internal.h
index 355d93f9220861..be45cc63886192 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -51,6 +51,7 @@ extern int finish_clean_context(struct fs_context *fc);
/*
* namei.c
*/
+int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);
extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
struct path *path, const struct path *root);
int filename_rmdir(int dfd, struct filename *name);
diff --git a/fs/locks.c b/fs/locks.c
index 6e4ff7fcec053e..02f810dc9e7e7a 100644
--- a/fs/locks.c
+++ b/fs/locks.c
@@ -68,6 +68,7 @@
#include <trace/events/filelock.h>
#include <linux/uaccess.h>
+#include "internal.h"
static struct file_lock *file_lock(struct file_lock_core *flc)
{
@@ -2136,10 +2137,12 @@ int
vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)
{
struct inode *inode = file_inode(filp);
- vfsuid_t vfsuid = i_uid_into_vfsuid(file_mnt_idmap(filp), inode);
int error;
- if ((!vfsuid_eq_kuid(vfsuid, current_fsuid())) && !capable(CAP_LEASE))
+ error = vfs_inode_is_owned_by_me(file_mnt_idmap(filp), inode);
+ if (error < 0)
+ return error;
+ if (error != 0 && !capable(CAP_LEASE))
return -EACCES;
error = security_file_lock(filp, arg);
if (error)
diff --git a/fs/namei.c b/fs/namei.c
index 19ce43c9a6e662..72f94d204f93f4 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -55,8 +55,8 @@
* The new code replaces the old recursive symlink resolution with
* an iterative one (in case of non-nested symlink chains). It does
* this with calls to <fs>_follow_link().
- * As a side effect, dir_namei(), _namei() and follow_link() are now
- * replaced with a single function lookup_dentry() that can handle all
+ * As a side effect, dir_namei(), _namei() and follow_link() are now
+ * replaced with a single function lookup_dentry() that can handle all
* the special cases of the former code.
*
* With the new dcache, the pathname is stored at each inode, at least as
@@ -1255,6 +1255,72 @@ fs_initcall(init_fs_namei_sysctls);
#endif /* CONFIG_SYSCTL */
+/*
+ * Determine if an inode is owned by the process (allowing for fsuid override),
+ * returning 0 if so, 1 if not and a negative error code if there was a problem
+ * making the determination.
+ */
+int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)
+{
+ if (unlikely(inode->i_opflags & IOP_OWNERSHIP_OVERRIDE))
+ return inode->i_op->is_owned_by_me(idmap, inode);
+ if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid()))
+ return 0;
+ return 1; /* Not same. */
+}
+
+/*
+ * Determine if an inode has the same owner as its parent directory, returning
+ * 0 if so, 1 if not and a negative error code if there was a problem making
+ * the determination.
+ */
+static int vfs_inode_and_dir_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,
+ const struct nameidata *nd)
+{
+ if (unlikely(inode->i_opflags & IOP_OWNERSHIP_OVERRIDE)) {
+ struct dentry *parent;
+ struct inode *dir;
+ int ret;
+
+ if (inode != nd->inode) {
+ dir = nd->inode;
+ ret = inode->i_op->have_same_owner(idmap, inode, dir);
+ } else if (nd->flags & LOOKUP_RCU) {
+ parent = READ_ONCE(nd->path.dentry);
+ dir = READ_ONCE(parent->d_inode);
+ if (!dir)
+ return -ECHILD;
+ ret = inode->i_op->have_same_owner(idmap, inode, dir);
+ } else {
+ parent = dget_parent(nd->path.dentry);
+ dir = parent->d_inode;
+ ret = inode->i_op->have_same_owner(idmap, inode, dir);
+ dput(parent);
+ }
+ return ret;
+ }
+
+ if (vfsuid_valid(nd->dir_vfsuid) &&
+ vfsuid_eq(i_uid_into_vfsuid(idmap, inode), nd->dir_vfsuid))
+ return 0;
+ return 1; /* Not same. */
+}
+
+/*
+ * Determine if two inodes have the same owner, returning 0 if so, 1 if not and
+ * a negative error code if there was a problem making the determination.
+ */
+static int vfs_inodes_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,
+ struct inode *dir)
+{
+ if (unlikely(inode->i_opflags & IOP_OWNERSHIP_OVERRIDE))
+ return inode->i_op->have_same_owner(idmap, inode, dir);
+ if (vfsuid_eq(i_uid_into_vfsuid(idmap, inode),
+ i_uid_into_vfsuid(idmap, dir)))
+ return 0;
+ return 1; /* Not same. */
+}
+
/**
* may_follow_link - Check symlink following for unsafe situations
* @nd: nameidata pathwalk data
@@ -1271,27 +1337,28 @@ fs_initcall(init_fs_namei_sysctls);
*
* Returns 0 if following the symlink is allowed, -ve on error.
*/
-static inline int may_follow_link(struct nameidata *nd, const struct inode *inode)
+static inline int may_follow_link(struct nameidata *nd, struct inode *inode)
{
struct mnt_idmap *idmap;
- vfsuid_t vfsuid;
+ int ret;
if (!sysctl_protected_symlinks)
return 0;
- idmap = mnt_idmap(nd->path.mnt);
- vfsuid = i_uid_into_vfsuid(idmap, inode);
- /* Allowed if owner and follower match. */
- if (vfsuid_eq_kuid(vfsuid, current_fsuid()))
- return 0;
-
/* Allowed if parent directory not sticky and world-writable. */
if ((nd->dir_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
return 0;
+ idmap = mnt_idmap(nd->path.mnt);
+ /* Allowed if owner and follower match. */
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret <= 0)
+ return ret;
+
/* Allowed if parent directory and link owner match. */
- if (vfsuid_valid(nd->dir_vfsuid) && vfsuid_eq(nd->dir_vfsuid, vfsuid))
- return 0;
+ ret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);
+ if (ret <= 0)
+ return ret;
if (nd->flags & LOOKUP_RCU)
return -ECHILD;
@@ -1389,12 +1456,12 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link)
* @inode: the inode of the file to open
*
* Block an O_CREAT open of a FIFO (or a regular file) when:
- * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled
- * - the file already exists
- * - we are in a sticky directory
- * - we don't own the file
+ * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled,
+ * - the file already exists,
+ * - we are in a sticky directory,
+ * - the directory is world writable,
+ * - we don't own the file and
* - the owner of the directory doesn't own the file
- * - the directory is world writable
* If the sysctl_protected_fifos (or sysctl_protected_regular) is set to 2
* the directory doesn't have to be world writable: being group writable will
* be enough.
@@ -1405,13 +1472,45 @@ int may_linkat(struct mnt_idmap *idmap, const struct path *link)
* On non-idmapped mounts or if permission checking is to be performed on the
* raw inode simply pass @nop_mnt_idmap.
*
+ * For a filesystem (e.g. a network filesystem) that has a separate ID space
+ * and has foreign IDs (maybe even non-integer IDs), i_uid cannot be compared
+ * to current_fsuid() and may not be directly comparable to another i_uid.
+ * Instead, the filesystem is asked to perform the comparisons. With network
+ * filesystems, there also exists the possibility of doing anonymous
+ * operations and having anonymously-owned objects.
+ *
+ * We have the following scenarios:
+ *
+ * USER DIR FILE FILE ALLOWED
+ * OWNER OWNER STATE
+ * ======= ======= ======= ======= =======
+ * A A - New Yes
+ * A A A Exists Yes
+ * A A C Exists No
+ * A B - New Yes
+ * A B A Exists Yes, FO==U
+ * A B B Exists Yes, FO==DO
+ * A B C Exists No
+ * A anon[1] - New Yes
+ * A anon[1] A Exists Yes
+ * A anon[1] C Exists No
+ * anon A - New Yes
+ * anon A A Exists Yes, FO==DO
+ * anon anon[1] - New Yes
+ * anon anon[1] - Exists No
+ * anon A A Exists Yes, FO==DO
+ * anon A C Exists No
+ * anon A anon Exists No
+ *
+ * [1] Can anonymously-owned dirs be sticky?
+ *
* Returns 0 if the open is allowed, -ve on error.
*/
static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,
- struct inode *const inode)
+ struct inode *inode)
{
umode_t dir_mode = nd->dir_mode;
- vfsuid_t dir_vfsuid = nd->dir_vfsuid, i_vfsuid;
+ int ret;
if (likely(!(dir_mode & S_ISVTX)))
return 0;
@@ -1422,13 +1521,13 @@ static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,
if (S_ISFIFO(inode->i_mode) && !sysctl_protected_fifos)
return 0;
- i_vfsuid = i_uid_into_vfsuid(idmap, inode);
-
- if (vfsuid_eq(i_vfsuid, dir_vfsuid))
- return 0;
+ ret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);
+ if (ret <= 0)
+ return ret;
- if (vfsuid_eq_kuid(i_vfsuid, current_fsuid()))
- return 0;
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret <= 0)
+ return ret;
if (likely(dir_mode & 0002)) {
audit_log_path_denied(AUDIT_ANOM_CREAT, "sticky_create");
@@ -3645,12 +3744,14 @@ EXPORT_SYMBOL(user_path_at);
int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,
struct inode *inode)
{
- kuid_t fsuid = current_fsuid();
+ int ret;
- if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), fsuid))
- return 0;
- if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, dir), fsuid))
- return 0;
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret <= 0)
+ return ret;
+ ret = vfs_inodes_have_same_owner(idmap, inode, dir);
+ if (ret <= 0)
+ return ret;
return !capable_wrt_inode_uidgid(idmap, inode, CAP_FOWNER);
}
EXPORT_SYMBOL(__check_sticky);
diff --git a/fs/remap_range.c b/fs/remap_range.c
index 26afbbbfb10c2e..9eee93c2700125 100644
--- a/fs/remap_range.c
+++ b/fs/remap_range.c
@@ -413,20 +413,22 @@ loff_t vfs_clone_file_range(struct file *file_in, loff_t pos_in,
EXPORT_SYMBOL(vfs_clone_file_range);
/* Check whether we are allowed to dedupe the destination file */
-static bool may_dedupe_file(struct file *file)
+static int may_dedupe_file(struct file *file)
{
struct mnt_idmap *idmap = file_mnt_idmap(file);
struct inode *inode = file_inode(file);
+ int ret;
if (capable(CAP_SYS_ADMIN))
- return true;
+ return 0;
if (file->f_mode & FMODE_WRITE)
- return true;
- if (vfsuid_eq_kuid(i_uid_into_vfsuid(idmap, inode), current_fsuid()))
- return true;
+ return 0;
+ ret = vfs_inode_is_owned_by_me(idmap, inode);
+ if (ret <= 0)
+ return ret;
if (!inode_permission(idmap, inode, MAY_WRITE))
- return true;
- return false;
+ return 0;
+ return -EPERM;
}
loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
@@ -459,8 +461,8 @@ loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,
if (ret)
return ret;
- ret = -EPERM;
- if (!may_dedupe_file(dst_file))
+ ret = may_dedupe_file(dst_file);
+ if (ret < 0)
goto out_drop_write;
ret = -EXDEV;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2b78f1..8013c13a353d67 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -628,6 +628,7 @@ is_uncached_acl(struct posix_acl *acl)
#define IOP_CACHED_LINK 0x0040
#define IOP_FASTPERM_MAY_EXEC 0x0080
#define IOP_FLCTX 0x0100
+#define IOP_OWNERSHIP_OVERRIDE 0x0200 /* Use ->is_owned_by_me() and ->have_same_owner() */
/*
* Inode state bits. Protected by inode->i_lock
@@ -1755,8 +1756,7 @@ static inline bool file_write_not_started(const struct file *file)
return sb_write_not_started(file_inode(file)->i_sb);
}
-bool inode_owner_or_capable(struct mnt_idmap *idmap,
- const struct inode *inode);
+bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode);
/*
* VFS helper functions..
@@ -2036,6 +2036,9 @@ struct inode_operations {
struct dentry *dentry, struct file_kattr *fa);
int (*fileattr_get)(struct dentry *dentry, struct file_kattr *fa);
struct offset_ctx *(*get_offset_ctx)(struct inode *inode);
+ int (*is_owned_by_me)(struct mnt_idmap *idmap, struct inode *inode);
+ int (*have_same_owner)(struct mnt_idmap *idmap, struct inode *inode1,
+ struct inode *inode2);
} ____cacheline_aligned;
/* Did the driver provide valid mmap hook configuration? */
|
| 3/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10815 output: 72 thoughts: 532 Thoughts: **Analyzing a Patch Introducing New VFS Inode Operations** Okay, so I'm looking at a patch that introduces some new inode operations: `is_owned_by_me` and `have_same_owner`. These are interesting because they're designed to *override* the standard VFS ownership checks, which currently just compare the `i_uid` to `current_fsuid()`. The patch touches several core VFS components: `fs/namei.c`, `fs/attr.c`, `fs/coredump.c`, `fs/inode.c`, `fs/locks.c`, and `fs/remap_range.c`. It's also implementing these for the AFS filesystem in `fs/afs/`. This clearly represents a functional change in both the VFS layer and the AFS filesystem, and it modifies reachable kernel logic. Given the impact on core functionality, `WorthFuzzing=true` is a reasonable assessment. I'll need to dig into this further to see where the real complexities lie. I'm making a mental list of the specific functions affected and where they're located. I need to focus on these: * `vfs_inode_is_owned_by_me` * `vfs_inode_and_dir_have_same_owner` * `vfs_inodes_have_same_owner` * `afs_is_owned_by_me` * `afs_have_same_owner` * `chown_ok` * `chgrp_ok` * `may_create_in_sticky` * `may_follow_link` * `__check_sticky` * `may_dedupe_file` Okay, now let's see how these are exposed. `vfs_inode_is_owned_by_me` is in `fs/namei.c` and is actually exported in `fs/internal.h`. The other two VFS functions, `vfs_inode_and_dir_have_same_owner` and `vfs_inodes_have_same_owner` are static inside `fs/namei.c`. Both AFS functions are in `fs/afs/security.c`. I also see that to effectively test the AFS integration, I'll need `CONFIG_AFS_FS` enabled in the kernel config. Right, time to move beyond this cursory inspection and make sure the function names are correct! |
| 4/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "(vfs_inode_is_owned_by_me|vfs_inode_and_dir_have_same_owner|vfs_inodes_have_same_owner|afs_is_owned_by_me|afs_have_same_owner)"
}Results: {
"Output": "fs/afs/dir.c=60=const struct inode_operations afs_dir_inode_operations = {\n--\nfs/afs/dir.c-71-\t.setattr\t= afs_setattr,\nfs/afs/dir.c:72:\t.is_owned_by_me\t= afs_is_owned_by_me,\nfs/afs/dir.c:73:\t.have_same_owner = afs_have_same_owner,\nfs/afs/dir.c-74-};\n--\nfs/afs/file.c=48=const struct inode_operations afs_file_inode_operations = {\n--\nfs/afs/file.c-51-\t.permission\t= afs_permission,\nfs/afs/file.c:52:\t.is_owned_by_me\t= afs_is_owned_by_me,\nfs/afs/file.c:53:\t.have_same_owner = afs_have_same_owner,\nfs/afs/file.c-54-};\n--\nfs/afs/internal.h=1532=extern int afs_permission(struct mnt_idmap *, struct inode *, int);\nfs/afs/internal.h:1533:int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);\nfs/afs/internal.h:1534:int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,\nfs/afs/internal.h-1535-\t\t\tstruct inode *inode2);\n--\nfs/afs/security.c=431=int afs_permission(struct mnt_idmap *idmap, struct inode *inode,\n--\nfs/afs/security.c-518- */\nfs/afs/security.c:519:int afs_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)\nfs/afs/security.c-520-{\n--\nfs/afs/security.c-549- */\nfs/afs/security.c:550:int afs_have_same_owner(struct mnt_idmap *idmap, struct inode *inode1,\nfs/afs/security.c-551-\t\t\tstruct inode *inode2)\n--\nfs/attr.c=95=static int chown_ok(struct mnt_idmap *idmap,\n--\nfs/attr.c-100-\nfs/attr.c:101:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/attr.c-102-\tif (ret \u003c= 0)\n--\nfs/attr.c=124=static int chgrp_ok(struct mnt_idmap *idmap,\n--\nfs/attr.c-129-\nfs/attr.c:130:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/attr.c-131-\tif (ret \u003c 0)\n--\nfs/coredump.c=878=static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,\n--\nfs/coredump.c-956-\tidmap = file_mnt_idmap(file);\nfs/coredump.c:957:\tif (vfs_inode_is_owned_by_me(idmap, inode) != 0) {\nfs/coredump.c-958-\t\tcoredump_report_failure(\"Core dump to %s aborted: cannot preserve file owner\", cn-\u003ecorename);\n--\nfs/inode.c=2753=bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode)\n--\nfs/inode.c-2758-\nfs/inode.c:2759:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/inode.c-2760-\tif (ret == 0)\n--\nfs/internal.h=49=extern int finish_clean_context(struct fs_context *fc);\n--\nfs/internal.h-53- */\nfs/internal.h:54:int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);\nfs/internal.h-55-extern int filename_lookup(int dfd, struct filename *name, unsigned flags,\n--\nfs/locks.c=2137=vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)\n--\nfs/locks.c-2141-\nfs/locks.c:2142:\terror = vfs_inode_is_owned_by_me(file_mnt_idmap(filp), inode);\nfs/locks.c-2143-\tif (error \u003c 0)\n--\nfs/namei.c=1254=fs_initcall(init_fs_namei_sysctls);\n--\nfs/namei.c-1262- */\nfs/namei.c:1263:int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)\nfs/namei.c-1264-{\n--\nfs/namei.c-1276- */\nfs/namei.c:1277:static int vfs_inode_and_dir_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,\nfs/namei.c-1278-\t\t\t\t\t const struct nameidata *nd)\n--\nfs/namei.c-1312- */\nfs/namei.c:1313:static int vfs_inodes_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,\nfs/namei.c-1314-\t\t\t\t struct inode *dir)\n--\nfs/namei.c=1340=static inline int may_follow_link(struct nameidata *nd, struct inode *inode)\n--\nfs/namei.c-1353-\t/* Allowed if owner and follower match. */\nfs/namei.c:1354:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-1355-\tif (ret \u003c= 0)\n--\nfs/namei.c-1358-\t/* Allowed if parent directory and link owner match. */\nfs/namei.c:1359:\tret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);\nfs/namei.c-1360-\tif (ret \u003c= 0)\n--\nfs/namei.c=1509=static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,\n--\nfs/namei.c-1523-\nfs/namei.c:1524:\tret = vfs_inode_and_dir_have_same_owner(idmap, inode, nd);\nfs/namei.c-1525-\tif (ret \u003c= 0)\n--\nfs/namei.c-1527-\nfs/namei.c:1528:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-1529-\tif (ret \u003c= 0)\n--\nfs/namei.c=3744=int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,\n--\nfs/namei.c-3748-\nfs/namei.c:3749:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-3750-\tif (ret \u003c= 0)\nfs/namei.c-3751-\t\treturn ret;\nfs/namei.c:3752:\tret = vfs_inodes_have_same_owner(idmap, inode, dir);\nfs/namei.c-3753-\tif (ret \u003c= 0)\n--\nfs/remap_range.c=416=static int may_dedupe_file(struct file *file)\n--\nfs/remap_range.c-425-\t\treturn 0;\nfs/remap_range.c:426:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/remap_range.c-427-\tif (ret \u003c= 0)\n"
} |
| 5/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14142 output: 19 thoughts: 151 Thoughts: **My Initial Assessment of Ownership and Access Control Functions** Okay, so I'm diving into this code, and it looks like the critical functions I need to focus on right now are these: `vfs_inode_is_owned_by_me`, `vfs_inode_and_dir_have_same_owner`, `vfs_inodes_have_same_owner`, `afs_is_owned_by_me`, `afs_have_same_owner`, `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `__check_sticky`, `may_dedupe_file`, and `may_follow_link`. Seems like a pretty comprehensive list for dealing with file ownership, group membership, and permissions checks, especially given the range of VFS and AFS implementations in play here. I want to check `CONFIG_AFS_FS` is the correct config to address the architecture, but more generally this seems like a solid foundation for evaluating how the system determines user access. |
| 6/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "config AFS_FS"
}Results: {
"Output": "fs/afs/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nfs/afs/Kconfig:2:config AFS_FS\nfs/afs/Kconfig-3-\ttristate \"Andrew File System support (AFS)\"\n--\nfs/afs/Kconfig=17=config AFS_DEBUG\n--\nfs/afs/Kconfig-26-\nfs/afs/Kconfig:27:config AFS_FSCACHE\nfs/afs/Kconfig-28-\tbool \"Provide AFS client caching support\"\n"
} |
| 7/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14671 output: 19 thoughts: 214 Thoughts: **Refining the Ownership and Permissions Logic** Okay, so I've confirmed that enabling `AFS_FS` is the right configuration for this. Now, before I move on, I'd like to quickly double-check that there aren't any other obscure configurations lurking around that I've missed. I know the changes primarily relate to the Virtual File System (VFS) and the AFS layers. The VFS is always enabled, so AFS is the key, and that relies on `AFS_FS`. Got it. Now, let's get back to refining this `FocusSymbols` list. I need to make this as accurate and complete as possible. So, I'll need to focus on this list: * `vfs_inode_is_owned_by_me` * `vfs_inode_and_dir_have_same_owner` * `vfs_inodes_have_same_owner` * `afs_is_owned_by_me` * `afs_have_same_owner` * `chown_ok` * `chgrp_ok` * `may_create_in_sticky` * `__check_sticky` * `may_dedupe_file` * `may_follow_link` Hang on a second… isn't `may_follow_link` implemented inline? Yes, it is. I'll need to check `fs/namei.c` to see how it's handled there. |
| 8/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "may_follow_link"
}Results: {
"Output": "Documentation/filesystems/path-lookup.rst=1130=component of the lookup, so we will check userspace flag ``LOOKUP_FOLLOW`` to\nDocumentation/filesystems/path-lookup.rst:1131:decide whether follow it when it is a symlink and call ``may_follow_link()`` to\nDocumentation/filesystems/path-lookup.rst-1132-check if we have privilege to follow it.\n--\nfs/namei.c=1313=static int vfs_inodes_have_same_owner(struct mnt_idmap *idmap, struct inode *inode,\n--\nfs/namei.c-1324-/**\nfs/namei.c:1325: * may_follow_link - Check symlink following for unsafe situations\nfs/namei.c-1326- * @nd: nameidata pathwalk data\n--\nfs/namei.c-1339- */\nfs/namei.c:1340:static inline int may_follow_link(struct nameidata *nd, struct inode *inode)\nfs/namei.c-1341-{\n--\nfs/namei.c=2106=static noinline const char *pick_link(struct nameidata *nd, struct path *link,\n--\nfs/namei.c-2133-\tif (flags \u0026 WALK_TRAILING) {\nfs/namei.c:2134:\t\terror = may_follow_link(nd, inode);\nfs/namei.c-2135-\t\tif (unlikely(error))\n"
} |
| 9/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15632 output: 18 thoughts: 121 Thoughts: **Analyzing File System Permissions** Okay, so I'm looking at these functions and their scopes within the file system code. I see that `may_follow_link` is *inline*, which means it's probably not a good candidate to focus on directly. It's integrated into other functions. On the other hand, `chown_ok` and `chgrp_ok` are clearly defined as *static* variables within `fs/attr.c`. That gives me a definite place to start if I want to examine those access control checks. Similarly, `may_create_in_sticky` is static in `fs/namei.c`. It is useful to know where these flags live. Now, `__check_sticky` is *exported* from `fs/namei.c`. That means it's available for use outside of that file. Okay, good to know. It is good information, but not immediately useful. Finally, `may_dedupe_file` is static in `fs/remap_range.c`. So I know where *that* function resides. Alright, now let's circle back to `chown_ok`... let's check it's definition to see if it is inline. |
| 10/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "chown_ok"
}Results: {
"Output": "fs/attr.c=81=EXPORT_SYMBOL(setattr_should_drop_suidgid);\n--\nfs/attr.c-83-/**\nfs/attr.c:84: * chown_ok - verify permissions to chown inode\nfs/attr.c-85- * @idmap:\tidmap of the mount @inode was found from\n--\nfs/attr.c-94- */\nfs/attr.c:95:static int chown_ok(struct mnt_idmap *idmap,\nfs/attr.c-96-\t\t struct inode *inode, vfsuid_t ia_vfsuid)\n--\nfs/attr.c=168=int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,\n--\nfs/attr.c-200-\tif (ia_valid \u0026 ATTR_UID) {\nfs/attr.c:201:\t\tret = chown_ok(idmap, inode, attr-\u003eia_vfsuid);\nfs/attr.c-202-\t\tif (ret \u003c 0)\n"
} |
| 11/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16314 output: 18 thoughts: 17 Thoughts: Okay, here's my take on those thoughts, framed as my own reasoning: **Analyzing the Situation** Alright, so it's not straightforward, not an "inline" solution, which immediately suggests we need to dig a bit deeper. My first instinct is to zero in on a specific element. Let's see... the `chgrp_ok` setting. That's a good place to start my investigation. It likely relates to how we can proceed. Time to look into it. |
| 12/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "chgrp_ok"
}Results: {
"Output": "fs/attr.c=95=static int chown_ok(struct mnt_idmap *idmap,\n--\nfs/attr.c-112-/**\nfs/attr.c:113: * chgrp_ok - verify permissions to chgrp inode\nfs/attr.c-114- * @idmap:\tidmap of the mount @inode was found from\n--\nfs/attr.c-123- */\nfs/attr.c:124:static int chgrp_ok(struct mnt_idmap *idmap,\nfs/attr.c-125-\t\t struct inode *inode, vfsgid_t ia_vfsgid)\n--\nfs/attr.c=168=int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry,\n--\nfs/attr.c-207-\tif (ia_valid \u0026 ATTR_GID) {\nfs/attr.c:208:\t\tret = chgrp_ok(idmap, inode, attr-\u003eia_vfsgid);\nfs/attr.c-209-\t\tif (ret \u003c 0)\n"
} |
| 13/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16739 output: 21 thoughts: 20 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Navigating the `may_create_in_sticky` Logic** Alright, so the initial assumption was incorrect – it's not an inline scenario. That's good to know, narrows down the possible causes. Let's dig deeper. The next logical step is to examine the `may_create_in_sticky` function. I need to understand its behavior, its conditions, and how it interacts with the rest of the system. I'll need to scrutinize its implementation to see if it's operating as expected and determine whether it contains any flaws. This is where the root cause probably lies. |
| 14/2 | 2026/07/24 16:31 | tool | grepper |
0mArgs:{
"Expression": "may_create_in_sticky"
}Results: {
"Output": "fs/namei.c=1427=int may_linkat(struct mnt_idmap *idmap, const struct path *link)\n--\nfs/namei.c-1450-/**\nfs/namei.c:1451: * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory\nfs/namei.c-1452- *\t\t\t should be allowed, or not, on files that already\n--\nfs/namei.c-1508- */\nfs/namei.c:1509:static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,\nfs/namei.c-1510-\t\t\t\tstruct inode *inode)\n--\nfs/namei.c=4753=static int do_open(struct nameidata *nd,\n--\nfs/namei.c-4774-\t\t\treturn -EISDIR;\nfs/namei.c:4775:\t\terror = may_create_in_sticky(idmap, nd,\nfs/namei.c-4776-\t\t\t\t\t d_backing_inode(nd-\u003epath.dentry));\n"
} |
| 15/2 | 2026/07/24 16:31 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17232 output: 20 thoughts: 19 Thoughts: Okay, here's my interpretation of those thoughts, summarized from a professional standpoint: **Deconstructing the Problem: A Quick Diagnostic** Hmm, this isn't an in-line situation. That's the initial assessment. My immediate next step is to drill down and examine the function `may_dedupe_file`. I need to understand its behavior and logic to figure out what's really happening. I'm focusing specifically on that function as a key element to understanding what's off. |
| 16/2 | 2026/07/24 16:32 | tool | grepper |
0mArgs:{
"Expression": "may_dedupe_file"
}Results: {
"Output": "fs/remap_range.c=413=EXPORT_SYMBOL(vfs_clone_file_range);\n--\nfs/remap_range.c-415-/* Check whether we are allowed to dedupe the destination file */\nfs/remap_range.c:416:static int may_dedupe_file(struct file *file)\nfs/remap_range.c-417-{\n--\nfs/remap_range.c=434=loff_t vfs_dedupe_file_range_one(struct file *src_file, loff_t src_pos,\n--\nfs/remap_range.c-456-\t * This needs to be called after remap_verify_area() because of\nfs/remap_range.c:457:\t * sb_start_write() and before may_dedupe_file() because the mount's\nfs/remap_range.c-458-\t * MAY_WRITE need to be checked with mnt_get_write_access_file() held.\n--\nfs/remap_range.c-463-\nfs/remap_range.c:464:\tret = may_dedupe_file(dst_file);\nfs/remap_range.c-465-\tif (ret \u003c 0)\n"
} |
| 17/2 | 2026/07/24 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17704 output: 26 thoughts: 25 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing Potential Ownership and Context** Alright, so the clue isn't coming through directly in the inline elements. I need to figure out where the ownership information is held. Let's see...I need to quickly check the `vfs_inode_is_owned_by_me` function. That seems like the logical place to start digging into whether this is a matter of possession I need to investigate. |
| 18/2 | 2026/07/24 16:32 | tool | grepper |
0mArgs:{
"Expression": "vfs_inode_is_owned_by_me"
}Results: {
"Output": "fs/attr.c=95=static int chown_ok(struct mnt_idmap *idmap,\n--\nfs/attr.c-100-\nfs/attr.c:101:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/attr.c-102-\tif (ret \u003c= 0)\n--\nfs/attr.c=124=static int chgrp_ok(struct mnt_idmap *idmap,\n--\nfs/attr.c-129-\nfs/attr.c:130:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/attr.c-131-\tif (ret \u003c 0)\n--\nfs/coredump.c=878=static bool coredump_file(struct core_name *cn, struct coredump_params *cprm,\n--\nfs/coredump.c-956-\tidmap = file_mnt_idmap(file);\nfs/coredump.c:957:\tif (vfs_inode_is_owned_by_me(idmap, inode) != 0) {\nfs/coredump.c-958-\t\tcoredump_report_failure(\"Core dump to %s aborted: cannot preserve file owner\", cn-\u003ecorename);\n--\nfs/inode.c=2753=bool inode_owner_or_capable(struct mnt_idmap *idmap, struct inode *inode)\n--\nfs/inode.c-2758-\nfs/inode.c:2759:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/inode.c-2760-\tif (ret == 0)\n--\nfs/internal.h=49=extern int finish_clean_context(struct fs_context *fc);\n--\nfs/internal.h-53- */\nfs/internal.h:54:int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode);\nfs/internal.h-55-extern int filename_lookup(int dfd, struct filename *name, unsigned flags,\n--\nfs/locks.c=2137=vfs_setlease(struct file *filp, int arg, struct file_lease **lease, void **priv)\n--\nfs/locks.c-2141-\nfs/locks.c:2142:\terror = vfs_inode_is_owned_by_me(file_mnt_idmap(filp), inode);\nfs/locks.c-2143-\tif (error \u003c 0)\n--\nfs/namei.c=1254=fs_initcall(init_fs_namei_sysctls);\n--\nfs/namei.c-1262- */\nfs/namei.c:1263:int vfs_inode_is_owned_by_me(struct mnt_idmap *idmap, struct inode *inode)\nfs/namei.c-1264-{\n--\nfs/namei.c=1340=static inline int may_follow_link(struct nameidata *nd, struct inode *inode)\n--\nfs/namei.c-1353-\t/* Allowed if owner and follower match. */\nfs/namei.c:1354:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-1355-\tif (ret \u003c= 0)\n--\nfs/namei.c=1509=static int may_create_in_sticky(struct mnt_idmap *idmap, struct nameidata *nd,\n--\nfs/namei.c-1527-\nfs/namei.c:1528:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-1529-\tif (ret \u003c= 0)\n--\nfs/namei.c=3744=int __check_sticky(struct mnt_idmap *idmap, struct inode *dir,\n--\nfs/namei.c-3748-\nfs/namei.c:3749:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/namei.c-3750-\tif (ret \u003c= 0)\n--\nfs/remap_range.c=416=static int may_dedupe_file(struct file *file)\n--\nfs/remap_range.c-425-\t\treturn 0;\nfs/remap_range.c:426:\tret = vfs_inode_is_owned_by_me(idmap, inode);\nfs/remap_range.c-427-\tif (ret \u003c= 0)\n"
} |
| 19/2 | 2026/07/24 16:32 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 19065 output: 246 thoughts: 487 Thoughts: **Analyzing the Ownership Patch** Okay, let's break this down. From what I see, these ownership checks are core to the file system, specifically defined within `fs/internal.h` and implemented in `fs/namei.c`. So we're talking about a few key functions: `vfs_inode_is_owned_by_me`, `vfs_inode_and_dir_have_same_owner`, `vfs_inodes_have_same_owner`, along with some others like `afs_is_owned_by_me`, `afs_have_same_owner`, and some permission-related checks: `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `__check_sticky`, and `may_dedupe_file`. The fact that this patch touches `fs/afs/` immediately suggests that `AFS_FS` is implicated. I wonder if there are any other configuration dependencies we should keep an eye on? But the core of the matter seems to be in VFS. Alright, the patch adds two *new* inode operations: `is_owned_by_me` and `have_same_owner`. These are implemented within the AFS subsystem and then, crucially, modified to be used *within* the VFS. This means we're dealing with a functional change that alters how ownership is determined, at least in some cases. It's a fundamental shift in how the VFS interacts with ownership checks, potentially overriding the default behavior. The fact that the VFS itself is being modified is a big flag for fuzzing, which will be essential here. Therefore, this patch warrants fuzzing, and these are the functions I'll keep top of mind: `vfs_inode_is_owned_by_me`, `vfs_inode_and_dir_have_same_owner`, `vfs_inodes_have_same_owner`, `afs_is_owned_by_me`, `afs_have_same_owner`, `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `__check_sticky`, and `may_dedupe_file`. And, as a final consideration, enabling `AFS_FS` seems essential to proper testing. |
| 20/2 | 2026/07/24 16:32 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"AFS_FS"
],
"FocusSymbols": [
"vfs_inode_is_owned_by_me",
"vfs_inode_and_dir_have_same_owner",
"vfs_inodes_have_same_owner",
"afs_is_owned_by_me",
"afs_have_same_owner",
"chown_ok",
"chgrp_ok",
"may_create_in_sticky",
"__check_sticky",
"may_dedupe_file"
],
"Reasoning": "The patch introduces two new inode operations `is_owned_by_me` and `have_same_owner` to override the default VFS ownership checks. It implements these operations in the AFS filesystem and modifies core VFS functions (like `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `may_dedupe_file`, etc.) to use them. This is a functional change in core VFS and AFS that should be fuzzed.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"AFS_FS"
],
"FocusSymbols": [
"vfs_inode_is_owned_by_me",
"vfs_inode_and_dir_have_same_owner",
"vfs_inodes_have_same_owner",
"afs_is_owned_by_me",
"afs_have_same_owner",
"chown_ok",
"chgrp_ok",
"may_create_in_sticky",
"__check_sticky",
"may_dedupe_file"
],
"Reasoning": "The patch introduces two new inode operations `is_owned_by_me` and `have_same_owner` to override the default VFS ownership checks. It implements these operations in the AFS filesystem and modifies core VFS functions (like `chown_ok`, `chgrp_ok`, `may_create_in_sticky`, `may_dedupe_file`, etc.) to use them. This is a functional change in core VFS and AFS that should be fuzzed.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|