AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/13 22:38 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "HUGETLBFS"
  ],
  "FocusSymbols": [
    "allocate_file_region_entries",
    "region_add",
    "region_chg",
    "region_del"
  ],
  "Reasoning": "The patch fixes a list corruption bug in allocate_file_region_entries in hugetlbfs. The bug is triggered by a race condition when the region cache needs to be refilled multiple times. Fuzzing hugetlbfs reservation functions can help ensure the fix is robust.",
  "WorthFuzzing": true
}

1/1 2026/07/13 22:38 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 8adbd5b6f4b467aae12cb91750c1b0b86742b6e6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jul 13 22:38:12 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex 571212b80835ec..f9577a789fe64f 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -693,7 +693,7 @@ static int allocate_file_region_entries(struct resv_map *resv,\n \n \t\tspin_lock(\u0026resv-\u003elock);\n \n-\t\tlist_splice(\u0026allocated_regions, \u0026resv-\u003eregion_cache);\n+\t\tlist_splice_init(\u0026allocated_regions, \u0026resv-\u003eregion_cache);\n \t\tresv-\u003eregion_cache_count += to_allocate;\n \t}\n \ndiff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore\nindex 9ccd9e1447e66b..7db8657960d9f0 100644\n--- a/tools/testing/selftests/mm/.gitignore\n+++ b/tools/testing/selftests/mm/.gitignore\n@@ -59,6 +59,7 @@ hugetlb_madv_vs_map\n mseal_test\n droppable\n hugetlb_dio\n+hugetlb_region_cache_race\n pkey_sighandler_tests_32\n pkey_sighandler_tests_64\n guard-regions\ndiff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile\nindex e6df968f0971c8..421a0b7fdddf28 100644\n--- a/tools/testing/selftests/mm/Makefile\n+++ b/tools/testing/selftests/mm/Makefile\n@@ -67,6 +67,7 @@ TEST_GEN_FILES += hugetlb-read-hwpoison\n TEST_GEN_FILES += hugetlb-shm\n TEST_GEN_FILES += hugetlb-soft-offline\n TEST_GEN_FILES += hugetlb-vmemmap\n+TEST_GEN_FILES += hugetlb_region_cache_race\n TEST_GEN_FILES += khugepaged\n TEST_GEN_FILES += madv_populate\n TEST_GEN_FILES += map_fixed_noreplace\ndiff --git a/tools/testing/selftests/mm/hugetlb_region_cache_race.c b/tools/testing/selftests/mm/hugetlb_region_cache_race.c\nnew file mode 100644\nindex 00000000000000..6e3f753640be8c\n--- /dev/null\n+++ b/tools/testing/selftests/mm/hugetlb_region_cache_race.c\n@@ -0,0 +1,315 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Regression test for list corruption in\n+ * mm/hugetlb.c:allocate_file_region_entries().\n+ *\n+ * Fork processes sharing a hugetlb inode via `memfd_create(MFD_HUGETLB)`\n+ * and have them fault ranges and punch holes to fragment the shared\n+ * `resv_map` while draining `resv-\u003eregion_cache` from a separate address\n+ * space.\n+ */\n+#define _GNU_SOURCE\n+#include \u003cerrno.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003csetjmp.h\u003e\n+#include \u003csignal.h\u003e\n+#include \u003cstdbool.h\u003e\n+#include \u003cstdint.h\u003e\n+#include \u003cstdio.h\u003e\n+#include \u003cstdlib.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003ctime.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003csys/mman.h\u003e\n+#include \u003csys/syscall.h\u003e\n+#include \u003csys/wait.h\u003e\n+\n+#include \u003clinux/falloc.h\u003e\n+#include \u003clinux/memfd.h\u003e\n+\n+#include \"../kselftest.h\"\n+#include \"hugepage_settings.h\"\n+\n+/* Tunables (overridable via argv). */\n+static unsigned long file_pages = 64;\t/* huge pages backing the shared file */\n+static unsigned int nr_procs;\t\t/* 0 =\u003e auto (2 * nr online cpus) */\n+static unsigned int duration_sec = 5;\t/* --trigger run time */\n+\n+static unsigned long huge_page_size;\t/* bytes */\n+static int memfd = -1;\n+static volatile int *stop;\t\t/* shared across forked children */\n+\n+/*\n+ * Per-process SIGBUS escape: a fault can fail (pool exhausted) and raise\n+ * SIGBUS; jump back and abandon the rest of the current range instead of\n+ * dying.  Each child is single-threaded, so a file-scope buffer is fine.\n+ */\n+static sigjmp_buf sigbus_env;\n+\n+static void sigbus_handler(int sig)\n+{\n+\tsiglongjmp(sigbus_env, 1);\n+}\n+\n+static void install_sigbus_handler(void)\n+{\n+\tstruct sigaction sa = {\n+\t\t.sa_handler = sigbus_handler,\n+\t};\n+\n+\tsigemptyset(\u0026sa.sa_mask);\n+\tsigaction(SIGBUS, \u0026sa, NULL);\n+}\n+\n+static long sys_memfd_create(const char *name, unsigned int flags)\n+{\n+\treturn syscall(__NR_memfd_create, name, flags);\n+}\n+\n+/*\n+ * Map [start, start+len) of the shared file and fault every page in.\n+ *\n+ * The mmap() drives mmap-time hugetlb_reserve_pages -\u003e region_chg (the\n+ * wide-window victim when the range is fragmented) + region_add.  The\n+ * subsequent stores fault pages in *without* inode_lock, driving the\n+ * lock-free region_add/region_chg that drains resv-\u003eregion_cache -- the\n+ * concurrency the buggy loop needs from *other* processes.\n+ */\n+static void map_and_fault_range(unsigned long start, unsigned long len)\n+{\n+\tunsigned long i;\n+\tchar *addr;\n+\n+\tif (!len)\n+\t\treturn;\n+\taddr = mmap(NULL, len * huge_page_size, PROT_READ | PROT_WRITE,\n+\t\t    MAP_SHARED, memfd, start * huge_page_size);\n+\tif (addr == MAP_FAILED)\n+\t\treturn;\t\t/* ENOMEM under pool pressure is fine */\n+\n+\tfor (i = 0; i \u003c len; i++) {\n+\t\tif (sigsetjmp(sigbus_env, 1) == 0)\n+\t\t\t*(volatile char *)(addr + i * huge_page_size) = (char)i;\n+\t\telse\n+\t\t\tbreak;\t/* SIGBUS: pool exhausted, stop faulting */\n+\t}\n+\tmunmap(addr, len * huge_page_size);\n+}\n+\n+/*\n+ * Remove [start, start+len) huge pages from the shared file, driving\n+ * hugetlbfs_punch_hole -\u003e remove_inode_hugepages -\u003e region_del for the pages\n+ * that were faulted in above.  This frees pages back to the pool (bounding\n+ * residency) and re-fragments the resv_map so later region_chg calls keep\n+ * needing multiple entries.\n+ */\n+static void punch_range(unsigned long start, unsigned long len)\n+{\n+\tif (!len)\n+\t\treturn;\n+\tfallocate(memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,\n+\t\t  start * huge_page_size, len * huge_page_size);\n+}\n+\n+/*\n+ * One forked worker.  Each worker is simultaneously a victim (its wide\n+ * mmap-time region_chg) and, for the other workers, a lock-free cache\n+ * drainer (its faults) -- exactly the shared-inode map+fault churn seen in\n+ * the original crash (QEMU prealloc + SPDK/Bytelight on one guest-RAM file).\n+ */\n+static void child_worker(unsigned int id)\n+{\n+\tunsigned int seed = id * 2654435761u ^ (unsigned int)time(NULL) ^\n+\t\t\t    ((unsigned int)getpid() \u003c\u003c 1);\n+\n+\tinstall_sigbus_handler();\n+\n+\twhile (!*stop) {\n+\t\tunsigned long start = rand_r(\u0026seed) % file_pages;\n+\t\tunsigned long len = 1 + rand_r(\u0026seed) % (file_pages - start);\n+\n+\t\t/*\n+\t\t * Bias toward map+fault (victims and drainers); punch a\n+\t\t * quarter of the time to keep the map fragmented and to cap\n+\t\t * how many pages stay resident.\n+\t\t */\n+\t\tif (rand_r(\u0026seed) % 4)\n+\t\t\tmap_and_fault_range(start, len);\n+\t\telse\n+\t\t\tpunch_range(start, len);\n+\t}\n+}\n+\n+/*\n+ * Safe, single-process functional check.  It fragments the shared resv_map\n+ * (reservations for a shared mapping persist on the inode after munmap) so\n+ * that a subsequent whole-file mmap makes region_chg compute\n+ * regions_needed \u003e 1, forcing allocate_file_region_entries to allocate and\n+ * splice several entries.  This exercises the buggy line, but without\n+ * concurrency it always makes exactly one loop iteration, so it cannot trip\n+ * the race and is safe on any kernel.  Returns 0 on success.\n+ */\n+static int functional_check(void)\n+{\n+\tunsigned long i;\n+\tvoid *addr;\n+\n+\t/* Punch everything so we start from an empty resv_map. */\n+\tpunch_range(0, file_pages);\n+\n+\t/* Reserve every other huge page -\u003e ~file_pages/2 disjoint regions. */\n+\tfor (i = 0; i \u003c file_pages; i += 2)\n+\t\tmap_and_fault_range(i, 1);\n+\n+\t/*\n+\t * One mapping spanning the whole file must fill all the holes in a\n+\t * single region_chg -\u003e a multi-entry region_cache refill.\n+\t */\n+\taddr = mmap(NULL, file_pages * huge_page_size, PROT_READ | PROT_WRITE,\n+\t\t    MAP_SHARED, memfd, 0);\n+\tif (addr == MAP_FAILED) {\n+\t\tksft_print_msg(\"whole-file mmap failed: %s\\n\", strerror(errno));\n+\t\treturn -1;\n+\t}\n+\tmunmap(addr, file_pages * huge_page_size);\n+\tpunch_range(0, file_pages);\n+\treturn 0;\n+}\n+\n+static int run_trigger(void)\n+{\n+\tpid_t *pids;\n+\tunsigned int i, started = 0;\n+\n+\tstop = mmap(NULL, sizeof(*stop), PROT_READ | PROT_WRITE,\n+\t\t    MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n+\tif (stop == MAP_FAILED)\n+\t\tksft_exit_fail_msg(\"mmap(stop flag): %s\\n\", strerror(errno));\n+\t*stop = 0;\n+\n+\tpids = calloc(nr_procs, sizeof(*pids));\n+\tif (!pids)\n+\t\tksft_exit_fail_msg(\"calloc: %s\\n\", strerror(errno));\n+\n+\tksft_print_msg(\"stressing %u processes for %us on %lu huge pages (%lu MiB)\\n\",\n+\t\t       nr_procs, duration_sec, file_pages,\n+\t\t       (file_pages * huge_page_size) \u003e\u003e 20);\n+\tksft_print_msg(\"WARNING: a vulnerable kernel will panic here\\n\");\n+\n+\tfor (i = 0; i \u003c nr_procs; i++) {\n+\t\tpid_t pid = fork();\n+\n+\t\tif (pid \u003c 0) {\n+\t\t\tksft_print_msg(\"fork: %s\\n\", strerror(errno));\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (pid == 0) {\n+\t\t\tfree(pids);\n+\t\t\tchild_worker(i);\n+\t\t\t_exit(0);\n+\t\t}\n+\t\tpids[started++] = pid;\n+\t}\n+\n+\tif (!started) {\n+\t\tfree(pids);\n+\t\tmunmap((void *)stop, sizeof(*stop));\n+\t\tksft_exit_fail_msg(\"no worker processes started\\n\");\n+\t}\n+\n+\tsleep(duration_sec);\n+\t*stop = 1;\n+\n+\tfor (i = 0; i \u003c started; i++)\n+\t\twaitpid(pids[i], NULL, 0);\n+\tfree(pids);\n+\tmunmap((void *)stop, sizeof(*stop));\n+\treturn 0;\n+}\n+\n+static void usage(const char *prog)\n+{\n+\tfprintf(stderr,\n+\t\t\"usage: %s [--trigger] [-s huge_pages] [-t procs] [-d seconds]\\n\"\n+\t\t\"  (default) run a safe single-process functional check\\n\"\n+\t\t\"  --trigger fork+fault concurrent reproducer (CAN PANIC A BUGGY HOST)\\n\"\n+\t\t\"  -s  huge pages backing the shared file (default %lu)\\n\"\n+\t\t\"  -t  worker processes for --trigger (default 2 * nr_cpus)\\n\"\n+\t\t\"  -d  --trigger run time in seconds (default %u)\\n\",\n+\t\tprog, file_pages, duration_sec);\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tbool trigger = false;\n+\tint i, ret;\n+\n+\tfor (i = 1; i \u003c argc; i++) {\n+\t\tif (!strcmp(argv[i], \"--trigger\")) {\n+\t\t\ttrigger = true;\n+\t\t} else if (!strcmp(argv[i], \"-s\") \u0026\u0026 i + 1 \u003c argc) {\n+\t\t\tfile_pages = strtoul(argv[++i], NULL, 0);\n+\t\t} else if (!strcmp(argv[i], \"-t\") \u0026\u0026 i + 1 \u003c argc) {\n+\t\t\tnr_procs = strtoul(argv[++i], NULL, 0);\n+\t\t} else if (!strcmp(argv[i], \"-d\") \u0026\u0026 i + 1 \u003c argc) {\n+\t\t\tduration_sec = strtoul(argv[++i], NULL, 0);\n+\t\t} else {\n+\t\t\tusage(argv[0]);\n+\t\t\treturn KSFT_FAIL;\n+\t\t}\n+\t}\n+\n+\tksft_print_header();\n+\tksft_set_plan(1);\n+\n+\tif (file_pages \u003c 4)\n+\t\tfile_pages = 4;\n+\tif (!nr_procs) {\n+\t\tlong cpus = sysconf(_SC_NPROCESSORS_ONLN);\n+\n+\t\tnr_procs = cpus \u003e 0 ? (unsigned int)cpus * 2 : 4;\n+\t}\n+\tif (nr_procs \u003c 2)\n+\t\tnr_procs = 2;\n+\n+\thuge_page_size = default_huge_page_size();\n+\tif (!huge_page_size)\n+\t\tksft_exit_skip(\"no hugetlbfs / huge page size available\\n\");\n+\n+\t/*\n+\t * Both modes fault pages in (functional_check touches every page it\n+\t * maps, and --trigger's workers fault the ranges they map), so the\n+\t * whole file may become resident; require that many *free* huge pages,\n+\t * not just reservations.\n+\t */\n+\tif (hugetlb_free_default_pages() \u003c file_pages)\n+\t\tksft_exit_skip(\"need %lu free huge pages, have %lu (set nr_hugepages)\\n\",\n+\t\t\t       file_pages, hugetlb_free_default_pages());\n+\n+\tmemfd = sys_memfd_create(\"hugetlb_region_cache_race\",\n+\t\t\t\t MFD_HUGETLB |\n+\t\t\t\t (__builtin_ctzl(huge_page_size) \u003c\u003c MFD_HUGE_SHIFT));\n+\tif (memfd \u003c 0)\n+\t\tksft_exit_skip(\"memfd_create(MFD_HUGETLB): %s\\n\",\n+\t\t\t       strerror(errno));\n+\tif (ftruncate(memfd, file_pages * huge_page_size)) {\n+\t\tclose(memfd);\n+\t\tksft_exit_fail_msg(\"ftruncate: %s\\n\", strerror(errno));\n+\t}\n+\n+\tif (trigger)\n+\t\tret = run_trigger();\n+\telse\n+\t\tret = functional_check();\n+\n+\tclose(memfd);\n+\n+\tif (ret)\n+\t\tksft_test_result_fail(\"hugetlb region_cache race check\\n\");\n+\telse if (trigger)\n+\t\tksft_test_result_pass(\"survived concurrent region_cache churn\\n\");\n+\telse\n+\t\tksft_test_result_pass(\"multi-entry region_cache refill works\\n\");\n+\n+\tksft_finished();\n+}\ndiff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh\nindex 8c296dedf0474d..0cb857056915f4 100755\n--- a/tools/testing/selftests/mm/run_vmtests.sh\n+++ b/tools/testing/selftests/mm/run_vmtests.sh\n@@ -267,6 +267,7 @@ CATEGORY=\"hugetlb\" run_test ./hugetlb-madvise\n CATEGORY=\"hugetlb\" run_test ./hugetlb_dio\n CATEGORY=\"hugetlb\" run_test ./hugetlb_fault_after_madv\n CATEGORY=\"hugetlb\" run_test ./hugetlb_madv_vs_map\n+CATEGORY=\"hugetlb\" run_test ./hugetlb_region_cache_race\n \n if test_selected \"hugetlb\"; then\n \techo \"NOTE: These hugetlb tests provide minimal coverage.  Use\"\t  | tap_prefix\n"
}

2/1 2026/07/13 22:38 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "HUGETLBFS"
  ],
  "FocusSymbols": [
    "allocate_file_region_entries",
    "region_add",
    "region_chg",
    "region_del"
  ],
  "Reasoning": "The patch fixes a list corruption bug in allocate_file_region_entries in hugetlbfs. The bug is triggered by a race condition when the region cache needs to be refilled multiple times. Fuzzing hugetlbfs reservation functions can help ensure the fix is robust.",
  "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 8adbd5b6f4b467aae12cb91750c1b0b86742b6e6
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jul 13 22:38:12 2026 +0000

    syz-cluster: applied patch under review

diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835ec..f9577a789fe64f 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -693,7 +693,7 @@ static int allocate_file_region_entries(struct resv_map *resv,
 
 		spin_lock(&resv->lock);
 
-		list_splice(&allocated_regions, &resv->region_cache);
+		list_splice_init(&allocated_regions, &resv->region_cache);
 		resv->region_cache_count += to_allocate;
 	}
 
diff --git a/tools/testing/selftests/mm/.gitignore b/tools/testing/selftests/mm/.gitignore
index 9ccd9e1447e66b..7db8657960d9f0 100644
--- a/tools/testing/selftests/mm/.gitignore
+++ b/tools/testing/selftests/mm/.gitignore
@@ -59,6 +59,7 @@ hugetlb_madv_vs_map
 mseal_test
 droppable
 hugetlb_dio
+hugetlb_region_cache_race
 pkey_sighandler_tests_32
 pkey_sighandler_tests_64
 guard-regions
diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index e6df968f0971c8..421a0b7fdddf28 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -67,6 +67,7 @@ TEST_GEN_FILES += hugetlb-read-hwpoison
 TEST_GEN_FILES += hugetlb-shm
 TEST_GEN_FILES += hugetlb-soft-offline
 TEST_GEN_FILES += hugetlb-vmemmap
+TEST_GEN_FILES += hugetlb_region_cache_race
 TEST_GEN_FILES += khugepaged
 TEST_GEN_FILES += madv_populate
 TEST_GEN_FILES += map_fixed_noreplace
diff --git a/tools/testing/selftests/mm/hugetlb_region_cache_race.c b/tools/testing/selftests/mm/hugetlb_region_cache_race.c
new file mode 100644
index 00000000000000..6e3f753640be8c
--- /dev/null
+++ b/tools/testing/selftests/mm/hugetlb_region_cache_race.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Regression test for list corruption in
+ * mm/hugetlb.c:allocate_file_region_entries().
+ *
+ * Fork processes sharing a hugetlb inode via `memfd_create(MFD_HUGETLB)`
+ * and have them fault ranges and punch holes to fragment the shared
+ * `resv_map` while draining `resv->region_cache` from a separate address
+ * space.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+
+#include <linux/falloc.h>
+#include <linux/memfd.h>
+
+#include "../kselftest.h"
+#include "hugepage_settings.h"
+
+/* Tunables (overridable via argv). */
+static unsigned long file_pages = 64;	/* huge pages backing the shared file */
+static unsigned int nr_procs;		/* 0 => auto (2 * nr online cpus) */
+static unsigned int duration_sec = 5;	/* --trigger run time */
+
+static unsigned long huge_page_size;	/* bytes */
+static int memfd = -1;
+static volatile int *stop;		/* shared across forked children */
+
+/*
+ * Per-process SIGBUS escape: a fault can fail (pool exhausted) and raise
+ * SIGBUS; jump back and abandon the rest of the current range instead of
+ * dying.  Each child is single-threaded, so a file-scope buffer is fine.
+ */
+static sigjmp_buf sigbus_env;
+
+static void sigbus_handler(int sig)
+{
+	siglongjmp(sigbus_env, 1);
+}
+
+static void install_sigbus_handler(void)
+{
+	struct sigaction sa = {
+		.sa_handler = sigbus_handler,
+	};
+
+	sigemptyset(&sa.sa_mask);
+	sigaction(SIGBUS, &sa, NULL);
+}
+
+static long sys_memfd_create(const char *name, unsigned int flags)
+{
+	return syscall(__NR_memfd_create, name, flags);
+}
+
+/*
+ * Map [start, start+len) of the shared file and fault every page in.
+ *
+ * The mmap() drives mmap-time hugetlb_reserve_pages -> region_chg (the
+ * wide-window victim when the range is fragmented) + region_add.  The
+ * subsequent stores fault pages in *without* inode_lock, driving the
+ * lock-free region_add/region_chg that drains resv->region_cache -- the
+ * concurrency the buggy loop needs from *other* processes.
+ */
+static void map_and_fault_range(unsigned long start, unsigned long len)
+{
+	unsigned long i;
+	char *addr;
+
+	if (!len)
+		return;
+	addr = mmap(NULL, len * huge_page_size, PROT_READ | PROT_WRITE,
+		    MAP_SHARED, memfd, start * huge_page_size);
+	if (addr == MAP_FAILED)
+		return;		/* ENOMEM under pool pressure is fine */
+
+	for (i = 0; i < len; i++) {
+		if (sigsetjmp(sigbus_env, 1) == 0)
+			*(volatile char *)(addr + i * huge_page_size) = (char)i;
+		else
+			break;	/* SIGBUS: pool exhausted, stop faulting */
+	}
+	munmap(addr, len * huge_page_size);
+}
+
+/*
+ * Remove [start, start+len) huge pages from the shared file, driving
+ * hugetlbfs_punch_hole -> remove_inode_hugepages -> region_del for the pages
+ * that were faulted in above.  This frees pages back to the pool (bounding
+ * residency) and re-fragments the resv_map so later region_chg calls keep
+ * needing multiple entries.
+ */
+static void punch_range(unsigned long start, unsigned long len)
+{
+	if (!len)
+		return;
+	fallocate(memfd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
+		  start * huge_page_size, len * huge_page_size);
+}
+
+/*
+ * One forked worker.  Each worker is simultaneously a victim (its wide
+ * mmap-time region_chg) and, for the other workers, a lock-free cache
+ * drainer (its faults) -- exactly the shared-inode map+fault churn seen in
+ * the original crash (QEMU prealloc + SPDK/Bytelight on one guest-RAM file).
+ */
+static void child_worker(unsigned int id)
+{
+	unsigned int seed = id * 2654435761u ^ (unsigned int)time(NULL) ^
+			    ((unsigned int)getpid() << 1);
+
+	install_sigbus_handler();
+
+	while (!*stop) {
+		unsigned long start = rand_r(&seed) % file_pages;
+		unsigned long len = 1 + rand_r(&seed) % (file_pages - start);
+
+		/*
+		 * Bias toward map+fault (victims and drainers); punch a
+		 * quarter of the time to keep the map fragmented and to cap
+		 * how many pages stay resident.
+		 */
+		if (rand_r(&seed) % 4)
+			map_and_fault_range(start, len);
+		else
+			punch_range(start, len);
+	}
+}
+
+/*
+ * Safe, single-process functional check.  It fragments the shared resv_map
+ * (reservations for a shared mapping persist on the inode after munmap) so
+ * that a subsequent whole-file mmap makes region_chg compute
+ * regions_needed > 1, forcing allocate_file_region_entries to allocate and
+ * splice several entries.  This exercises the buggy line, but without
+ * concurrency it always makes exactly one loop iteration, so it cannot trip
+ * the race and is safe on any kernel.  Returns 0 on success.
+ */
+static int functional_check(void)
+{
+	unsigned long i;
+	void *addr;
+
+	/* Punch everything so we start from an empty resv_map. */
+	punch_range(0, file_pages);
+
+	/* Reserve every other huge page -> ~file_pages/2 disjoint regions. */
+	for (i = 0; i < file_pages; i += 2)
+		map_and_fault_range(i, 1);
+
+	/*
+	 * One mapping spanning the whole file must fill all the holes in a
+	 * single region_chg -> a multi-entry region_cache refill.
+	 */
+	addr = mmap(NULL, file_pages * huge_page_size, PROT_READ | PROT_WRITE,
+		    MAP_SHARED, memfd, 0);
+	if (addr == MAP_FAILED) {
+		ksft_print_msg("whole-file mmap failed: %s\n", strerror(errno));
+		return -1;
+	}
+	munmap(addr, file_pages * huge_page_size);
+	punch_range(0, file_pages);
+	return 0;
+}
+
+static int run_trigger(void)
+{
+	pid_t *pids;
+	unsigned int i, started = 0;
+
+	stop = mmap(NULL, sizeof(*stop), PROT_READ | PROT_WRITE,
+		    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	if (stop == MAP_FAILED)
+		ksft_exit_fail_msg("mmap(stop flag): %s\n", strerror(errno));
+	*stop = 0;
+
+	pids = calloc(nr_procs, sizeof(*pids));
+	if (!pids)
+		ksft_exit_fail_msg("calloc: %s\n", strerror(errno));
+
+	ksft_print_msg("stressing %u processes for %us on %lu huge pages (%lu MiB)\n",
+		       nr_procs, duration_sec, file_pages,
+		       (file_pages * huge_page_size) >> 20);
+	ksft_print_msg("WARNING: a vulnerable kernel will panic here\n");
+
+	for (i = 0; i < nr_procs; i++) {
+		pid_t pid = fork();
+
+		if (pid < 0) {
+			ksft_print_msg("fork: %s\n", strerror(errno));
+			break;
+		}
+		if (pid == 0) {
+			free(pids);
+			child_worker(i);
+			_exit(0);
+		}
+		pids[started++] = pid;
+	}
+
+	if (!started) {
+		free(pids);
+		munmap((void *)stop, sizeof(*stop));
+		ksft_exit_fail_msg("no worker processes started\n");
+	}
+
+	sleep(duration_sec);
+	*stop = 1;
+
+	for (i = 0; i < started; i++)
+		waitpid(pids[i], NULL, 0);
+	free(pids);
+	munmap((void *)stop, sizeof(*stop));
+	return 0;
+}
+
+static void usage(const char *prog)
+{
+	fprintf(stderr,
+		"usage: %s [--trigger] [-s huge_pages] [-t procs] [-d seconds]\n"
+		"  (default) run a safe single-process functional check\n"
+		"  --trigger fork+fault concurrent reproducer (CAN PANIC A BUGGY HOST)\n"
+		"  -s  huge pages backing the shared file (default %lu)\n"
+		"  -t  worker processes for --trigger (default 2 * nr_cpus)\n"
+		"  -d  --trigger run time in seconds (default %u)\n",
+		prog, file_pages, duration_sec);
+}
+
+int main(int argc, char *argv[])
+{
+	bool trigger = false;
+	int i, ret;
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "--trigger")) {
+			trigger = true;
+		} else if (!strcmp(argv[i], "-s") && i + 1 < argc) {
+			file_pages = strtoul(argv[++i], NULL, 0);
+		} else if (!strcmp(argv[i], "-t") && i + 1 < argc) {
+			nr_procs = strtoul(argv[++i], NULL, 0);
+		} else if (!strcmp(argv[i], "-d") && i + 1 < argc) {
+			duration_sec = strtoul(argv[++i], NULL, 0);
+		} else {
+			usage(argv[0]);
+			return KSFT_FAIL;
+		}
+	}
+
+	ksft_print_header();
+	ksft_set_plan(1);
+
+	if (file_pages < 4)
+		file_pages = 4;
+	if (!nr_procs) {
+		long cpus = sysconf(_SC_NPROCESSORS_ONLN);
+
+		nr_procs = cpus > 0 ? (unsigned int)cpus * 2 : 4;
+	}
+	if (nr_procs < 2)
+		nr_procs = 2;
+
+	huge_page_size = default_huge_page_size();
+	if (!huge_page_size)
+		ksft_exit_skip("no hugetlbfs / huge page size available\n");
+
+	/*
+	 * Both modes fault pages in (functional_check touches every page it
+	 * maps, and --trigger's workers fault the ranges they map), so the
+	 * whole file may become resident; require that many *free* huge pages,
+	 * not just reservations.
+	 */
+	if (hugetlb_free_default_pages() < file_pages)
+		ksft_exit_skip("need %lu free huge pages, have %lu (set nr_hugepages)\n",
+			       file_pages, hugetlb_free_default_pages());
+
+	memfd = sys_memfd_create("hugetlb_region_cache_race",
+				 MFD_HUGETLB |
+				 (__builtin_ctzl(huge_page_size) << MFD_HUGE_SHIFT));
+	if (memfd < 0)
+		ksft_exit_skip("memfd_create(MFD_HUGETLB): %s\n",
+			       strerror(errno));
+	if (ftruncate(memfd, file_pages * huge_page_size)) {
+		close(memfd);
+		ksft_exit_fail_msg("ftruncate: %s\n", strerror(errno));
+	}
+
+	if (trigger)
+		ret = run_trigger();
+	else
+		ret = functional_check();
+
+	close(memfd);
+
+	if (ret)
+		ksft_test_result_fail("hugetlb region_cache race check\n");
+	else if (trigger)
+		ksft_test_result_pass("survived concurrent region_cache churn\n");
+	else
+		ksft_test_result_pass("multi-entry region_cache refill works\n");
+
+	ksft_finished();
+}
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index 8c296dedf0474d..0cb857056915f4 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -267,6 +267,7 @@ CATEGORY="hugetlb" run_test ./hugetlb-madvise
 CATEGORY="hugetlb" run_test ./hugetlb_dio
 CATEGORY="hugetlb" run_test ./hugetlb_fault_after_madv
 CATEGORY="hugetlb" run_test ./hugetlb_madv_vs_map
+CATEGORY="hugetlb" run_test ./hugetlb_region_cache_race
 
 if test_selected "hugetlb"; then
 	echo "NOTE: These hugetlb tests provide minimal coverage.  Use"	  | tap_prefix

3/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6158
							output: 34
							thoughts: 247
						

Thoughts:
**Analyzing a Kernel Patch: HugeTLB Modification**

Okay, so I've got this patch here that touches `mm/hugetlb.c` and also adds a new selftest. The core of the change is this subtle modification within `allocate_file_region_entries`. They've swapped out `list_splice` for `list_splice_init` in that function. That's a significant change to core kernel code. Anytime we see something like this, a change to memory management, HugeTLB in this case, my first thought is "fuzzing". This needs a thorough examination.

Let's quickly verify that the `allocate_file_region_entries` function is indeed the location of the change, as indicated by the diff. I'll fire up `grepper` and double-check, but I'm pretty sure that's the one. Let's make sure before we move on.



4/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "allocate_file_region_entries",
  "PathPrefix": "mm/hugetlb.c"
}

Results:
{
  "Output": "mm/hugetlb.c=594=static long add_reservation_in_range(struct resv_map *resv, long f, long t,\n--\nmm/hugetlb.c-655- */\nmm/hugetlb.c:656:static int allocate_file_region_entries(struct resv_map *resv,\nmm/hugetlb.c-657-\t\t\t\t\tint regions_needed)\n--\nmm/hugetlb.c=727=static long region_add(struct resv_map *resv, long f, long t,\n--\nmm/hugetlb.c-757-\nmm/hugetlb.c:758:\t\tif (allocate_file_region_entries(\nmm/hugetlb.c-759-\t\t\t    resv, actual_regions_needed - in_regions_needed)) {\n--\nmm/hugetlb.c=794=static long region_chg(struct resv_map *resv, long f, long t,\n--\nmm/hugetlb.c-807-\nmm/hugetlb.c:808:\tif (allocate_file_region_entries(resv, *out_regions_needed))\nmm/hugetlb.c-809-\t\treturn -ENOMEM;\n--\nmm/hugetlb.c=5141=void __unmap_hugepage_range(struct mmu_gather *tlb, struct vm_area_struct *vma,\n--\nmm/hugetlb.c-5272-\t\t\tif (rc \u003c 0)\nmm/hugetlb.c:5273:\t\t\t\t/* Pressumably allocate_file_region_entries failed\nmm/hugetlb.c-5274-\t\t\t\t * to allocate a file_region struct. Clear\n"
}

5/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7085
							output: 26
							thoughts: 68
						

Thoughts:
**Analyzing the `allocate_file_region_entries` Function**

Okay, so I'm looking at this `allocate_file_region_entries` function – seems important, and I can see it's changed in `mm/hugetlb.c`. My initial thought is to quickly assess any configuration dependencies. With HugeTLB changes, `CONFIG_HUGETLBFS` and `CONFIG_HUGETLB_PAGE` are the obvious ones to check, but I can't rely on memory alone. Just to be sure, I should verify the build configurations related to this area. I'll take a look at `mm/Makefile` to confirm which settings are relevant to this function and the module it lives in.



6/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "hugetlb",
  "PathPrefix": "mm/Makefile"
}

Results:
{
  "Output": "mm/Makefile=80=obj-$(CONFIG_HAS_DMA)\t+= dmapool.o\nmm/Makefile:81:obj-$(CONFIG_HUGETLBFS)\t+= hugetlb.o hugetlb_sysfs.o hugetlb_sysctl.o\nmm/Makefile-82-ifdef CONFIG_CMA\nmm/Makefile:83:obj-$(CONFIG_HUGETLBFS)\t+= hugetlb_cma.o\nmm/Makefile-84-endif\nmm/Makefile:85:obj-$(CONFIG_HUGETLB_PAGE_OPTIMIZE_VMEMMAP)\t+= hugetlb_vmemmap.o\nmm/Makefile-86-obj-$(CONFIG_NUMA) \t+= mempolicy.o\n--\nmm/Makefile=108=endif\nmm/Makefile:109:obj-$(CONFIG_CGROUP_HUGETLB) += hugetlb_cgroup.o\nmm/Makefile-110-obj-$(CONFIG_GUP_TEST) += gup_test.o\n"
}

7/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7588
							output: 19
							thoughts: 53
						

Thoughts:
Okay, here's my take on those thoughts, framed as my own mental process:

**Diving into HugeTLB Configuration**

Right, so I see `CONFIG_HUGETLBFS` mentioned, that's the starting point. But, hang on, there's also `CONFIG_HUGETLB_PAGE` – those are definitely intertwined. I need to get the full picture. My first instinct is to dive into the kernel configuration files. Let's see... `init/Kconfig` is a possibility, but probably not the best spot for file system options, so let's start with `fs/Kconfig`. Or maybe it's memory management, so `mm/Kconfig` might be the place to go first. I know I will find it there. I need to confirm the relationship between these options and get the details.



8/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "config HUGETLBFS"
}

Results:
{
  "Output": "fs/Kconfig=247=config ARCH_SUPPORTS_HUGETLBFS\n--\nfs/Kconfig-249-\nfs/Kconfig:250:menuconfig HUGETLBFS\nfs/Kconfig-251-\tbool \"HugeTLB file system support\"\n"
}

9/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7919
							output: 20
							thoughts: 12
						

Thoughts:
Here's my take on those thoughts, tailored for an expert audience:

**Assessing Huge Page Configuration**

Okay, so the initial prompt is pretty straightforward. My first instinct, as an expert in this area, is to zero in on `config HUGETLB_PAGE`. That's where the rubber meets the road when it comes to huge page implementation at a kernel level. This is the lynchpin – the master switch if you will – for whether or not huge pages are even enabled and how the system even looks for large memory blocks. It's the starting point for any serious investigation, be it troubleshooting performance issues, verifying a setup, or confirming a build configuration. I'll need to know its state to determine next steps. I'm going to determine what that means for performance.



10/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "config HUGETLB_PAGE"
}

Results:
{
  "Output": "arch/sh/mm/Kconfig=158=choice\n--\narch/sh/mm/Kconfig-163-\narch/sh/mm/Kconfig:164:config HUGETLB_PAGE_SIZE_64K\narch/sh/mm/Kconfig-165-\tbool \"64kB\"\n--\narch/sh/mm/Kconfig-167-\narch/sh/mm/Kconfig:168:config HUGETLB_PAGE_SIZE_256K\narch/sh/mm/Kconfig-169-\tbool \"256kB\"\n--\narch/sh/mm/Kconfig-171-\narch/sh/mm/Kconfig:172:config HUGETLB_PAGE_SIZE_1MB\narch/sh/mm/Kconfig-173-\tbool \"1MB\"\narch/sh/mm/Kconfig-174-\narch/sh/mm/Kconfig:175:config HUGETLB_PAGE_SIZE_4MB\narch/sh/mm/Kconfig-176-\tbool \"4MB\"\n--\narch/sh/mm/Kconfig-178-\narch/sh/mm/Kconfig:179:config HUGETLB_PAGE_SIZE_64MB\narch/sh/mm/Kconfig-180-\tbool \"64MB\"\n--\nfs/Kconfig=262=if HUGETLBFS\nfs/Kconfig:263:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP_DEFAULT_ON\nfs/Kconfig-264-\tbool \"HugeTLB Vmemmap Optimization (HVO) defaults to on\"\n--\nfs/Kconfig=271=endif # HUGETLBFS\nfs/Kconfig-272-\nfs/Kconfig:273:config HUGETLB_PAGE\nfs/Kconfig-274-\tdef_bool HUGETLBFS\n--\nfs/Kconfig-276-\nfs/Kconfig:277:config HUGETLB_PAGE_OPTIMIZE_VMEMMAP\nfs/Kconfig-278-\tdef_bool HUGETLB_PAGE\n--\nmm/Kconfig=706=config ARCH_ENABLE_THP_MIGRATION\n--\nmm/Kconfig-708-\nmm/Kconfig:709:config HUGETLB_PAGE_SIZE_VARIABLE\nmm/Kconfig-710-\tdef_bool n\n"
}

11/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8713
							output: 35
							thoughts: 174
						

Thoughts:
**Analyzing the hugetlbfs Patch**

Okay, so this patch is focused on `HUGETLBFS`, that's the key configuration setting here. My initial thought is to zero in on the function `allocate_file_region_entries`. It looks like this function is called by `region_add` and `region_chg`, which I immediately recognize as critical routines dealing with hugetlbfs reservations. I know these are related to memory allocations and management within the hugetlbfs.

The bug is in `allocate_file_region_entries`, and it seems like they were using `list_splice` instead of `list_splice_init`. That's a red flag! Using `list_splice` without initializing the destination list can leave `allocated_regions` – a local variable, I now remember – in an undefined state. Its pointers, specifically `next` and `prev`, would still be referencing the old elements that were spliced *into* `resv->region_cache`. This could lead to a textbook case of list corruption down the line if `allocated_regions` is re-used. This needs a deeper dive, so let's check out the code of `allocate_file_region_entries` directly.



12/2 2026/07/13 22:38 tool read-file
0m Args:
{
  "File": "mm/hugetlb.c",
  "FirstLine": 656,
  "LineCount": 50
}

Results:
{
  "Contents": " 656:\tstatic int allocate_file_region_entries(struct resv_map *resv,\n 657:\t\t\t\t\t\tint regions_needed)\n 658:\t\t__must_hold(\u0026resv-\u003elock)\n 659:\t{\n 660:\t\tLIST_HEAD(allocated_regions);\n 661:\t\tint to_allocate = 0, i = 0;\n 662:\t\tstruct file_region *trg = NULL, *rg = NULL;\n 663:\t\n 664:\t\tVM_BUG_ON(regions_needed \u003c 0);\n 665:\t\n 666:\t\t/*\n 667:\t\t * Check for sufficient descriptors in the cache to accommodate\n 668:\t\t * the number of in progress add operations plus regions_needed.\n 669:\t\t *\n 670:\t\t * This is a while loop because when we drop the lock, some other call\n 671:\t\t * to region_add or region_del may have consumed some region_entries,\n 672:\t\t * so we keep looping here until we finally have enough entries for\n 673:\t\t * (adds_in_progress + regions_needed).\n 674:\t\t */\n 675:\t\twhile (resv-\u003eregion_cache_count \u003c\n 676:\t\t       (resv-\u003eadds_in_progress + regions_needed)) {\n 677:\t\t\tto_allocate = resv-\u003eadds_in_progress + regions_needed -\n 678:\t\t\t\t      resv-\u003eregion_cache_count;\n 679:\t\n 680:\t\t\t/* At this point, we should have enough entries in the cache\n 681:\t\t\t * for all the existing adds_in_progress. We should only be\n 682:\t\t\t * needing to allocate for regions_needed.\n 683:\t\t\t */\n 684:\t\t\tVM_BUG_ON(resv-\u003eregion_cache_count \u003c resv-\u003eadds_in_progress);\n 685:\t\n 686:\t\t\tspin_unlock(\u0026resv-\u003elock);\n 687:\t\t\tfor (i = 0; i \u003c to_allocate; i++) {\n 688:\t\t\t\ttrg = kmalloc_obj(*trg);\n 689:\t\t\t\tif (!trg)\n 690:\t\t\t\t\tgoto out_of_memory;\n 691:\t\t\t\tlist_add(\u0026trg-\u003elink, \u0026allocated_regions);\n 692:\t\t\t}\n 693:\t\n 694:\t\t\tspin_lock(\u0026resv-\u003elock);\n 695:\t\n 696:\t\t\tlist_splice_init(\u0026allocated_regions, \u0026resv-\u003eregion_cache);\n 697:\t\t\tresv-\u003eregion_cache_count += to_allocate;\n 698:\t\t}\n 699:\t\n 700:\t\treturn 0;\n 701:\t\n 702:\tout_of_memory:\n 703:\t\tlist_for_each_entry_safe(rg, trg, \u0026allocated_regions, link) {\n 704:\t\t\tlist_del(\u0026rg-\u003elink);\n 705:\t\t\tkfree(rg);\n"
}

13/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9980
							output: 30
							thoughts: 227
						

Thoughts:
**Bug Discovery and Fuzzing Target Identification**

Okay, so I'm looking at this code and a potential vulnerability just jumped out at me. I see `allocated_regions` is a local variable, but it's *inside* this `while` loop – that's a red flag! Then I double-check and see `LIST_HEAD(allocated_regions);` is declared *outside* the loop. Aha! The loop reuses `allocated_regions`. If `list_splice` had been used, it would have prevented re-initialization. But without it, on the second iteration, `list_add` is going to be called on a corrupted `allocated_regions` list head. That's a textbook example of a bug, and thankfully, it looks like it was fixed.

Now that I've spotted that, I'm thinking about fuzzing. The `allocate_file_region_entries` function looks like a prime candidate – it's definitely something that could benefit from being tested more rigorously. Let's make that a focus. I should probably also target other related functions. Functions like `region_add`, `region_chg`, and `region_del` would all need to be fuzzed as well. I'll add them to `FocusSymbols`.

Let's do a quick scan to make sure I haven't missed anything related to the `FocusSymbols`. Looks like I got them all; `region_add`, `region_chg`, `region_del`, `allocate_file_region_entries`. Now I'll quickly check if `region_add` and `region_chg` are static functions, just to get a good sense of the scope.



14/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "region_add",
  "PathPrefix": "mm/hugetlb.c"
}

Results:
{
  "Output": "mm/hugetlb.c=656=static int allocate_file_region_entries(struct resv_map *resv,\n--\nmm/hugetlb.c-670-\t * This is a while loop because when we drop the lock, some other call\nmm/hugetlb.c:671:\t * to region_add or region_del may have consumed some region_entries,\nmm/hugetlb.c-672-\t * so we keep looping here until we finally have enough entries for\n--\nmm/hugetlb.c-714- * call to region_chg with the same range, but in some cases the cache will not\nmm/hugetlb.c:715: * have sufficient entries due to races with other code doing region_add or\nmm/hugetlb.c-716- * region_del.  The extra needed entries will be allocated.\n--\nmm/hugetlb.c-722- * this operation and we were not able to allocate, it returns -ENOMEM.\nmm/hugetlb.c:723: * region_add of regions of length 1 never allocate file_regions and cannot\nmm/hugetlb.c:724: * fail; region_chg will always allocate at least 1 entry and a region_add for\nmm/hugetlb.c-725- * 1 page will only require at most 1 entry.\nmm/hugetlb.c-726- */\nmm/hugetlb.c:727:static long region_add(struct resv_map *resv, long f, long t,\nmm/hugetlb.c-728-\t\t       long in_regions_needed, struct hstate *h,\n--\nmm/hugetlb.c-752-\t\t\t    (actual_regions_needed - in_regions_needed)) {\nmm/hugetlb.c:753:\t\t/* region_add operation of range 1 should never need to\nmm/hugetlb.c-754-\t\t * allocate file_region entries.\n--\nmm/hugetlb.c-777- * represented.  This routine is called before a subsequent\nmm/hugetlb.c:778: * call to region_add that will actually modify the reserve\nmm/hugetlb.c-779- * map to add the specified range [f, t).  region_chg does\n--\nmm/hugetlb.c-781- * map.  A number of new file_region structures is added to the cache as a\nmm/hugetlb.c:782: * placeholder, for the subsequent region_add call to use. At least 1\nmm/hugetlb.c-783- * file_region structure is added.\n--\nmm/hugetlb.c-786- * resv-\u003eadds_in_progress.  This value needs to be provided to a follow up call\nmm/hugetlb.c:787: * to region_add or region_abort for proper accounting.\nmm/hugetlb.c-788- *\n--\nmm/hugetlb.c=794=static long region_chg(struct resv_map *resv, long f, long t,\n--\nmm/hugetlb.c-819- * of the resv_map keeps track of the operations in progress between\nmm/hugetlb.c:820: * calls to region_chg and region_add.  Operations are sometimes\nmm/hugetlb.c-821- * aborted after the call to region_chg.  In such cases, region_abort\n--\nmm/hugetlb.c=2430=static long __vma_reservation_common(struct hstate *h,\n--\nmm/hugetlb.c-2453-\tcase VMA_COMMIT_RESV:\nmm/hugetlb.c:2454:\t\tret = region_add(resv, idx, idx + 1, 1, NULL, NULL);\nmm/hugetlb.c:2455:\t\t/* region_add calls of range 1 should never fail. */\nmm/hugetlb.c-2456-\t\tVM_BUG_ON(ret \u003c 0);\n--\nmm/hugetlb.c-2463-\t\tif (vma-\u003evm_flags \u0026 VM_MAYSHARE) {\nmm/hugetlb.c:2464:\t\t\tret = region_add(resv, idx, idx + 1, 1, NULL, NULL);\nmm/hugetlb.c:2465:\t\t\t/* region_add calls of range 1 should never fail. */\nmm/hugetlb.c-2466-\t\t\tVM_BUG_ON(ret \u003c 0);\n--\nmm/hugetlb.c-2476-\t\t} else {\nmm/hugetlb.c:2477:\t\t\tret = region_add(resv, idx, idx + 1, 1, NULL, NULL);\nmm/hugetlb.c:2478:\t\t\t/* region_add calls of range 1 should never fail. */\nmm/hugetlb.c-2479-\t\t\tVM_BUG_ON(ret \u003c 0);\n--\nmm/hugetlb.c=6540=long hugetlb_reserve_pages(struct inode *inode,\n--\nmm/hugetlb.c-6650-\tif (!vma || vma_test(vma, VMA_MAYSHARE_BIT)) {\nmm/hugetlb.c:6651:\t\tadd = region_add(resv_map, from, to, regions_needed, h, h_cg);\nmm/hugetlb.c-6652-\n--\nmm/hugetlb.c-6659-\t\t\t * pages in this range were added to the reserve\nmm/hugetlb.c:6660:\t\t\t * map between region_chg and region_add.  This\nmm/hugetlb.c-6661-\t\t\t * indicates a race with alloc_hugetlb_folio.  Adjust\n--\nmm/hugetlb.c-6716-\t\t/* Only call region_abort if the region_chg succeeded but the\nmm/hugetlb.c:6717:\t\t * region_add failed or didn't run.\nmm/hugetlb.c-6718-\t\t */\n"
}

15/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12146
							output: 29
							thoughts: 283
						

Thoughts:
**Analyzing a Static Bug in `allocate_file_region_entries`**

Okay, so I'm looking at this patch and it's pretty clear: the core issue is in `allocate_file_region_entries`. It's definitely static, which simplifies things a bit. The bug is list corruption. Specifically, the list gets messed up when the `allocate_file_region_entries` function iterates more than once. This happens when the value of `resv->region_cache_count` is less than the sum of `resv->adds_in_progress` and `regions_needed` after releasing the lock, which screams race condition.

The selftest, `hugetlb_region_cache_race`, cleverly uses `memfd_create(MFD_HUGETLB)` and `fallocate(FALLOC_FL_PUNCH_HOLE)` to create fragmentation within the `resv_map`. This fragmentation then triggers the race. So, the feature area is hugetlbfs. Seems like this is a HUGETLBFS specific issue.

The key functions to focus on here are `allocate_file_region_entries` (obviously), `region_add`, `region_chg`, and `region_del` – the standard list manipulation functions in this context. I'll need to meticulously review the locking and access patterns within these functions.

Now, before I dig too deep, let me quickly scan for any related configs. Is there anything else that might contribute? Ah, `CONFIG_MEMFD_CREATE`. No, wait a minute, `memfd_create` is always available if `CONFIG_SHMEM` or `CONFIG_HUGETLBFS` is enabled, which makes sense. I have to remind myself `memfd_create` is a syscall. Hmm, let me check the `HUGETLBFS` config, specifically in `fs/Kconfig`. I'll see if there's any implicit dependency I'm missing.



16/2 2026/07/13 22:38 tool grepper
0m Args:
{
  "Expression": "config HUGETLBFS",
  "PathPrefix": "fs/Kconfig"
}

Results:
{
  "Output": "fs/Kconfig=247=config ARCH_SUPPORTS_HUGETLBFS\n--\nfs/Kconfig-249-\nfs/Kconfig:250:menuconfig HUGETLBFS\nfs/Kconfig-251-\tbool \"HugeTLB file system support\"\n"
}

17/2 2026/07/13 22:38 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 12956
							output: 123
							thoughts: 229
						

Thoughts:
**Analyzing the HUGETLBFS Patch for Fuzzing Potential**

Okay, so looking at this patch, the central component appears to be `HUGETLBFS`. I need to confirm that `HUGETLB_PAGE` isn't separately required; based on the dependency, it's just the `HUGETLBFS` option that's truly essential here. That's a good start.

Now, let's drill down and see the scope of the changes. The primary function modified seems to be `allocate_file_region_entries`. My initial inspection shows modifications confined to `mm/hugetlb.c` and a few selftest areas, nothing that immediately screams "widespread impact" or significant architectural changes. It's a focused alteration, which suggests it’s a functional fix, exactly the sort that warrants fuzzing.

Given this, I'm going to codify this into some fuzzing directives.

**WorthFuzzing: true**. Absolutely, this has clear fuzzing potential.

**FocusSymbols: ["allocate_file_region_entries", "region_add", "region_chg", "region_del"]**. These are the key functions, particularly that core `allocate_file_region_entries` function and the region management functions, which appear to be intimately involved in this bug.

**EnableConfigs: ["HUGETLBFS"]**. That's the crucial configuration element to enable.

**Reasoning: "The patch fixes a list corruption bug in allocate_file_region_entries in hugetlbfs. The bug is triggered by a race condition when the region cache needs to be refilled multiple times. Fuzzing hugetlbfs reservation functions can help ensure the fix is robust."** This summarizes the vulnerability's nature and the rationale for our fuzzing strategy. Essentially, we're looking to validate the fix's robustness against race conditions in the hugetlbfs region cache.



18/2 2026/07/13 22:38 tool set-results
0m Args:
{
  "EnableConfigs": [
    "HUGETLBFS"
  ],
  "FocusSymbols": [
    "allocate_file_region_entries",
    "region_add",
    "region_chg",
    "region_del"
  ],
  "Reasoning": "The patch fixes a list corruption bug in allocate_file_region_entries in hugetlbfs. The bug is triggered by a race condition when the region cache needs to be refilled multiple times. Fuzzing hugetlbfs reservation functions can help ensure the fix is robust.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "HUGETLBFS"
  ],
  "FocusSymbols": [
    "allocate_file_region_entries",
    "region_add",
    "region_chg",
    "region_del"
  ],
  "Reasoning": "The patch fixes a list corruption bug in allocate_file_region_entries in hugetlbfs. The bug is triggered by a race condition when the region cache needs to be refilled multiple times. Fuzzing hugetlbfs reservation functions can help ensure the fix is robust.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)