| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/24 19:49 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"copy_from_kernel_nofault",
"copy_to_kernel_nofault",
"strncpy_from_kernel_nofault",
"bpf_strcmp",
"bpf_strcasecmp",
"bpf_strncasecmp",
"bpf_strstr",
"bpf_strcasestr",
"bpf_strnstr",
"bpf_strncasestr",
"bpf_strnchr",
"bpf_strchrnul",
"bpf_strrchr",
"bpf_strnlen",
"bpf_strspn",
"bpf_strcspn"
],
"KMSANReasoning": "The patch refactors `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, and several BPF string helper functions to optimize Tag Check Override (TCO) management on ARM64. It introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to be used within a guarded region (`guard(__kernel_nofault_bare)`), avoiding the overhead of toggling TCO on every memory access inside loops.\n\nThese changes do not introduce new data structures sent to user space, nor do they add complex allocations or conditional logic that might depend on uninitialized memory. The existing `kmsan_check_memory` calls are preserved. Any potential bugs introduced by this refactoring (such as incorrect bounds checking, missing early returns, or improper page fault handling) would result in out-of-bounds accesses, invalid memory dereferences, or crashes. These types of memory corruption and access violations are already effectively detected by KASAN and standard kernel testing. Therefore, a dedicated KMSAN fuzzing session is not necessary.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to optimize TCO (Tag Check Override) management on ARM64, and updates `maccess.c` and BPF string helpers to use them under `guard(__kernel_nofault_bare)()`. This is a functional change to core memory access routines and BPF helpers, which could introduce bugs if the nofault regions are not handled correctly.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/24 19:49 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 0516034bbdacab8b2b6644d9b8a40ab63da22ee0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Aug 24 19:49:25 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h\nindex 9f5bd9c69c249..1a14eb2a51dce 100644\n--- a/arch/arm64/include/asm/uaccess.h\n+++ b/arch/arm64/include/asm/uaccess.h\n@@ -270,28 +270,43 @@ do {\t\t\t\t\t\t\t\t\t\\\n #define get_user\t__get_user\n \n /*\n- * We must not call into the scheduler between __mte_enable_tco_async() and\n- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking\n- * functions, we must evaluate these outside of the critical section.\n+ * Nofault load without TCO management for use inside a\n+ * __begin/__end_kernel_nofault_bare() region.\n */\n-#define __get_kernel_nofault(dst, src, type, err_label)\t\t\t\\\n+#define __get_kernel_nofault_bare(dst, src, type, err_label)\t\t\\\n do {\t\t\t\t\t\t\t\t\t\\\n \t__typeof__(dst) __gkn_dst = (dst);\t\t\t\t\\\n \t__typeof__(src) __gkn_src = (src);\t\t\t\t\\\n \tdo { \t\t\t\t\t\t\t\t\\\n \t\t__label__ __gkn_label;\t\t\t\t\t\\\n-\t\t\t\t\t\t\t\t\t\\\n-\t\t__mte_enable_tco_async();\t\t\t\t\\\n \t\t__raw_get_mem(\"ldr\", *((type *)(__gkn_dst)),\t\t\\\n \t\t (__force type *)(__gkn_src), __gkn_label, K);\t\\\n-\t\t__mte_disable_tco_async();\t\t\t\t\\\n \t\tbreak;\t\t\t\t\t\t\t\\\n \t__gkn_label:\t\t\t\t\t\t\t\\\n-\t\t__mte_disable_tco_async();\t\t\t\t\\\n \t\tgoto err_label;\t\t\t\t\t\t\\\n \t} while (0);\t\t\t\t\t\t\t\\\n } while (0)\n \n+/*\n+ * We must not call into the scheduler between __mte_enable_tco_async() and\n+ * __mte_disable_tco_async(). As dst and src may contain blocking functions,\n+ * evaluate them before overriding TCO.\n+ */\n+#define __get_kernel_nofault(dst, src, type, err_label)\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\t__label__ __gkn_tco_err;\t\t\t\t\t\\\n+\t__typeof__(dst) __gkn_tco_dst = (dst);\t\t\t\t\\\n+\t__typeof__(src) __gkn_tco_src = (src);\t\t\t\t\\\n+\t__mte_enable_tco_async();\t\t\t\t\t\\\n+\t__get_kernel_nofault_bare(__gkn_tco_dst, __gkn_tco_src, type,\t\\\n+\t\t\t\t __gkn_tco_err);\t\t\t\\\n+\t__mte_disable_tco_async();\t\t\t\t\t\\\n+\tbreak;\t\t\t\t\t\t\t\t\\\n+__gkn_tco_err:\t\t\t\t\t\t\t\t\\\n+\t__mte_disable_tco_async();\t\t\t\t\t\\\n+\tgoto err_label;\t\t\t\t\t\t\t\\\n+} while (0)\n+\n #define __put_mem_asm(store, reg, x, addr, label, type)\t\t\t\\\n \tasm goto(\t\t\t\t\t\t\t\\\n \t\"1:\t\" store \"\t\" reg \"0, [%1]\\n\"\t\t\t\\\n@@ -366,28 +381,48 @@ do {\t\t\t\t\t\t\t\t\t\\\n \n #define put_user\t__put_user\n \n-/*\n- * We must not call into the scheduler between __mte_enable_tco_async() and\n- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking\n- * functions, we must evaluate these outside of the critical section.\n- */\n-#define __put_kernel_nofault(dst, src, type, err_label)\t\t\t\\\n+/* Nofault store without TCO management; see __get_kernel_nofault_bare. */\n+#define __put_kernel_nofault_bare(dst, src, type, err_label)\t\t\\\n do {\t\t\t\t\t\t\t\t\t\\\n \t__typeof__(dst) __pkn_dst = (dst);\t\t\t\t\\\n \t__typeof__(src) __pkn_src = (src);\t\t\t\t\\\n \t\t\t\t\t\t\t\t\t\\\n \tdo {\t\t\t\t\t\t\t\t\\\n \t\t__label__ __pkn_err;\t\t\t\t\t\\\n-\t\t__mte_enable_tco_async();\t\t\t\t\\\n \t\t__raw_put_mem(\"str\", *((type *)(__pkn_src)),\t\t\\\n \t\t\t (__force type *)(__pkn_dst), __pkn_err, K);\t\\\n-\t\t__mte_disable_tco_async();\t\t\t\t\\\n \t\tbreak;\t\t\t\t\t\t\t\\\n \t__pkn_err:\t\t\t\t\t\t\t\\\n-\t\t__mte_disable_tco_async();\t\t\t\t\\\n \t\tgoto err_label;\t\t\t\t\t\t\\\n \t} while (0);\t\t\t\t\t\t\t\\\n-} while(0)\n+} while (0)\n+\n+/*\n+ * We must not call into the scheduler between __mte_enable_tco_async() and\n+ * __mte_disable_tco_async(). As `dst` and `src` may contain blocking\n+ * functions, we must evaluate these outside of the critical section.\n+ */\n+#define __put_kernel_nofault(dst, src, type, err_label)\t\t\t\\\n+do {\t\t\t\t\t\t\t\t\t\\\n+\t__label__ __pkn_tco_err;\t\t\t\t\t\\\n+\t__typeof__(dst) __pkn_tco_dst = (dst);\t\t\t\t\\\n+\t__typeof__(src) __pkn_tco_src = (src);\t\t\t\t\\\n+\t__mte_enable_tco_async();\t\t\t\t\t\\\n+\t__put_kernel_nofault_bare(__pkn_tco_dst, __pkn_tco_src, type,\t\\\n+\t\t\t\t __pkn_tco_err);\t\t\t\\\n+\t__mte_disable_tco_async();\t\t\t\t\t\\\n+\tbreak;\t\t\t\t\t\t\t\t\\\n+__pkn_tco_err:\t\t\t\t\t\t\t\t\\\n+\t__mte_disable_tco_async();\t\t\t\t\t\\\n+\tgoto err_label;\t\t\t\t\t\t\t\\\n+} while (0)\n+\n+/*\n+ * A context switch re-enables tag checking, hence the no-scheduling\n+ * requirement for a bare nofault region.\n+ */\n+#define __begin_kernel_nofault_bare()\t__mte_enable_tco_async()\n+#define __end_kernel_nofault_bare()\t__mte_disable_tco_async()\n \n extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);\n #define raw_copy_from_user(to, from, n)\t\t\t\t\t\\\ndiff --git a/include/linux/uaccess.h b/include/linux/uaccess.h\nindex eddbbb65ccc4f..b25e778ddc38a 100644\n--- a/include/linux/uaccess.h\n+++ b/include/linux/uaccess.h\n@@ -637,6 +637,26 @@ do {\t\t\t\t\t\t\t\\\n \n #endif /* !__get_kernel_nofault */\n \n+/*\n+ * Architectures may use the begin/end hooks to establish state shared by a\n+ * sequence of bare nofault accesses. Every path out of the region must call\n+ * the end hook. The region, including expressions passed to the bare\n+ * accessors, must not call into the scheduler.\n+ */\n+#ifndef __get_kernel_nofault_bare\n+#define __get_kernel_nofault_bare\t__get_kernel_nofault\n+#define __put_kernel_nofault_bare\t__put_kernel_nofault\n+#endif\n+\n+#ifndef __begin_kernel_nofault_bare\n+#define __begin_kernel_nofault_bare()\tdo {} while (0)\n+#define __end_kernel_nofault_bare()\tdo {} while (0)\n+#endif\n+\n+DEFINE_LOCK_GUARD_0(__kernel_nofault_bare,\n+\t\t __begin_kernel_nofault_bare(),\n+\t\t __end_kernel_nofault_bare())\n+\n /**\n * get_kernel_nofault(): safely attempt to read from a location\n * @val: read into this variable\ndiff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c\nindex b3cc5c8fc8756..f8c557161584e 100644\n--- a/kernel/bpf/helpers.c\n+++ b/kernel/bpf/helpers.c\n@@ -3726,7 +3726,8 @@ __bpf_kfunc void __bpf_trap(void)\n *\n * Since strings are not necessarily %NUL-terminated, we cannot directly call\n * in-kernel implementations. Instead, we open-code the implementations using\n- * __get_kernel_nofault instead of plain dereference to make them safe.\n+ * __get_kernel_nofault_bare under guard(__kernel_nofault_bare)() to make them\n+ * safe.\n */\n \n static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)\n@@ -3738,11 +3739,14 @@ static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, s\n \t !copy_from_kernel_nofault_allowed(s2, 1)) {\n \t\treturn -ERANGE;\n \t}\n+\tif (!len)\n+\t\treturn 0;\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c len \u0026\u0026 i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026c1, s1, char, err_out);\n-\t\t__get_kernel_nofault(\u0026c2, s2, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026c1, s1, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026c2, s2, char, err_out);\n \t\tif (ignore_case) {\n \t\t\tc1 = tolower(c1);\n \t\t\tc2 = tolower(c2);\n@@ -3837,10 +3841,13 @@ __bpf_kfunc int bpf_strnchr(const char *s__ign, size_t count, char c)\n \n \tif (!copy_from_kernel_nofault_allowed(s__ign, 1))\n \t\treturn -ERANGE;\n+\tif (!count)\n+\t\treturn -ENOENT;\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c count \u0026\u0026 i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026sc, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026sc, s__ign, char, err_out);\n \t\tif (sc == c)\n \t\t\treturn i;\n \t\tif (sc == '\\0')\n@@ -3893,8 +3900,9 @@ __bpf_kfunc int bpf_strchrnul(const char *s__ign, char c)\n \t\treturn -ERANGE;\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026sc, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026sc, s__ign, char, err_out);\n \t\tif (sc == '\\0' || sc == c)\n \t\t\treturn i;\n \t\ts__ign++;\n@@ -3925,8 +3933,9 @@ __bpf_kfunc int bpf_strrchr(const char *s__ign, int c)\n \t\treturn -ERANGE;\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026sc, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026sc, s__ign, char, err_out);\n \t\tif (sc == c)\n \t\t\tlast = i;\n \t\tif (sc == '\\0')\n@@ -3956,10 +3965,13 @@ __bpf_kfunc int bpf_strnlen(const char *s__ign, size_t count)\n \n \tif (!copy_from_kernel_nofault_allowed(s__ign, 1))\n \t\treturn -ERANGE;\n+\tif (!count)\n+\t\treturn 0;\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c count \u0026\u0026 i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026c, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026c, s__ign, char, err_out);\n \t\tif (c == '\\0')\n \t\t\treturn i;\n \t\ts__ign++;\n@@ -4008,12 +4020,13 @@ __bpf_kfunc int bpf_strspn(const char *s__ign, const char *accept__ign)\n \t}\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026cs, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026cs, s__ign, char, err_out);\n \t\tif (cs == '\\0')\n \t\t\treturn i;\n \t\tfor (j = 0; j \u003c XATTR_SIZE_MAX; j++) {\n-\t\t\t__get_kernel_nofault(\u0026ca, accept__ign + j, char, err_out);\n+\t\t\t__get_kernel_nofault_bare(\u0026ca, accept__ign + j, char, err_out);\n \t\t\tif (cs == ca || ca == '\\0')\n \t\t\t\tbreak;\n \t\t}\n@@ -4052,12 +4065,13 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)\n \t}\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c XATTR_SIZE_MAX; i++) {\n-\t\t__get_kernel_nofault(\u0026cs, s__ign, char, err_out);\n+\t\t__get_kernel_nofault_bare(\u0026cs, s__ign, char, err_out);\n \t\tif (cs == '\\0')\n \t\t\treturn i;\n \t\tfor (j = 0; j \u003c XATTR_SIZE_MAX; j++) {\n-\t\t\t__get_kernel_nofault(\u0026cr, reject__ign + j, char, err_out);\n+\t\t\t__get_kernel_nofault_bare(\u0026cr, reject__ign + j, char, err_out);\n \t\t\tif (cs == cr || cr == '\\0')\n \t\t\t\tbreak;\n \t\t}\n@@ -4084,9 +4098,10 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,\n \t}\n \n \tguard(pagefault)();\n+\tguard(__kernel_nofault_bare)();\n \tfor (i = 0; i \u003c XATTR_SIZE_MAX; i++) {\n \t\tfor (j = 0; i + j \u003c= len \u0026\u0026 j \u003c XATTR_SIZE_MAX; j++) {\n-\t\t\t__get_kernel_nofault(\u0026c2, s2 + j, char, err_out);\n+\t\t\t__get_kernel_nofault_bare(\u0026c2, s2 + j, char, err_out);\n \t\t\tif (c2 == '\\0')\n \t\t\t\treturn i;\n \t\t\t/*\n@@ -4096,7 +4111,7 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,\n \t\t\t */\n \t\t\tif (i + j == len)\n \t\t\t\tbreak;\n-\t\t\t__get_kernel_nofault(\u0026c1, s1 + j, char, err_out);\n+\t\t\t__get_kernel_nofault_bare(\u0026c1, s1 + j, char, err_out);\n \n \t\t\tif (ignore_case) {\n \t\t\t\tc1 = tolower(c1);\ndiff --git a/mm/maccess.c b/mm/maccess.c\nindex 486559d688583..87486bf98d15a 100644\n--- a/mm/maccess.c\n+++ b/mm/maccess.c\n@@ -19,7 +19,7 @@ bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,\n */\n #define copy_from_kernel_nofault_loop(dst, src, len, type, err_label)\t\\\n \twhile (len \u003e= sizeof(type)) {\t\t\t\t\t\\\n-\t\t__get_kernel_nofault(dst, src, type, err_label);\t\\\n+\t\t__get_kernel_nofault_bare(dst, src, type, err_label);\t\\\n \t\tkmsan_check_memory(src, sizeof(type));\t\t\t\\\n \t\tdst += sizeof(type);\t\t\t\t\t\\\n \t\tsrc += sizeof(type);\t\t\t\t\t\\\n@@ -35,26 +35,29 @@ long copy_from_kernel_nofault(void *dst, const void *src, size_t size)\n \n \tif (!copy_from_kernel_nofault_allowed(src, size))\n \t\treturn -ERANGE;\n+\tif (!size)\n+\t\treturn 0;\n \n-\tpagefault_disable();\n-\tif (!(align \u0026 7))\n-\t\tcopy_from_kernel_nofault_loop(dst, src, size, u64, Efault);\n-\tif (!(align \u0026 3))\n-\t\tcopy_from_kernel_nofault_loop(dst, src, size, u32, Efault);\n-\tif (!(align \u0026 1))\n-\t\tcopy_from_kernel_nofault_loop(dst, src, size, u16, Efault);\n-\tcopy_from_kernel_nofault_loop(dst, src, size, u8, Efault);\n-\tpagefault_enable();\n+\tscoped_guard(pagefault) {\n+\t\tscoped_guard(__kernel_nofault_bare) {\n+\t\t\tif (!(align \u0026 7))\n+\t\t\t\tcopy_from_kernel_nofault_loop(dst, src, size, u64, Efault);\n+\t\t\tif (!(align \u0026 3))\n+\t\t\t\tcopy_from_kernel_nofault_loop(dst, src, size, u32, Efault);\n+\t\t\tif (!(align \u0026 1))\n+\t\t\t\tcopy_from_kernel_nofault_loop(dst, src, size, u16, Efault);\n+\t\t\tcopy_from_kernel_nofault_loop(dst, src, size, u8, Efault);\n+\t\t}\n+\t}\n \treturn 0;\n Efault:\n-\tpagefault_enable();\n \treturn -EFAULT;\n }\n EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);\n \n #define copy_to_kernel_nofault_loop(dst, src, len, type, err_label)\t\\\n \twhile (len \u003e= sizeof(type)) {\t\t\t\t\t\\\n-\t\t__put_kernel_nofault(dst, src, type, err_label);\t\\\n+\t\t__put_kernel_nofault_bare(dst, src, type, err_label);\t\\\n \t\tinstrument_write(dst, sizeof(type));\t\t\t\\\n \t\tdst += sizeof(type);\t\t\t\t\t\\\n \t\tsrc += sizeof(type);\t\t\t\t\t\\\n@@ -65,21 +68,25 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size)\n {\n \tunsigned long align = 0;\n \n+\tif (!size)\n+\t\treturn 0;\n+\n \tif (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))\n \t\talign = (unsigned long)dst | (unsigned long)src;\n \n-\tpagefault_disable();\n-\tif (!(align \u0026 7))\n-\t\tcopy_to_kernel_nofault_loop(dst, src, size, u64, Efault);\n-\tif (!(align \u0026 3))\n-\t\tcopy_to_kernel_nofault_loop(dst, src, size, u32, Efault);\n-\tif (!(align \u0026 1))\n-\t\tcopy_to_kernel_nofault_loop(dst, src, size, u16, Efault);\n-\tcopy_to_kernel_nofault_loop(dst, src, size, u8, Efault);\n-\tpagefault_enable();\n+\tscoped_guard(pagefault) {\n+\t\tscoped_guard(__kernel_nofault_bare) {\n+\t\t\tif (!(align \u0026 7))\n+\t\t\t\tcopy_to_kernel_nofault_loop(dst, src, size, u64, Efault);\n+\t\t\tif (!(align \u0026 3))\n+\t\t\t\tcopy_to_kernel_nofault_loop(dst, src, size, u32, Efault);\n+\t\t\tif (!(align \u0026 1))\n+\t\t\t\tcopy_to_kernel_nofault_loop(dst, src, size, u16, Efault);\n+\t\t\tcopy_to_kernel_nofault_loop(dst, src, size, u8, Efault);\n+\t\t}\n+\t}\n \treturn 0;\n Efault:\n-\tpagefault_enable();\n \treturn -EFAULT;\n }\n \n@@ -92,18 +99,19 @@ long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)\n \tif (!copy_from_kernel_nofault_allowed(unsafe_addr, count))\n \t\treturn -ERANGE;\n \n-\tpagefault_disable();\n-\tdo {\n-\t\t__get_kernel_nofault(dst, src, u8, Efault);\n-\t\tdst++;\n-\t\tsrc++;\n-\t} while (dst[-1] \u0026\u0026 src - unsafe_addr \u003c count);\n-\tpagefault_enable();\n+\tscoped_guard(pagefault) {\n+\t\tscoped_guard(__kernel_nofault_bare) {\n+\t\t\tdo {\n+\t\t\t\t__get_kernel_nofault_bare(dst, src, u8, Efault);\n+\t\t\t\tdst++;\n+\t\t\t\tsrc++;\n+\t\t\t} while (dst[-1] \u0026\u0026 src - unsafe_addr \u003c count);\n+\t\t}\n+\t}\n \n \tdst[-1] = '\\0';\n \treturn src - unsafe_addr;\n Efault:\n-\tpagefault_enable();\n \tdst[0] = '\\0';\n \treturn -EFAULT;\n }\n"
} |
| 2/1 | 2026/08/24 19:49 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"copy_from_kernel_nofault",
"copy_to_kernel_nofault",
"strncpy_from_kernel_nofault",
"bpf_strcmp",
"bpf_strcasecmp",
"bpf_strncasecmp",
"bpf_strstr",
"bpf_strcasestr",
"bpf_strnstr",
"bpf_strncasestr",
"bpf_strnchr",
"bpf_strchrnul",
"bpf_strrchr",
"bpf_strnlen",
"bpf_strspn",
"bpf_strcspn"
],
"Reasoning": "The patch introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to optimize TCO (Tag Check Override) management on ARM64, and updates `maccess.c` and BPF string helpers to use them under `guard(__kernel_nofault_bare)()`. This is a functional change to core memory access routines and BPF helpers, which could introduce bugs if the nofault regions are not handled correctly.",
"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 0516034bbdacab8b2b6644d9b8a40ab63da22ee0
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Aug 24 19:49:25 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 9f5bd9c69c249..1a14eb2a51dce 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -270,28 +270,43 @@ do { \
#define get_user __get_user
/*
- * We must not call into the scheduler between __mte_enable_tco_async() and
- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
- * functions, we must evaluate these outside of the critical section.
+ * Nofault load without TCO management for use inside a
+ * __begin/__end_kernel_nofault_bare() region.
*/
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define __get_kernel_nofault_bare(dst, src, type, err_label) \
do { \
__typeof__(dst) __gkn_dst = (dst); \
__typeof__(src) __gkn_src = (src); \
do { \
__label__ __gkn_label; \
- \
- __mte_enable_tco_async(); \
__raw_get_mem("ldr", *((type *)(__gkn_dst)), \
(__force type *)(__gkn_src), __gkn_label, K); \
- __mte_disable_tco_async(); \
break; \
__gkn_label: \
- __mte_disable_tco_async(); \
goto err_label; \
} while (0); \
} while (0)
+/*
+ * We must not call into the scheduler between __mte_enable_tco_async() and
+ * __mte_disable_tco_async(). As dst and src may contain blocking functions,
+ * evaluate them before overriding TCO.
+ */
+#define __get_kernel_nofault(dst, src, type, err_label) \
+do { \
+ __label__ __gkn_tco_err; \
+ __typeof__(dst) __gkn_tco_dst = (dst); \
+ __typeof__(src) __gkn_tco_src = (src); \
+ __mte_enable_tco_async(); \
+ __get_kernel_nofault_bare(__gkn_tco_dst, __gkn_tco_src, type, \
+ __gkn_tco_err); \
+ __mte_disable_tco_async(); \
+ break; \
+__gkn_tco_err: \
+ __mte_disable_tco_async(); \
+ goto err_label; \
+} while (0)
+
#define __put_mem_asm(store, reg, x, addr, label, type) \
asm goto( \
"1: " store " " reg "0, [%1]\n" \
@@ -366,28 +381,48 @@ do { \
#define put_user __put_user
-/*
- * We must not call into the scheduler between __mte_enable_tco_async() and
- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
- * functions, we must evaluate these outside of the critical section.
- */
-#define __put_kernel_nofault(dst, src, type, err_label) \
+/* Nofault store without TCO management; see __get_kernel_nofault_bare. */
+#define __put_kernel_nofault_bare(dst, src, type, err_label) \
do { \
__typeof__(dst) __pkn_dst = (dst); \
__typeof__(src) __pkn_src = (src); \
\
do { \
__label__ __pkn_err; \
- __mte_enable_tco_async(); \
__raw_put_mem("str", *((type *)(__pkn_src)), \
(__force type *)(__pkn_dst), __pkn_err, K); \
- __mte_disable_tco_async(); \
break; \
__pkn_err: \
- __mte_disable_tco_async(); \
goto err_label; \
} while (0); \
-} while(0)
+} while (0)
+
+/*
+ * We must not call into the scheduler between __mte_enable_tco_async() and
+ * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
+ * functions, we must evaluate these outside of the critical section.
+ */
+#define __put_kernel_nofault(dst, src, type, err_label) \
+do { \
+ __label__ __pkn_tco_err; \
+ __typeof__(dst) __pkn_tco_dst = (dst); \
+ __typeof__(src) __pkn_tco_src = (src); \
+ __mte_enable_tco_async(); \
+ __put_kernel_nofault_bare(__pkn_tco_dst, __pkn_tco_src, type, \
+ __pkn_tco_err); \
+ __mte_disable_tco_async(); \
+ break; \
+__pkn_tco_err: \
+ __mte_disable_tco_async(); \
+ goto err_label; \
+} while (0)
+
+/*
+ * A context switch re-enables tag checking, hence the no-scheduling
+ * requirement for a bare nofault region.
+ */
+#define __begin_kernel_nofault_bare() __mte_enable_tco_async()
+#define __end_kernel_nofault_bare() __mte_disable_tco_async()
extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
#define raw_copy_from_user(to, from, n) \
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index eddbbb65ccc4f..b25e778ddc38a 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -637,6 +637,26 @@ do { \
#endif /* !__get_kernel_nofault */
+/*
+ * Architectures may use the begin/end hooks to establish state shared by a
+ * sequence of bare nofault accesses. Every path out of the region must call
+ * the end hook. The region, including expressions passed to the bare
+ * accessors, must not call into the scheduler.
+ */
+#ifndef __get_kernel_nofault_bare
+#define __get_kernel_nofault_bare __get_kernel_nofault
+#define __put_kernel_nofault_bare __put_kernel_nofault
+#endif
+
+#ifndef __begin_kernel_nofault_bare
+#define __begin_kernel_nofault_bare() do {} while (0)
+#define __end_kernel_nofault_bare() do {} while (0)
+#endif
+
+DEFINE_LOCK_GUARD_0(__kernel_nofault_bare,
+ __begin_kernel_nofault_bare(),
+ __end_kernel_nofault_bare())
+
/**
* get_kernel_nofault(): safely attempt to read from a location
* @val: read into this variable
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b3cc5c8fc8756..f8c557161584e 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3726,7 +3726,8 @@ __bpf_kfunc void __bpf_trap(void)
*
* Since strings are not necessarily %NUL-terminated, we cannot directly call
* in-kernel implementations. Instead, we open-code the implementations using
- * __get_kernel_nofault instead of plain dereference to make them safe.
+ * __get_kernel_nofault_bare under guard(__kernel_nofault_bare)() to make them
+ * safe.
*/
static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)
@@ -3738,11 +3739,14 @@ static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, s
!copy_from_kernel_nofault_allowed(s2, 1)) {
return -ERANGE;
}
+ if (!len)
+ return 0;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < len && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&c1, s1, char, err_out);
- __get_kernel_nofault(&c2, s2, char, err_out);
+ __get_kernel_nofault_bare(&c1, s1, char, err_out);
+ __get_kernel_nofault_bare(&c2, s2, char, err_out);
if (ignore_case) {
c1 = tolower(c1);
c2 = tolower(c2);
@@ -3837,10 +3841,13 @@ __bpf_kfunc int bpf_strnchr(const char *s__ign, size_t count, char c)
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
+ if (!count)
+ return -ENOENT;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == c)
return i;
if (sc == '\0')
@@ -3893,8 +3900,9 @@ __bpf_kfunc int bpf_strchrnul(const char *s__ign, char c)
return -ERANGE;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == '\0' || sc == c)
return i;
s__ign++;
@@ -3925,8 +3933,9 @@ __bpf_kfunc int bpf_strrchr(const char *s__ign, int c)
return -ERANGE;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == c)
last = i;
if (sc == '\0')
@@ -3956,10 +3965,13 @@ __bpf_kfunc int bpf_strnlen(const char *s__ign, size_t count)
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
+ if (!count)
+ return 0;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&c, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&c, s__ign, char, err_out);
if (c == '\0')
return i;
s__ign++;
@@ -4008,12 +4020,13 @@ __bpf_kfunc int bpf_strspn(const char *s__ign, const char *accept__ign)
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&cs, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&ca, accept__ign + j, char, err_out);
+ __get_kernel_nofault_bare(&ca, accept__ign + j, char, err_out);
if (cs == ca || ca == '\0')
break;
}
@@ -4052,12 +4065,13 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&cs, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&cr, reject__ign + j, char, err_out);
+ __get_kernel_nofault_bare(&cr, reject__ign + j, char, err_out);
if (cs == cr || cr == '\0')
break;
}
@@ -4084,9 +4098,10 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
for (j = 0; i + j <= len && j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&c2, s2 + j, char, err_out);
+ __get_kernel_nofault_bare(&c2, s2 + j, char, err_out);
if (c2 == '\0')
return i;
/*
@@ -4096,7 +4111,7 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,
*/
if (i + j == len)
break;
- __get_kernel_nofault(&c1, s1 + j, char, err_out);
+ __get_kernel_nofault_bare(&c1, s1 + j, char, err_out);
if (ignore_case) {
c1 = tolower(c1);
diff --git a/mm/maccess.c b/mm/maccess.c
index 486559d688583..87486bf98d15a 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -19,7 +19,7 @@ bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
*/
#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \
while (len >= sizeof(type)) { \
- __get_kernel_nofault(dst, src, type, err_label); \
+ __get_kernel_nofault_bare(dst, src, type, err_label); \
kmsan_check_memory(src, sizeof(type)); \
dst += sizeof(type); \
src += sizeof(type); \
@@ -35,26 +35,29 @@ long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
if (!copy_from_kernel_nofault_allowed(src, size))
return -ERANGE;
+ if (!size)
+ return 0;
- pagefault_disable();
- if (!(align & 7))
- copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
- if (!(align & 3))
- copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
- if (!(align & 1))
- copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
- copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ if (!(align & 7))
+ copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
+ if (!(align & 3))
+ copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
+ if (!(align & 1))
+ copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
+ copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
+ }
+ }
return 0;
Efault:
- pagefault_enable();
return -EFAULT;
}
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \
while (len >= sizeof(type)) { \
- __put_kernel_nofault(dst, src, type, err_label); \
+ __put_kernel_nofault_bare(dst, src, type, err_label); \
instrument_write(dst, sizeof(type)); \
dst += sizeof(type); \
src += sizeof(type); \
@@ -65,21 +68,25 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
{
unsigned long align = 0;
+ if (!size)
+ return 0;
+
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
align = (unsigned long)dst | (unsigned long)src;
- pagefault_disable();
- if (!(align & 7))
- copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
- if (!(align & 3))
- copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
- if (!(align & 1))
- copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
- copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ if (!(align & 7))
+ copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
+ if (!(align & 3))
+ copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
+ if (!(align & 1))
+ copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
+ copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
+ }
+ }
return 0;
Efault:
- pagefault_enable();
return -EFAULT;
}
@@ -92,18 +99,19 @@ long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
return -ERANGE;
- pagefault_disable();
- do {
- __get_kernel_nofault(dst, src, u8, Efault);
- dst++;
- src++;
- } while (dst[-1] && src - unsafe_addr < count);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ do {
+ __get_kernel_nofault_bare(dst, src, u8, Efault);
+ dst++;
+ src++;
+ } while (dst[-1] && src - unsafe_addr < count);
+ }
+ }
dst[-1] = '\0';
return src - unsafe_addr;
Efault:
- pagefault_enable();
dst[0] = '\0';
return -EFAULT;
}
|
| 3/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7546 output: 36 thoughts: 489 Thoughts: **Fuzzing Potential of the New Kernel Patch** Okay, so I'm looking at this patch that introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` and uses them within `guard(__kernel_nofault_bare())` in `kernel/bpf/helpers.c` and `mm/maccess.c`. This is a pretty significant functional change because it alters how `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, `strncpy_from_kernel_nofault`, and several BPF string helpers are implemented. I see that the primary benefit seems to be optimizing the TCO management on ARM64 by reducing overhead. My immediate thought is, "Is this worth fuzzing?" Yes, absolutely. It's modifying core kernel logic, specifically BPF helpers and memory access mechanisms. Given my experience, that's a prime target for potential vulnerabilities. Now, let me list the functions modified: * `copy_from_kernel_nofault` * `copy_to_kernel_nofault` * `strncpy_from_kernel_nofault` * `__bpf_strncasecmp` * `bpf_strnchr` * `bpf_strchrnul` * `bpf_strrchr` * `bpf_strnlen` * `bpf_strspn` * `bpf_strcspn` * `__bpf_strnstr` These are all definitely reachable and testable. I can see clear paths to fuzzing these to expose any new issues introduced by this TCO management optimization. Okay, let's nail down the FocusSymbols for the fuzzer. I need to list them all: * `copy_from_kernel_nofault` * `copy_to_kernel_nofault` * `strncpy_from_kernel_nofault` * `bpf_strnchr` * `bpf_strchrnul` * `bpf_strrchr` * `bpf_strnlen` * `bpf_strspn` * `bpf_strcspn` Hmm, `__bpf_strncasecmp` and `__bpf_strnstr` are static, so I need to verify whether they're inlined or have distinct symbols. I should check `kernel/bpf/helpers.c` to see which functions call `__bpf_strncasecmp` and `__bpf_strnstr`. That will make sure I'm capturing all the relevant code paths. |
| 4/2 | 2026/08/24 19:49 | tool | grepper |
0mArgs:{
"Expression": "__bpf_strncasecmp",
"PathPrefix": "kernel/bpf/helpers.c"
}Results: {
"Output": "kernel/bpf/helpers.c=3720=__bpf_kfunc void __bpf_trap(void)\n--\nkernel/bpf/helpers.c-3732-\nkernel/bpf/helpers.c:3733:static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)\nkernel/bpf/helpers.c-3734-{\n--\nkernel/bpf/helpers.c=3779=__bpf_kfunc int bpf_strcmp(const char *s1__ign, const char *s2__ign)\nkernel/bpf/helpers.c-3780-{\nkernel/bpf/helpers.c:3781:\treturn __bpf_strncasecmp(s1__ign, s2__ign, false, XATTR_SIZE_MAX);\nkernel/bpf/helpers.c-3782-}\n--\nkernel/bpf/helpers.c=3797=__bpf_kfunc int bpf_strcasecmp(const char *s1__ign, const char *s2__ign)\nkernel/bpf/helpers.c-3798-{\nkernel/bpf/helpers.c:3799:\treturn __bpf_strncasecmp(s1__ign, s2__ign, true, XATTR_SIZE_MAX);\nkernel/bpf/helpers.c-3800-}\n--\nkernel/bpf/helpers.c=3816=__bpf_kfunc int bpf_strncasecmp(const char *s1__ign, const char *s2__ign, size_t len)\nkernel/bpf/helpers.c-3817-{\nkernel/bpf/helpers.c:3818:\treturn __bpf_strncasecmp(s1__ign, s2__ign, true, len);\nkernel/bpf/helpers.c-3819-}\n"
} |
| 5/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9156 output: 35 thoughts: 50 Thoughts: Okay, here's my breakdown of what's going on here. **Deconstructing String Comparisons in BPF** Alright, so I'm looking at how string comparisons are handled in BPF. I see that the functions `bpf_strcmp`, `bpf_strcasecmp`, and `bpf_strncasecmp` all seem to funnel down to a common implementation: `__bpf_strncasecmp`. This suggests a layer of abstraction, which is pretty standard and makes sense for code reuse and potentially for optimization later. The core string comparison, especially case-insensitive versions, is clearly centralized. Now, I need to understand how the lower-level comparison is working. Let's move on and examine `__bpf_strnstr` next. It seems likely that the `__bpf_strncasecmp` would likely utilize something like this function. |
| 6/2 | 2026/08/24 19:49 | tool | grepper |
0mArgs:{
"Expression": "__bpf_strnstr",
"PathPrefix": "kernel/bpf/helpers.c"
}Results: {
"Output": "kernel/bpf/helpers.c=4057=__bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)\n--\nkernel/bpf/helpers.c-4088-\nkernel/bpf/helpers.c:4089:static int __bpf_strnstr(const char *s1, const char *s2, size_t len,\nkernel/bpf/helpers.c-4090-\t\t\t bool ignore_case)\n--\nkernel/bpf/helpers.c=4150=__bpf_kfunc int bpf_strstr(const char *s1__ign, const char *s2__ign)\nkernel/bpf/helpers.c-4151-{\nkernel/bpf/helpers.c:4152:\treturn __bpf_strnstr(s1__ign, s2__ign, XATTR_SIZE_MAX, false);\nkernel/bpf/helpers.c-4153-}\n--\nkernel/bpf/helpers.c=4169=__bpf_kfunc int bpf_strcasestr(const char *s1__ign, const char *s2__ign)\nkernel/bpf/helpers.c-4170-{\nkernel/bpf/helpers.c:4171:\treturn __bpf_strnstr(s1__ign, s2__ign, XATTR_SIZE_MAX, true);\nkernel/bpf/helpers.c-4172-}\n--\nkernel/bpf/helpers.c=4188=__bpf_kfunc int bpf_strnstr(const char *s1__ign, const char *s2__ign,\n--\nkernel/bpf/helpers.c-4190-{\nkernel/bpf/helpers.c:4191:\treturn __bpf_strnstr(s1__ign, s2__ign, len, false);\nkernel/bpf/helpers.c-4192-}\n--\nkernel/bpf/helpers.c=4209=__bpf_kfunc int bpf_strncasestr(const char *s1__ign, const char *s2__ign,\n--\nkernel/bpf/helpers.c-4211-{\nkernel/bpf/helpers.c:4212:\treturn __bpf_strnstr(s1__ign, s2__ign, len, true);\nkernel/bpf/helpers.c-4213-}\n"
} |
| 7/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10041 output: 32 thoughts: 233 Thoughts: **My BPF String Helper and `maccess` Function Checklist** Okay, let's see, I need to jog my memory on the BPF string functions. Right, those helpers are: `bpf_strcmp`, `bpf_strcasecmp`, `bpf_strncasecmp`, `bpf_strstr`, `bpf_strcasestr`, `bpf_strnstr`, `bpf_strncasestr`, `bpf_strnchr`, `bpf_strchrnul`, `bpf_strrchr`, `bpf_strnlen`, `bpf_strspn`, and `bpf_strcspn`. And then, on the memory access side, we have `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, and `strncpy_from_kernel_nofault`. Now, before I start writing anything using these, I need to think about any kernel configurations that need to be in place. I know BPF helpers generally need `CONFIG_BPF_SYSCALL` enabled, which is pretty standard these days, but it's always good to double-check. I think the `copy_from_kernel_nofault` functions are always available, but I'll need to watch for those when tracing to make sure they're behaving correctly. Alright, the most reliable thing to do is dive into the source code itself. I'll need to check `kernel/bpf/helpers.c` to see if there are any conditional compilation `#ifdef` directives around those string helper functions. Better to be sure than sorry, especially with something like this. |
| 8/2 | 2026/08/24 19:49 | tool | grepper |
0mArgs:{
"Expression": "bpf_strcmp",
"PathPrefix": "kernel/bpf/helpers.c"
}Results: {
"Output": "kernel/bpf/helpers.c=3733=static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)\n--\nkernel/bpf/helpers.c-3766-/**\nkernel/bpf/helpers.c:3767: * bpf_strcmp - Compare two strings\nkernel/bpf/helpers.c-3768- * @s1__ign: One string\n--\nkernel/bpf/helpers.c-3778- */\nkernel/bpf/helpers.c:3779:__bpf_kfunc int bpf_strcmp(const char *s1__ign, const char *s2__ign)\nkernel/bpf/helpers.c-3780-{\n--\nkernel/bpf/helpers.c=4964=BTF_ID_FLAGS(func, __bpf_trap)\nkernel/bpf/helpers.c:4965:BTF_ID_FLAGS(func, bpf_strcmp);\nkernel/bpf/helpers.c-4966-BTF_ID_FLAGS(func, bpf_strcasecmp);\n"
} |
| 9/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 10952 output: 33 thoughts: 51 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Verifying BPF Helper Compilation** Right, these helper functions in `kernel/bpf/helpers.c` don't seem to be guarded by any conditional compilation directives like `#ifdef`. That's a bit unusual, normally I'd expect to see some kind of check around this, but I'm going to quickly check the `kernel/bpf/Makefile` to confirm my suspicion. The fact that the entire file exists within a BPF context makes me assume it's compiled if `CONFIG_BPF_SYSCALL` is enabled. Better safe than sorry, I should verify, but I'm thinking I'll quickly see that the Makefile dictates this behavior. Let's see if the build system handles their inclusion based on that config option. |
| 10/2 | 2026/08/24 19:49 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/Makefile",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\tobj-y := core.o\n 3:\tifneq ($(CONFIG_BPF_JIT_ALWAYS_ON),y)\n 4:\t# ___bpf_prog_run() needs GCSE disabled on x86; see 3193c0836f203 for details\n 5:\tcflags-nogcse-$(CONFIG_X86)$(CONFIG_CC_IS_GCC) := -fno-gcse\n 6:\tendif\n 7:\tCFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)\n 8:\t\n 9:\tobj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o diagnostics.o\n 10:\tobj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o\n 11:\tobj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o\n 12:\tobj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o\n 13:\tobj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o\n 14:\tobj-$(CONFIG_BPF_SYSCALL) += fixups.o cfg.o states.o backtrack.o check_btf.o\n 15:\tobj-${CONFIG_BPF_LSM}\t += bpf_inode_storage.o\n 16:\tobj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o\n 17:\tobj-$(CONFIG_BPF_JIT) += trampoline.o\n 18:\tobj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o\n 19:\tifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy)\n 20:\tobj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o\n 21:\tendif\n 22:\tobj-$(CONFIG_BPF_JIT) += dispatcher.o\n 23:\tifeq ($(CONFIG_NET),y)\n 24:\tobj-$(CONFIG_BPF_SYSCALL) += devmap.o\n 25:\tobj-$(CONFIG_BPF_SYSCALL) += cpumap.o\n 26:\tobj-$(CONFIG_BPF_SYSCALL) += offload.o\n 27:\tobj-$(CONFIG_BPF_SYSCALL) += net_namespace.o\n 28:\tobj-$(CONFIG_BPF_SYSCALL) += tcx.o\n 29:\tendif\n 30:\tifeq ($(CONFIG_PERF_EVENTS),y)\n 31:\tobj-$(CONFIG_BPF_SYSCALL) += stackmap.o\n 32:\tendif\n 33:\tifeq ($(CONFIG_CGROUPS),y)\n 34:\tobj-$(CONFIG_BPF_SYSCALL) += cgroup_iter.o bpf_cgrp_storage.o\n 35:\tendif\n 36:\tobj-$(CONFIG_CGROUP_BPF) += cgroup.o\n 37:\tifeq ($(CONFIG_INET),y)\n 38:\tobj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o\n 39:\tendif\n 40:\tifeq ($(CONFIG_SYSFS),y)\n 41:\tobj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o\n 42:\tendif\n 43:\tifeq ($(CONFIG_BPF_JIT),y)\n 44:\tobj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o\n 45:\tobj-$(CONFIG_BPF_SYSCALL) += cpumask.o\n 46:\t# bpf_lsm_proto.o must precede bpf_lsm.o. The current pahole logic\n 47:\t# deduplicates function prototypes within\n 48:\t# btf_encoder__add_saved_func() by keeping the first instance seen. We\n 49:\t# need the function prototype(s) in bpf_lsm_proto.o to take precedence\n 50:\t# over those within bpf_lsm.o. Having bpf_lsm_proto.o precede\n 51:\t# bpf_lsm.o ensures its DWARF CU is processed early, forcing the\n 52:\t# generated BTF to contain the overrides.\n 53:\t#\n 54:\t# Notably, this is a temporary workaround whilst the deduplication\n 55:\t# semantics within pahole are revisited accordingly.\n 56:\tobj-${CONFIG_BPF_LSM} += bpf_lsm_proto.o bpf_lsm.o\n 57:\tendif\n 58:\tifneq ($(CONFIG_CRYPTO),)\n 59:\tobj-$(CONFIG_BPF_SYSCALL) += crypto.o\n 60:\tendif\n 61:\tobj-$(CONFIG_BPF_PRELOAD) += preload/\n 62:\t\n 63:\tobj-$(CONFIG_BPF_SYSCALL) += relo_core.o\n 64:\tobj-$(CONFIG_BPF_SYSCALL) += btf_iter.o\n 65:\tobj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o\n 66:\tobj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o\n 67:\tifeq ($(CONFIG_DMA_SHARED_BUFFER),y)\n 68:\tobj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o\n 69:\tendif\n 70:\t\n 71:\tCFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)\n 72:\tCFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)\n 73:\tCFLAGS_REMOVE_queue_stack_maps.o = $(CC_FLAGS_FTRACE)\n 74:\tCFLAGS_REMOVE_lpm_trie.o = $(CC_FLAGS_FTRACE)\n 75:\tCFLAGS_REMOVE_ringbuf.o = $(CC_FLAGS_FTRACE)\n 76:\tCFLAGS_REMOVE_rqspinlock.o = $(CC_FLAGS_FTRACE)\n"
} |
| 11/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12917 output: 30 thoughts: 36 Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as someone familiar with kernel internals: **Investigating eBPF Helper Availability and Memory Access in the Kernel** Alright, I remember that `helpers.o` is indeed conditional on `CONFIG_BPF_SYSCALL`. Good, that confirms my initial suspicion regarding the availability of those eBPF helper functions. Now, the next logical step to track down this issue is to investigate the memory access issue. I need to refresh my memory on where the kernel defines `maccess.o`. My gut tells me it will be in the `mm/` directory since that's where the memory management code typically resides. I'll take a look at the `mm/Makefile` to confirm and get the relevant details. |
| 12/2 | 2026/08/24 19:49 | tool | read-file |
0mArgs:{
"File": "mm/Makefile",
"FirstLine": 1,
"LineCount": 100
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t#\n 3:\t# Makefile for the linux memory manager.\n 4:\t#\n 5:\t\n 6:\tKASAN_SANITIZE_slab_common.o := n\n 7:\tKASAN_SANITIZE_slub.o := n\n 8:\tKASAN_SANITIZE_kmemleak.o := n\n 9:\tKCSAN_SANITIZE_kmemleak.o := n\n 10:\t\n 11:\t# These produce frequent data race reports: most of them are due to races on\n 12:\t# the same word but accesses to different bits of that word. Re-enable KCSAN\n 13:\t# for these when we have more consensus on what to do about them.\n 14:\tKCSAN_SANITIZE_slab_common.o := n\n 15:\tKCSAN_SANITIZE_slub.o := n\n 16:\tKCSAN_SANITIZE_page_alloc.o := n\n 17:\t# But enable explicit instrumentation for memory barriers.\n 18:\tKCSAN_INSTRUMENT_BARRIERS := y\n 19:\t\n 20:\t# These files are disabled because they produce non-interesting and/or\n 21:\t# flaky coverage that is not a function of syscall inputs. E.g. slab is out of\n 22:\t# free pages, or a task is migrated between nodes.\n 23:\tKCOV_INSTRUMENT_slab_common.o := n\n 24:\tKCOV_INSTRUMENT_slub.o := n\n 25:\tKCOV_INSTRUMENT_page_alloc.o := n\n 26:\tKCOV_INSTRUMENT_debug-pagealloc.o := n\n 27:\tKCOV_INSTRUMENT_kmemleak.o := n\n 28:\tKCOV_INSTRUMENT_memcontrol.o := n\n 29:\tKCOV_INSTRUMENT_memcontrol-v1.o := n\n 30:\tKCOV_INSTRUMENT_mmzone.o := n\n 31:\tKCOV_INSTRUMENT_vmstat.o := n\n 32:\tKCOV_INSTRUMENT_failslab.o := n\n 33:\t\n 34:\tCFLAGS_init-mm.o += -Wno-override-init\n 35:\t\n 36:\tmmu-y\t\t\t:= nommu.o\n 37:\tmmu-$(CONFIG_MMU)\t:= highmem.o memory.o mincore.o \\\n 38:\t\t\t\t mlock.o mmap.o mmu_gather.o mprotect.o mremap.o \\\n 39:\t\t\t\t msync.o page_vma_mapped.o pagewalk.o \\\n 40:\t\t\t\t pgtable-generic.o rmap.o vmalloc.o vma.o vma_exec.o\n 41:\t\n 42:\t\n 43:\tifdef CONFIG_CROSS_MEMORY_ATTACH\n 44:\tmmu-$(CONFIG_MMU)\t+= process_vm_access.o\n 45:\tendif\n 46:\t\n 47:\tifdef CONFIG_64BIT\n 48:\tmmu-$(CONFIG_MMU)\t+= mseal.o\n 49:\tendif\n 50:\t\n 51:\tobj-y\t\t\t:= filemap.o mempool.o oom_kill.o fadvise.o \\\n 52:\t\t\t\t maccess.o page-writeback.o folio-compat.o \\\n 53:\t\t\t\t readahead.o swap.o truncate.o vmscan.o shrinker.o \\\n 54:\t\t\t\t shmem.o util.o mmzone.o vmstat.o backing-dev.o \\\n 55:\t\t\t\t mm_init.o percpu.o slab_common.o \\\n 56:\t\t\t\t compaction.o show_mem.o \\\n 57:\t\t\t\t interval_tree.o list_lru.o workingset.o \\\n 58:\t\t\t\t debug.o gup.o mmap_lock.o vma_init.o $(mmu-y)\n 59:\t\n 60:\t# Give 'page_alloc' its own module-parameter namespace\n 61:\tpage-alloc-y := page_alloc.o\n 62:\tpage-alloc-$(CONFIG_SHUFFLE_PAGE_ALLOCATOR) += shuffle.o\n 63:\t\n 64:\t# Give 'memory_hotplug' its own module-parameter namespace\n 65:\tmemory-hotplug-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o\n 66:\t\n 67:\tobj-y += page-alloc.o\n 68:\tobj-y += page_frag_cache.o\n 69:\tobj-y += init-mm.o\n 70:\tobj-y += memblock.o\n 71:\tobj-y += $(memory-hotplug-y)\n 72:\tobj-y += slub.o\n 73:\t\n 74:\tifdef CONFIG_MMU\n 75:\t\tobj-$(CONFIG_ADVISE_SYSCALLS)\t+= madvise.o\n 76:\tendif\n 77:\t\n 78:\tobj-$(CONFIG_SWAP)\t+= page_io.o swap_state.o swapfile.o\n 79:\tobj-$(CONFIG_ZSWAP)\t+= zswap.o\n 80:\tobj-$(CONFIG_HAS_DMA)\t+= dmapool.o\n 81:\tobj-$(CONFIG_HUGETLBFS)\t+= hugetlb.o hugetlb_sysfs.o hugetlb_sysctl.o\n 82:\tifdef CONFIG_CMA\n 83:\tobj-$(CONFIG_HUGETLBFS)\t+= hugetlb_cma.o\n 84:\tendif\n 85:\tobj-$(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP)\t+= hugetlb_vmemmap.o\n 86:\tobj-$(CONFIG_NUMA) \t+= mempolicy.o\n 87:\tobj-$(CONFIG_SPARSEMEM)\t+= sparse.o\n 88:\tobj-$(CONFIG_SPARSEMEM_VMEMMAP) += sparse-vmemmap.o\n 89:\tobj-$(CONFIG_MMU_NOTIFIER) += mmu_notifier.o\n 90:\tobj-$(CONFIG_KSM) += ksm.o\n 91:\tobj-$(CONFIG_PAGE_POISONING) += page_poison.o\n 92:\tobj-$(CONFIG_KASAN)\t+= kasan/\n 93:\tobj-$(CONFIG_KFENCE) += kfence/\n 94:\tobj-$(CONFIG_KMSAN)\t+= kmsan/\n 95:\tobj-$(CONFIG_FAILSLAB) += failslab.o\n 96:\tobj-$(CONFIG_FAIL_PAGE_ALLOC) += fail_page_alloc.o\n 97:\tobj-$(CONFIG_MEMTEST)\t\t+= memtest.o\n 98:\tobj-$(CONFIG_MIGRATION) += migrate.o\n 99:\tobj-$(CONFIG_NUMA) += memory-tiers.o\n 100:\tobj-$(CONFIG_DEVICE_MIGRATION) += migrate_device.o\n"
} |
| 13/2 | 2026/08/24 19:49 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14974 output: 284 thoughts: 605 Thoughts: **My Analysis of the `maccess.o` Changes** Okay, so `maccess.o` is definitely part of `obj-y`, meaning it's always included. My focus is on the functions within it, particularly these string functions: `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, `strncpy_from_kernel_nofault`, and all those `bpf_` string comparison and search functions like `bpf_strcmp`, `bpf_strstr`, and their variations. Wait a minute... I see that `bpf_strcmp`, `bpf_strcasecmp`, `bpf_strncasecmp` are actually just wrappers around `__bpf_strncasecmp`. And the same is true for `bpf_strstr` and friends, which wrap `__bpf_strnstr`. Since the real logic lives in those `__bpf_` functions and they are static, there's a good chance they are inlined. It's important to keep track of this. But let me double-check something. Are those `bpf_` functions actually exported, or are they just BPF kfuncs? Yes, they're `__bpf_kfunc`. So, they're definitely accessible via BPF programs. The core of this change seems to be an optimization around TCO management on ARM64. It’s supposed to perform the TCO check once per loop, instead of on every byte/word access. That's a functional change, and those always make me a little nervous. There could be potential bugs introduced if, for example, the loop gets blocked/scheduled (which shouldn't happen, but you never know), or if the guard isn't properly placed. Definitely worth fuzzing to make sure this is safe. Now, let's consider the configuration. `BPF_SYSCALL` seems to be the key configuration. I need to make sure that's the only one, so I will check. `maccess.c` is always compiled. `helpers.c` is compiled with `CONFIG_BPF_SYSCALL`. Ah, here we go... The patch also touches `arch/arm64/include/asm/uaccess.h`. Okay, so this is ARM64-specific, but there are fallbacks in the generic `include/linux/uaccess.h`. Which means this should be testable on any architecture, although the specific ARM64 optimization will only be directly tested on ARM64. Syzkaller should pick this up on ARM64 instances, so that's good. Alright, time to prepare the set-results call. |
| 14/2 | 2026/08/24 19:50 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"copy_from_kernel_nofault",
"copy_to_kernel_nofault",
"strncpy_from_kernel_nofault",
"bpf_strcmp",
"bpf_strcasecmp",
"bpf_strncasecmp",
"bpf_strstr",
"bpf_strcasestr",
"bpf_strnstr",
"bpf_strncasestr",
"bpf_strnchr",
"bpf_strchrnul",
"bpf_strrchr",
"bpf_strnlen",
"bpf_strspn",
"bpf_strcspn"
],
"Reasoning": "The patch introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to optimize TCO (Tag Check Override) management on ARM64, and updates `maccess.c` and BPF string helpers to use them under `guard(__kernel_nofault_bare)()`. This is a functional change to core memory access routines and BPF helpers, which could introduce bugs if the nofault regions are not handled correctly.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_SYSCALL"
],
"FocusSymbols": [
"copy_from_kernel_nofault",
"copy_to_kernel_nofault",
"strncpy_from_kernel_nofault",
"bpf_strcmp",
"bpf_strcasecmp",
"bpf_strncasecmp",
"bpf_strstr",
"bpf_strcasestr",
"bpf_strnstr",
"bpf_strncasestr",
"bpf_strnchr",
"bpf_strchrnul",
"bpf_strrchr",
"bpf_strnlen",
"bpf_strspn",
"bpf_strcspn"
],
"Reasoning": "The patch introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to optimize TCO (Tag Check Override) management on ARM64, and updates `maccess.c` and BPF string helpers to use them under `guard(__kernel_nofault_bare)()`. This is a functional change to core memory access routines and BPF helpers, which could introduce bugs if the nofault regions are not handled correctly.",
"WorthFuzzing": true
} |
| 15/1 | 2026/08/24 19:50 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 16/2 | 2026/08/24 19:50 | agent | kmsan-evaluator |
0mModel:best-expensive Args: null Results: {
"KMSANReasoning": "The patch refactors `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, and several BPF string helper functions to optimize Tag Check Override (TCO) management on ARM64. It introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to be used within a guarded region (`guard(__kernel_nofault_bare)`), avoiding the overhead of toggling TCO on every memory access inside loops.\n\nThese changes do not introduce new data structures sent to user space, nor do they add complex allocations or conditional logic that might depend on uninitialized memory. The existing `kmsan_check_memory` calls are preserved. Any potential bugs introduced by this refactoring (such as incorrect bounds checking, missing early returns, or improper page fault handling) would result in out-of-bounds accesses, invalid memory dereferences, or crashes. These types of memory corruption and access violations are already effectively detected by KASAN and standard kernel testing. Therefore, a dedicated KMSAN fuzzing session is not necessary.",
"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 0516034bbdacab8b2b6644d9b8a40ab63da22ee0
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Aug 24 19:49:25 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
index 9f5bd9c69c249..1a14eb2a51dce 100644
--- a/arch/arm64/include/asm/uaccess.h
+++ b/arch/arm64/include/asm/uaccess.h
@@ -270,28 +270,43 @@ do { \
#define get_user __get_user
/*
- * We must not call into the scheduler between __mte_enable_tco_async() and
- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
- * functions, we must evaluate these outside of the critical section.
+ * Nofault load without TCO management for use inside a
+ * __begin/__end_kernel_nofault_bare() region.
*/
-#define __get_kernel_nofault(dst, src, type, err_label) \
+#define __get_kernel_nofault_bare(dst, src, type, err_label) \
do { \
__typeof__(dst) __gkn_dst = (dst); \
__typeof__(src) __gkn_src = (src); \
do { \
__label__ __gkn_label; \
- \
- __mte_enable_tco_async(); \
__raw_get_mem("ldr", *((type *)(__gkn_dst)), \
(__force type *)(__gkn_src), __gkn_label, K); \
- __mte_disable_tco_async(); \
break; \
__gkn_label: \
- __mte_disable_tco_async(); \
goto err_label; \
} while (0); \
} while (0)
+/*
+ * We must not call into the scheduler between __mte_enable_tco_async() and
+ * __mte_disable_tco_async(). As dst and src may contain blocking functions,
+ * evaluate them before overriding TCO.
+ */
+#define __get_kernel_nofault(dst, src, type, err_label) \
+do { \
+ __label__ __gkn_tco_err; \
+ __typeof__(dst) __gkn_tco_dst = (dst); \
+ __typeof__(src) __gkn_tco_src = (src); \
+ __mte_enable_tco_async(); \
+ __get_kernel_nofault_bare(__gkn_tco_dst, __gkn_tco_src, type, \
+ __gkn_tco_err); \
+ __mte_disable_tco_async(); \
+ break; \
+__gkn_tco_err: \
+ __mte_disable_tco_async(); \
+ goto err_label; \
+} while (0)
+
#define __put_mem_asm(store, reg, x, addr, label, type) \
asm goto( \
"1: " store " " reg "0, [%1]\n" \
@@ -366,28 +381,48 @@ do { \
#define put_user __put_user
-/*
- * We must not call into the scheduler between __mte_enable_tco_async() and
- * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
- * functions, we must evaluate these outside of the critical section.
- */
-#define __put_kernel_nofault(dst, src, type, err_label) \
+/* Nofault store without TCO management; see __get_kernel_nofault_bare. */
+#define __put_kernel_nofault_bare(dst, src, type, err_label) \
do { \
__typeof__(dst) __pkn_dst = (dst); \
__typeof__(src) __pkn_src = (src); \
\
do { \
__label__ __pkn_err; \
- __mte_enable_tco_async(); \
__raw_put_mem("str", *((type *)(__pkn_src)), \
(__force type *)(__pkn_dst), __pkn_err, K); \
- __mte_disable_tco_async(); \
break; \
__pkn_err: \
- __mte_disable_tco_async(); \
goto err_label; \
} while (0); \
-} while(0)
+} while (0)
+
+/*
+ * We must not call into the scheduler between __mte_enable_tco_async() and
+ * __mte_disable_tco_async(). As `dst` and `src` may contain blocking
+ * functions, we must evaluate these outside of the critical section.
+ */
+#define __put_kernel_nofault(dst, src, type, err_label) \
+do { \
+ __label__ __pkn_tco_err; \
+ __typeof__(dst) __pkn_tco_dst = (dst); \
+ __typeof__(src) __pkn_tco_src = (src); \
+ __mte_enable_tco_async(); \
+ __put_kernel_nofault_bare(__pkn_tco_dst, __pkn_tco_src, type, \
+ __pkn_tco_err); \
+ __mte_disable_tco_async(); \
+ break; \
+__pkn_tco_err: \
+ __mte_disable_tco_async(); \
+ goto err_label; \
+} while (0)
+
+/*
+ * A context switch re-enables tag checking, hence the no-scheduling
+ * requirement for a bare nofault region.
+ */
+#define __begin_kernel_nofault_bare() __mte_enable_tco_async()
+#define __end_kernel_nofault_bare() __mte_disable_tco_async()
extern unsigned long __must_check __arch_copy_from_user(void *to, const void __user *from, unsigned long n);
#define raw_copy_from_user(to, from, n) \
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index eddbbb65ccc4f..b25e778ddc38a 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -637,6 +637,26 @@ do { \
#endif /* !__get_kernel_nofault */
+/*
+ * Architectures may use the begin/end hooks to establish state shared by a
+ * sequence of bare nofault accesses. Every path out of the region must call
+ * the end hook. The region, including expressions passed to the bare
+ * accessors, must not call into the scheduler.
+ */
+#ifndef __get_kernel_nofault_bare
+#define __get_kernel_nofault_bare __get_kernel_nofault
+#define __put_kernel_nofault_bare __put_kernel_nofault
+#endif
+
+#ifndef __begin_kernel_nofault_bare
+#define __begin_kernel_nofault_bare() do {} while (0)
+#define __end_kernel_nofault_bare() do {} while (0)
+#endif
+
+DEFINE_LOCK_GUARD_0(__kernel_nofault_bare,
+ __begin_kernel_nofault_bare(),
+ __end_kernel_nofault_bare())
+
/**
* get_kernel_nofault(): safely attempt to read from a location
* @val: read into this variable
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b3cc5c8fc8756..f8c557161584e 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3726,7 +3726,8 @@ __bpf_kfunc void __bpf_trap(void)
*
* Since strings are not necessarily %NUL-terminated, we cannot directly call
* in-kernel implementations. Instead, we open-code the implementations using
- * __get_kernel_nofault instead of plain dereference to make them safe.
+ * __get_kernel_nofault_bare under guard(__kernel_nofault_bare)() to make them
+ * safe.
*/
static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, size_t len)
@@ -3738,11 +3739,14 @@ static int __bpf_strncasecmp(const char *s1, const char *s2, bool ignore_case, s
!copy_from_kernel_nofault_allowed(s2, 1)) {
return -ERANGE;
}
+ if (!len)
+ return 0;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < len && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&c1, s1, char, err_out);
- __get_kernel_nofault(&c2, s2, char, err_out);
+ __get_kernel_nofault_bare(&c1, s1, char, err_out);
+ __get_kernel_nofault_bare(&c2, s2, char, err_out);
if (ignore_case) {
c1 = tolower(c1);
c2 = tolower(c2);
@@ -3837,10 +3841,13 @@ __bpf_kfunc int bpf_strnchr(const char *s__ign, size_t count, char c)
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
+ if (!count)
+ return -ENOENT;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == c)
return i;
if (sc == '\0')
@@ -3893,8 +3900,9 @@ __bpf_kfunc int bpf_strchrnul(const char *s__ign, char c)
return -ERANGE;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == '\0' || sc == c)
return i;
s__ign++;
@@ -3925,8 +3933,9 @@ __bpf_kfunc int bpf_strrchr(const char *s__ign, int c)
return -ERANGE;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&sc, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&sc, s__ign, char, err_out);
if (sc == c)
last = i;
if (sc == '\0')
@@ -3956,10 +3965,13 @@ __bpf_kfunc int bpf_strnlen(const char *s__ign, size_t count)
if (!copy_from_kernel_nofault_allowed(s__ign, 1))
return -ERANGE;
+ if (!count)
+ return 0;
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < count && i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&c, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&c, s__ign, char, err_out);
if (c == '\0')
return i;
s__ign++;
@@ -4008,12 +4020,13 @@ __bpf_kfunc int bpf_strspn(const char *s__ign, const char *accept__ign)
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&cs, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&ca, accept__ign + j, char, err_out);
+ __get_kernel_nofault_bare(&ca, accept__ign + j, char, err_out);
if (cs == ca || ca == '\0')
break;
}
@@ -4052,12 +4065,13 @@ __bpf_kfunc int bpf_strcspn(const char *s__ign, const char *reject__ign)
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
- __get_kernel_nofault(&cs, s__ign, char, err_out);
+ __get_kernel_nofault_bare(&cs, s__ign, char, err_out);
if (cs == '\0')
return i;
for (j = 0; j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&cr, reject__ign + j, char, err_out);
+ __get_kernel_nofault_bare(&cr, reject__ign + j, char, err_out);
if (cs == cr || cr == '\0')
break;
}
@@ -4084,9 +4098,10 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,
}
guard(pagefault)();
+ guard(__kernel_nofault_bare)();
for (i = 0; i < XATTR_SIZE_MAX; i++) {
for (j = 0; i + j <= len && j < XATTR_SIZE_MAX; j++) {
- __get_kernel_nofault(&c2, s2 + j, char, err_out);
+ __get_kernel_nofault_bare(&c2, s2 + j, char, err_out);
if (c2 == '\0')
return i;
/*
@@ -4096,7 +4111,7 @@ static int __bpf_strnstr(const char *s1, const char *s2, size_t len,
*/
if (i + j == len)
break;
- __get_kernel_nofault(&c1, s1 + j, char, err_out);
+ __get_kernel_nofault_bare(&c1, s1 + j, char, err_out);
if (ignore_case) {
c1 = tolower(c1);
diff --git a/mm/maccess.c b/mm/maccess.c
index 486559d688583..87486bf98d15a 100644
--- a/mm/maccess.c
+++ b/mm/maccess.c
@@ -19,7 +19,7 @@ bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
*/
#define copy_from_kernel_nofault_loop(dst, src, len, type, err_label) \
while (len >= sizeof(type)) { \
- __get_kernel_nofault(dst, src, type, err_label); \
+ __get_kernel_nofault_bare(dst, src, type, err_label); \
kmsan_check_memory(src, sizeof(type)); \
dst += sizeof(type); \
src += sizeof(type); \
@@ -35,26 +35,29 @@ long copy_from_kernel_nofault(void *dst, const void *src, size_t size)
if (!copy_from_kernel_nofault_allowed(src, size))
return -ERANGE;
+ if (!size)
+ return 0;
- pagefault_disable();
- if (!(align & 7))
- copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
- if (!(align & 3))
- copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
- if (!(align & 1))
- copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
- copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ if (!(align & 7))
+ copy_from_kernel_nofault_loop(dst, src, size, u64, Efault);
+ if (!(align & 3))
+ copy_from_kernel_nofault_loop(dst, src, size, u32, Efault);
+ if (!(align & 1))
+ copy_from_kernel_nofault_loop(dst, src, size, u16, Efault);
+ copy_from_kernel_nofault_loop(dst, src, size, u8, Efault);
+ }
+ }
return 0;
Efault:
- pagefault_enable();
return -EFAULT;
}
EXPORT_SYMBOL_GPL(copy_from_kernel_nofault);
#define copy_to_kernel_nofault_loop(dst, src, len, type, err_label) \
while (len >= sizeof(type)) { \
- __put_kernel_nofault(dst, src, type, err_label); \
+ __put_kernel_nofault_bare(dst, src, type, err_label); \
instrument_write(dst, sizeof(type)); \
dst += sizeof(type); \
src += sizeof(type); \
@@ -65,21 +68,25 @@ long copy_to_kernel_nofault(void *dst, const void *src, size_t size)
{
unsigned long align = 0;
+ if (!size)
+ return 0;
+
if (!IS_ENABLED(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS))
align = (unsigned long)dst | (unsigned long)src;
- pagefault_disable();
- if (!(align & 7))
- copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
- if (!(align & 3))
- copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
- if (!(align & 1))
- copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
- copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ if (!(align & 7))
+ copy_to_kernel_nofault_loop(dst, src, size, u64, Efault);
+ if (!(align & 3))
+ copy_to_kernel_nofault_loop(dst, src, size, u32, Efault);
+ if (!(align & 1))
+ copy_to_kernel_nofault_loop(dst, src, size, u16, Efault);
+ copy_to_kernel_nofault_loop(dst, src, size, u8, Efault);
+ }
+ }
return 0;
Efault:
- pagefault_enable();
return -EFAULT;
}
@@ -92,18 +99,19 @@ long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
if (!copy_from_kernel_nofault_allowed(unsafe_addr, count))
return -ERANGE;
- pagefault_disable();
- do {
- __get_kernel_nofault(dst, src, u8, Efault);
- dst++;
- src++;
- } while (dst[-1] && src - unsafe_addr < count);
- pagefault_enable();
+ scoped_guard(pagefault) {
+ scoped_guard(__kernel_nofault_bare) {
+ do {
+ __get_kernel_nofault_bare(dst, src, u8, Efault);
+ dst++;
+ src++;
+ } while (dst[-1] && src - unsafe_addr < count);
+ }
+ }
dst[-1] = '\0';
return src - unsafe_addr;
Efault:
- pagefault_enable();
dst[0] = '\0';
return -EFAULT;
}
|
| 17/3 | 2026/08/24 19:50 | llm | kmsan-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 7543 output: 252 thoughts: 739 Thoughts: **KMSAN Applicability Analysis for Kernel Memory Access Optimization** Okay, so I've been looking at this patch and its changes to the kernel's memory access routines, specifically focusing on its impact on KMSAN. This patch touches `arch/arm64/include/asm/uaccess.h`, `include/linux/uaccess.h`, `kernel/bpf/helpers.c`, and `mm/maccess.c`. Essentially, it's refactoring the `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, `strncpy_from_kernel_nofault`, and several BPF string helpers. The goal is to optimize the Memory Tagging Extension (MTE) on ARM64, namely the Tag Check Override (TCO) management. It achieves this by introducing `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to be used within a guarded region defined by `__begin_kernel_nofault_bare()` / `__end_kernel_nofault_bare()`. My primary concern is whether this introduces any new opportunities for KMSAN to detect uninitialized memory reads. But from what I can see, it doesn't. This patch doesn't introduce any new structures sent to user space, so that's a check. It doesn't use uninitialized variables to control program flow. It also doesn't involve any significant new memory allocations or complex data structures. The changes are focused on optimizing how existing memory regions are accessed safely. It's really about minimizing overhead, specifically the TCO toggle, which would have been expensive in the loop. I see that `copy_from_kernel_nofault_loop` already calls `kmsan_check_memory(src, sizeof(type));`. The patch doesn't remove this. It just changes the internal helper calls within the loop from `__get_kernel_nofault` to `__get_kernel_nofault_bare`, and wraps the whole loop in the guarded region. That's a good thing, because the KMSAN checks already exist. My conclusion? This patch is a refactoring/optimization. I don't see any changes that would introduce new KMSAN-specific risks. If there were bugs introduced here, they'd likely manifest as out-of-bounds accesses, page faults, or logical errors. KASAN and standard kernel tests would, in my experience, catch those. Therefore, KMSAN doesn't need to be specifically targeted with additional fuzzing sessions for this change. The existing memory safety infrastructure should cover it. |
| 18/3 | 2026/08/24 19:50 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch refactors `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, and several BPF string helper functions to optimize Tag Check Override (TCO) management on ARM64. It introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to be used within a guarded region (`guard(__kernel_nofault_bare)`), avoiding the overhead of toggling TCO on every memory access inside loops.\n\nThese changes do not introduce new data structures sent to user space, nor do they add complex allocations or conditional logic that might depend on uninitialized memory. The existing `kmsan_check_memory` calls are preserved. Any potential bugs introduced by this refactoring (such as incorrect bounds checking, missing early returns, or improper page fault handling) would result in out-of-bounds accesses, invalid memory dereferences, or crashes. These types of memory corruption and access violations are already effectively detected by KASAN and standard kernel testing. Therefore, a dedicated KMSAN fuzzing session is not necessary.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch refactors `copy_from_kernel_nofault`, `copy_to_kernel_nofault`, and several BPF string helper functions to optimize Tag Check Override (TCO) management on ARM64. It introduces `__get_kernel_nofault_bare` and `__put_kernel_nofault_bare` to be used within a guarded region (`guard(__kernel_nofault_bare)`), avoiding the overhead of toggling TCO on every memory access inside loops.\n\nThese changes do not introduce new data structures sent to user space, nor do they add complex allocations or conditional logic that might depend on uninitialized memory. The existing `kmsan_check_memory` calls are preserved. Any potential bugs introduced by this refactoring (such as incorrect bounds checking, missing early returns, or improper page fault handling) would result in out-of-bounds accesses, invalid memory dereferences, or crashes. These types of memory corruption and access violations are already effectively detected by KASAN and standard kernel testing. Therefore, a dedicated KMSAN fuzzing session is not necessary.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|