AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/24 22:06 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch refactors the calculation of kexec handover (KHO) scratch sizes by introducing a static helper memblock_reserved_size_nid() in kernel/liveupdate/kexec_handover.c and removing memblock_reserved_hugetlb_size() from mm/memblock.c. All modified and added functions (memblock_reserved_size_nid, scratch_size_update, scratch_size_node) are annotated with __init / __init_memblock and are executed strictly during early boot via mm_core_init() before userspace starts and before memblock memory is discarded (memblock_free_all()). Because this boot-time code is structurally unreachable at runtime by syzkaller syscall fuzzing, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

1/1 2026/09/24 22:06 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 01f3b31d83aff63b6773254b7db96200d8f2c480\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Sep 24 22:06:08 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memblock.h b/include/linux/memblock.h\nindex d62db9e776cf9..678fe466529a2 100644\n--- a/include/linux/memblock.h\n+++ b/include/linux/memblock.h\n@@ -487,7 +487,6 @@ static inline __init_memblock bool memblock_bottom_up(void)\n phys_addr_t memblock_phys_mem_size(void);\n phys_addr_t memblock_reserved_size(void);\n phys_addr_t memblock_reserved_kern_size(phys_addr_t limit, int nid);\n-phys_addr_t memblock_reserved_hugetlb_size(phys_addr_t limit, int nid);\n unsigned long memblock_estimated_nr_free_pages(void);\n phys_addr_t memblock_start_of_DRAM(void);\n phys_addr_t memblock_end_of_DRAM(void);\ndiff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c\nindex 7c4d86daf86d5..dc809e1e768c0 100644\n--- a/kernel/liveupdate/kexec_handover.c\n+++ b/kernel/liveupdate/kexec_handover.c\n@@ -752,6 +752,31 @@ static int __init kho_parse_scratch_size(char *p)\n }\n early_param(\"kho_scratch\", kho_parse_scratch_size);\n \n+static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,\n+\t\t\t\t\t\t\t      enum memblock_flags region_type)\n+{\n+\tstruct memblock_region *r;\n+\tphys_addr_t total = 0;\n+\n+\tfor_each_reserved_mem_region(r) {\n+\t\tphys_addr_t size = r-\u003esize;\n+\n+\t\tif (r-\u003ebase \u003e limit)\n+\t\t\tbreak;\n+\n+\t\tif (r-\u003ebase + r-\u003esize \u003e limit)\n+\t\t\tsize = limit - r-\u003ebase;\n+\n+#ifdef CONFIG_NUMA\n+\t\tif (nid == memblock_get_region_node(r))\n+#endif\n+\t\t\tif (r-\u003eflags \u0026 region_type)\n+\t\t\t\ttotal += size;\n+\t}\n+\n+\treturn total;\n+}\n+\n static void __init scratch_size_update(void)\n {\n \t/*\n@@ -762,17 +787,17 @@ static void __init scratch_size_update(void)\n \tif (scratch_scale) {\n \t\tphys_addr_t size;\n \n-\t\tsize = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,\n-\t\t\t\t\t\t   NUMA_NO_NODE);\n-\t\tsize -= memblock_reserved_hugetlb_size(ARCH_LOW_ADDRESS_LIMIT,\n-\t\t\t\t\t\t       NUMA_NO_NODE);\n+\t\tsize = memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,\n+\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n+\t\tsize -= memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,\n+\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n \t\tsize = size * scratch_scale / 100;\n \t\tscratch_size_lowmem = size;\n \n-\t\tsize = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,\n-\t\t\t\t\t\t   NUMA_NO_NODE);\n-\t\tsize -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,\n-\t\t\t\t\t\t       NUMA_NO_NODE);\n+\t\tsize = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,\n+\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n+\t\tsize -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,\n+\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n \t\tsize = size * scratch_scale / 100 - scratch_size_lowmem;\n \t\tscratch_size_global = size;\n \t}\n@@ -790,11 +815,11 @@ static phys_addr_t __init scratch_size_node(int nid)\n \tphys_addr_t size;\n \n \tif (scratch_scale) {\n-\t\tsize = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,\n-\t\t\t\t\t\t   nid);\n+\t\tsize = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,\n+\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n \t\t/* Do not count HugeTLB pages. */\n-\t\tsize -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,\n-\t\t\t\t\t\t       nid);\n+\t\tsize -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,\n+\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n \t\tsize = size * scratch_scale / 100;\n \t} else {\n \t\tsize = scratch_size_pernode;\ndiff --git a/mm/memblock.c b/mm/memblock.c\nindex 021db49eb7fc6..9da748e774ea6 100644\n--- a/mm/memblock.c\n+++ b/mm/memblock.c\n@@ -1900,28 +1900,6 @@ phys_addr_t __init_memblock memblock_reserved_size(void)\n \treturn memblock.reserved.total_size;\n }\n \n-phys_addr_t __init_memblock memblock_reserved_hugetlb_size(phys_addr_t limit, int nid)\n-{\n-\tstruct memblock_region *r;\n-\tphys_addr_t total = 0;\n-\n-\tfor_each_reserved_mem_region(r) {\n-\t\tphys_addr_t size = r-\u003esize;\n-\n-\t\tif (r-\u003ebase \u003e limit)\n-\t\t\tbreak;\n-\n-\t\tif (r-\u003ebase + r-\u003esize \u003e limit)\n-\t\t\tsize = limit - r-\u003ebase;\n-\n-\t\tif (nid == memblock_get_region_node(r) || !numa_valid_node(nid))\n-\t\t\tif (r-\u003eflags \u0026 MEMBLOCK_RSRV_HUGETLB)\n-\t\t\t\ttotal += size;\n-\t}\n-\n-\treturn total;\n-}\n-\n phys_addr_t __init_memblock memblock_reserved_kern_size(phys_addr_t limit, int nid)\n {\n \tstruct memblock_region *r;\n"
}

2/1 2026/09/24 22:06 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch refactors the calculation of kexec handover (KHO) scratch sizes by introducing a static helper memblock_reserved_size_nid() in kernel/liveupdate/kexec_handover.c and removing memblock_reserved_hugetlb_size() from mm/memblock.c. All modified and added functions (memblock_reserved_size_nid, scratch_size_update, scratch_size_node) are annotated with __init / __init_memblock and are executed strictly during early boot via mm_core_init() before userspace starts and before memblock memory is discarded (memblock_free_all()). Because this boot-time code is structurally unreachable at runtime by syzkaller syscall fuzzing, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any '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:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 01f3b31d83aff63b6773254b7db96200d8f2c480
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Sep 24 22:06:08 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/memblock.h b/include/linux/memblock.h
index d62db9e776cf9..678fe466529a2 100644
--- a/include/linux/memblock.h
+++ b/include/linux/memblock.h
@@ -487,7 +487,6 @@ static inline __init_memblock bool memblock_bottom_up(void)
 phys_addr_t memblock_phys_mem_size(void);
 phys_addr_t memblock_reserved_size(void);
 phys_addr_t memblock_reserved_kern_size(phys_addr_t limit, int nid);
-phys_addr_t memblock_reserved_hugetlb_size(phys_addr_t limit, int nid);
 unsigned long memblock_estimated_nr_free_pages(void);
 phys_addr_t memblock_start_of_DRAM(void);
 phys_addr_t memblock_end_of_DRAM(void);
diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c
index 7c4d86daf86d5..dc809e1e768c0 100644
--- a/kernel/liveupdate/kexec_handover.c
+++ b/kernel/liveupdate/kexec_handover.c
@@ -752,6 +752,31 @@ static int __init kho_parse_scratch_size(char *p)
 }
 early_param("kho_scratch", kho_parse_scratch_size);
 
+static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,
+							      enum memblock_flags region_type)
+{
+	struct memblock_region *r;
+	phys_addr_t total = 0;
+
+	for_each_reserved_mem_region(r) {
+		phys_addr_t size = r->size;
+
+		if (r->base > limit)
+			break;
+
+		if (r->base + r->size > limit)
+			size = limit - r->base;
+
+#ifdef CONFIG_NUMA
+		if (nid == memblock_get_region_node(r))
+#endif
+			if (r->flags & region_type)
+				total += size;
+	}
+
+	return total;
+}
+
 static void __init scratch_size_update(void)
 {
 	/*
@@ -762,17 +787,17 @@ static void __init scratch_size_update(void)
 	if (scratch_scale) {
 		phys_addr_t size;
 
-		size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,
-						   NUMA_NO_NODE);
-		size -= memblock_reserved_hugetlb_size(ARCH_LOW_ADDRESS_LIMIT,
-						       NUMA_NO_NODE);
+		size = memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,
+						  MEMBLOCK_RSRV_KERN);
+		size -= memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,
+						   MEMBLOCK_RSRV_HUGETLB);
 		size = size * scratch_scale / 100;
 		scratch_size_lowmem = size;
 
-		size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
-						   NUMA_NO_NODE);
-		size -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,
-						       NUMA_NO_NODE);
+		size = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,
+						  MEMBLOCK_RSRV_KERN);
+		size -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,
+						   MEMBLOCK_RSRV_HUGETLB);
 		size = size * scratch_scale / 100 - scratch_size_lowmem;
 		scratch_size_global = size;
 	}
@@ -790,11 +815,11 @@ static phys_addr_t __init scratch_size_node(int nid)
 	phys_addr_t size;
 
 	if (scratch_scale) {
-		size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
-						   nid);
+		size = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,
+						  MEMBLOCK_RSRV_KERN);
 		/* Do not count HugeTLB pages. */
-		size -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,
-						       nid);
+		size -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,
+						   MEMBLOCK_RSRV_HUGETLB);
 		size = size * scratch_scale / 100;
 	} else {
 		size = scratch_size_pernode;
diff --git a/mm/memblock.c b/mm/memblock.c
index 021db49eb7fc6..9da748e774ea6 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -1900,28 +1900,6 @@ phys_addr_t __init_memblock memblock_reserved_size(void)
 	return memblock.reserved.total_size;
 }
 
-phys_addr_t __init_memblock memblock_reserved_hugetlb_size(phys_addr_t limit, int nid)
-{
-	struct memblock_region *r;
-	phys_addr_t total = 0;
-
-	for_each_reserved_mem_region(r) {
-		phys_addr_t size = r->size;
-
-		if (r->base > limit)
-			break;
-
-		if (r->base + r->size > limit)
-			size = limit - r->base;
-
-		if (nid == memblock_get_region_node(r) || !numa_valid_node(nid))
-			if (r->flags & MEMBLOCK_RSRV_HUGETLB)
-				total += size;
-	}
-
-	return total;
-}
-
 phys_addr_t __init_memblock memblock_reserved_kern_size(phys_addr_t limit, int nid)
 {
 	struct memblock_region *r;

3/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 4342
							output: 40
							thoughts: 434
						

Thoughts:
**Analyzing a Recent Memory Management Patch**

Okay, let's dive into this patch. My initial thought is to break down the changes file by file. I see modifications in `include/linux/memblock.h`, `kernel/liveupdate/kexec_handover.c`, and `mm/memblock.c`.

First, it seems we've removed a declaration and function: `phys_addr_t memblock_reserved_hugetlb_size(phys_addr_t limit, int nid)` from `include/linux/memblock.h` and `mm/memblock.c`, respectively. Hmm, that suggests a refactoring or removal of the functionality it provided.

Now, the interesting bit: the changes in `kernel/liveupdate/kexec_handover.c`. It looks like we've introduced `static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid, enum memblock_flags region_type)`. And it appears to be replacing calls to `memblock_reserved_kern_size` and the now-removed `memblock_reserved_hugetlb_size` within the `scratch_size_update` and `scratch_size_node` functions. This screams optimization or a more generalized approach to memory reservation during live updates.

Hold on, I need to check the broader context of `kernel/liveupdate/kexec_handover.c`. I see functions like `kho_parse_scratch_size`, which is an `early_param`, along with `scratch_size_update` and `scratch_size_node`. Are these all `__init` functions? The presence of `early_param` suggests they're involved in early boot or initialization. I need to confirm this quickly. I'll take a look at the file around line 750 (where the new functions were added) with `read-file` or `grepper` to verify this suspicion. This will reveal the complete picture of this memory reservation change and its impact.



4/2 2026/09/24 22:06 tool read-file
0m Args:
{
  "File": "kernel/liveupdate/kexec_handover.c",
  "FirstLine": 750,
  "LineCount": 80
}

Results:
{
  "Contents": " 750:\t\n 751:\t\treturn 0;\n 752:\t}\n 753:\tearly_param(\"kho_scratch\", kho_parse_scratch_size);\n 754:\t\n 755:\tstatic phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,\n 756:\t\t\t\t\t\t\t\t      enum memblock_flags region_type)\n 757:\t{\n 758:\t\tstruct memblock_region *r;\n 759:\t\tphys_addr_t total = 0;\n 760:\t\n 761:\t\tfor_each_reserved_mem_region(r) {\n 762:\t\t\tphys_addr_t size = r-\u003esize;\n 763:\t\n 764:\t\t\tif (r-\u003ebase \u003e limit)\n 765:\t\t\t\tbreak;\n 766:\t\n 767:\t\t\tif (r-\u003ebase + r-\u003esize \u003e limit)\n 768:\t\t\t\tsize = limit - r-\u003ebase;\n 769:\t\n 770:\t#ifdef CONFIG_NUMA\n 771:\t\t\tif (nid == memblock_get_region_node(r))\n 772:\t#endif\n 773:\t\t\t\tif (r-\u003eflags \u0026 region_type)\n 774:\t\t\t\t\ttotal += size;\n 775:\t\t}\n 776:\t\n 777:\t\treturn total;\n 778:\t}\n 779:\t\n 780:\tstatic void __init scratch_size_update(void)\n 781:\t{\n 782:\t\t/*\n 783:\t\t * If fixed sizes are not provided via command line, calculate them now.\n 784:\t\t * Remove HugeTLB allocations from it because they never get allocated\n 785:\t\t * from scratch.\n 786:\t\t */\n 787:\t\tif (scratch_scale) {\n 788:\t\t\tphys_addr_t size;\n 789:\t\n 790:\t\t\tsize = memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,\n 791:\t\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n 792:\t\t\tsize -= memblock_reserved_size_nid(ARCH_LOW_ADDRESS_LIMIT, NUMA_NO_NODE,\n 793:\t\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n 794:\t\t\tsize = size * scratch_scale / 100;\n 795:\t\t\tscratch_size_lowmem = size;\n 796:\t\n 797:\t\t\tsize = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,\n 798:\t\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n 799:\t\t\tsize -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE,\n 800:\t\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n 801:\t\t\tsize = size * scratch_scale / 100 - scratch_size_lowmem;\n 802:\t\t\tscratch_size_global = size;\n 803:\t\t}\n 804:\t\n 805:\t\t/*\n 806:\t\t * Scratch areas are released as MIGRATE_CMA. Round them up to the right\n 807:\t\t * size.\n 808:\t\t */\n 809:\t\tscratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES);\n 810:\t\tscratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES);\n 811:\t}\n 812:\t\n 813:\tstatic phys_addr_t __init scratch_size_node(int nid)\n 814:\t{\n 815:\t\tphys_addr_t size;\n 816:\t\n 817:\t\tif (scratch_scale) {\n 818:\t\t\tsize = memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,\n 819:\t\t\t\t\t\t\t  MEMBLOCK_RSRV_KERN);\n 820:\t\t\t/* Do not count HugeTLB pages. */\n 821:\t\t\tsize -= memblock_reserved_size_nid(MEMBLOCK_ALLOC_ANYWHERE, nid,\n 822:\t\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\n 823:\t\t\tsize = size * scratch_scale / 100;\n 824:\t\t} else {\n 825:\t\t\tsize = scratch_size_pernode;\n 826:\t\t}\n 827:\t\n 828:\t\treturn round_up(size, SCRATCH_ALIGNMENT_BYTES);\n 829:\t}\n"
}

5/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 6489
							output: 28
							thoughts: 37
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Investigating Scratch Size Updates**

Alright, time to dive into this `scratch_size` business. I need to understand how the allocation size for the scratch space is being managed. My immediate instinct is to trace the execution paths that influence this value. Let's start by identifying all the points in the code where the functions `scratch_size_update` and `scratch_size_node` are actually *called*. I'll need to know which parts of the program are reading, writing, and possibly even manipulating this memory configuration.

The most efficient way to achieve that is to leverage the grepper tool - it's a quick and reliable way to locate the call sites. Searching for the callers of both functions should give me a complete view of all the relevant locations. This is the first step toward a more in-depth understanding of the implications of the scratch size configuration.



6/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "scratch_size_",
  "PathPrefix": "kernel/liveupdate"
}

Results:
{
  "Output": "kernel/liveupdate/kexec_handover.c=682=static unsigned int scratch_scale __initdata = 200;\nkernel/liveupdate/kexec_handover.c:683:static phys_addr_t scratch_size_global __initdata;\nkernel/liveupdate/kexec_handover.c:684:static phys_addr_t scratch_size_pernode __initdata;\nkernel/liveupdate/kexec_handover.c:685:static phys_addr_t scratch_size_lowmem __initdata;\nkernel/liveupdate/kexec_handover.c-686-\nkernel/liveupdate/kexec_handover.c=687=static int __init kho_parse_scratch_size(char *p)\n--\nkernel/liveupdate/kexec_handover.c-740-\nkernel/liveupdate/kexec_handover.c:741:\tscratch_size_lowmem = sizes[0];\nkernel/liveupdate/kexec_handover.c:742:\tscratch_size_global = sizes[1];\nkernel/liveupdate/kexec_handover.c:743:\tscratch_size_pernode = sizes[2];\nkernel/liveupdate/kexec_handover.c-744-\tscratch_scale = 0;\n--\nkernel/liveupdate/kexec_handover.c-746-\tpr_notice(\"scratch areas: lowmem: %lluMiB global: %lluMiB pernode: %lldMiB\\n\",\nkernel/liveupdate/kexec_handover.c:747:\t\t  (u64)(scratch_size_lowmem \u003e\u003e 20),\nkernel/liveupdate/kexec_handover.c:748:\t\t  (u64)(scratch_size_global \u003e\u003e 20),\nkernel/liveupdate/kexec_handover.c:749:\t\t  (u64)(scratch_size_pernode \u003e\u003e 20));\nkernel/liveupdate/kexec_handover.c-750-\n--\nkernel/liveupdate/kexec_handover.c=755=static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,\n--\nkernel/liveupdate/kexec_handover.c-779-\nkernel/liveupdate/kexec_handover.c:780:static void __init scratch_size_update(void)\nkernel/liveupdate/kexec_handover.c-781-{\n--\nkernel/liveupdate/kexec_handover.c-794-\t\tsize = size * scratch_scale / 100;\nkernel/liveupdate/kexec_handover.c:795:\t\tscratch_size_lowmem = size;\nkernel/liveupdate/kexec_handover.c-796-\n--\nkernel/liveupdate/kexec_handover.c-800-\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\nkernel/liveupdate/kexec_handover.c:801:\t\tsize = size * scratch_scale / 100 - scratch_size_lowmem;\nkernel/liveupdate/kexec_handover.c:802:\t\tscratch_size_global = size;\nkernel/liveupdate/kexec_handover.c-803-\t}\n--\nkernel/liveupdate/kexec_handover.c-808-\t */\nkernel/liveupdate/kexec_handover.c:809:\tscratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES);\nkernel/liveupdate/kexec_handover.c:810:\tscratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES);\nkernel/liveupdate/kexec_handover.c-811-}\nkernel/liveupdate/kexec_handover.c-812-\nkernel/liveupdate/kexec_handover.c:813:static phys_addr_t __init scratch_size_node(int nid)\nkernel/liveupdate/kexec_handover.c-814-{\n--\nkernel/liveupdate/kexec_handover.c-824-\t} else {\nkernel/liveupdate/kexec_handover.c:825:\t\tsize = scratch_size_pernode;\nkernel/liveupdate/kexec_handover.c-826-\t}\n--\nkernel/liveupdate/kexec_handover.c=856=static void __init kho_reserve_scratch(void)\n--\nkernel/liveupdate/kexec_handover.c-863-\nkernel/liveupdate/kexec_handover.c:864:\tscratch_size_update();\nkernel/liveupdate/kexec_handover.c-865-\n--\nkernel/liveupdate/kexec_handover.c-878-\t */\nkernel/liveupdate/kexec_handover.c:879:\tsize = scratch_size_lowmem;\nkernel/liveupdate/kexec_handover.c-880-\taddr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0,\n--\nkernel/liveupdate/kexec_handover.c-891-\t/* reserve large contiguous area for allocations without nid */\nkernel/liveupdate/kexec_handover.c:892:\tsize = scratch_size_global;\nkernel/liveupdate/kexec_handover.c-893-\taddr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES);\n--\nkernel/liveupdate/kexec_handover.c-907-\tfor_each_node_state(nid, N_MEMORY) {\nkernel/liveupdate/kexec_handover.c:908:\t\tsize = scratch_size_node(nid);\nkernel/liveupdate/kexec_handover.c-909-\t\taddr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES,\n"
}

7/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 8245
							output: 19
							thoughts: 15
						

Thoughts:
Okay, here's my summary, tailored for an expert audience, as if I'm thinking these things myself:

**Tracing the Usage of `kho_reserve_scratch`**

Alright, time to get down to brass tacks. I need to understand the execution flow that involves `kho_reserve_scratch`. The immediate goal is to map out where this function is actually being invoked. I'm going to start by performing a full search across the codebase. I need to identify *every* single instance of its call. From there, I'll be able to work through the various call paths to determine the context of each call to `kho_reserve_scratch`.



8/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "kho_reserve_scratch"
}

Results:
{
  "Output": "kernel/liveupdate/kexec_handover.c=831=bool kho_scratch_overlap(phys_addr_t phys, size_t size)\n--\nkernel/liveupdate/kexec_handover.c-847-/**\nkernel/liveupdate/kexec_handover.c:848: * kho_reserve_scratch - Reserve a contiguous chunk of memory for kexec\nkernel/liveupdate/kexec_handover.c-849- *\n--\nkernel/liveupdate/kexec_handover.c-855- */\nkernel/liveupdate/kexec_handover.c:856:static void __init kho_reserve_scratch(void)\nkernel/liveupdate/kexec_handover.c-857-{\n--\nkernel/liveupdate/kexec_handover.c=1965=void __init kho_memory_init(void)\n--\nkernel/liveupdate/kexec_handover.c-1969-\telse\nkernel/liveupdate/kexec_handover.c:1970:\t\tkho_reserve_scratch();\nkernel/liveupdate/kexec_handover.c-1971-}\n"
}

9/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 8711
							output: 19
							thoughts: 15
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:

**Investigating the Initialization of the `kho_memory` Subsystem**

Alright, let's dive into this. My initial thought process revolves around understanding the lifecycle of the `kho_memory` subsystem. The immediate question burning in my mind is: where does it begin? I need to understand the point of entry, and therefore the primary focus is to discover all the call sites of `kho_memory_init`. Pinpointing these calls will illuminate how this memory management subsystem is integrated and, crucially, when it's brought online. This should give me the crucial context I need to properly analyze its functionality and potential issues.



10/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "kho_memory_init"
}

Results:
{
  "Output": "include/linux/kexec_handover.h=34=int kho_retrieve_subtree(const char *name, phys_addr_t *phys, size_t *size);\ninclude/linux/kexec_handover.h-35-\ninclude/linux/kexec_handover.h:36:void kho_memory_init(void);\ninclude/linux/kexec_handover.h:37:void kho_memory_init_early(void);\ninclude/linux/kexec_handover.h-38-\n--\ninclude/linux/kexec_handover.h=115=static inline int kho_retrieve_subtree(const char *name, phys_addr_t *phys,\n--\ninclude/linux/kexec_handover.h-120-\ninclude/linux/kexec_handover.h:121:static inline void kho_memory_init(void) { }\ninclude/linux/kexec_handover.h-122-\ninclude/linux/kexec_handover.h:123:static inline void kho_memory_init_early(void) { }\ninclude/linux/kexec_handover.h-124-\n--\nkernel/liveupdate/kexec_handover.c=1923=fs_initcall(kho_init);\nkernel/liveupdate/kexec_handover.c-1924-\nkernel/liveupdate/kexec_handover.c:1925:void __init kho_memory_init_early(void)\nkernel/liveupdate/kexec_handover.c-1926-{\n--\nkernel/liveupdate/kexec_handover.c-1964-\nkernel/liveupdate/kexec_handover.c:1965:void __init kho_memory_init(void)\nkernel/liveupdate/kexec_handover.c-1966-{\n--\nmm/mm_init.c=2675=void __init mm_core_init_early(void)\nmm/mm_init.c-2676-{\nmm/mm_init.c:2677:\tkho_memory_init_early();\nmm/mm_init.c-2678-\n--\nmm/mm_init.c=2692=void __init mm_core_init(void)\n--\nmm/mm_init.c-2716-\t */\nmm/mm_init.c:2717:\tkho_memory_init();\nmm/mm_init.c-2718-\n"
}

11/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 9522
							output: 24
							thoughts: 230
						

Thoughts:
**Analyzing Memory Block Initialization During Early Boot**

Okay, so `kho_memory_init` is called from `mm_core_init`, which means this all happens super early, like, right at the beginning of boot. I need to understand the implications of this. I'm wondering if calls like `memblock_reserved_size_nid`, `kho_reserve_scratch`, `scratch_size_update`, or `scratch_size_node` are *also* only happening during this early phase. I need to be sure.

Right, time to get a more complete picture. I'm going to look at *every* caller and every reference to functions like `memblock_reserved_size_nid`, and `memblock_reserved_hugetlb_size`. I also need to go back in time and see if `memblock_reserved_hugetlb_size` *had* other callers before this patch. Historical context is important here.

Aha, I see it! In the patch, `mm/memblock.c`: `memblock_reserved_hugetlb_size` has been completely removed. Okay, let's see why, maybe I can find the commit message and what was removed. I'll need to use `git log -p -1` or something similar.

Okay, I'm going to leverage `grepper` here to do a quick sweep of the code base and make sure there aren't any sneaky other callers or references to `memblock_reserved_hugetlb_size` that I'm missing. Got to be thorough.



12/2 2026/09/24 22:06 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "memblock_reserved_hugetlb_size"
}

Results:
{
  "error": "no matches"
}

13/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 10127
							output: 22
							thoughts: 59
						

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

**Investigating Memory Reservation Usage**

Alright, no dice on finding other uses for `memblock_reserved_hugetlb_size` - it seems the kexec handover code in `kernel/liveupdate/kexec_handover.c` is the sole consumer. That's a bit surprising, but not entirely unexpected. Now, the next logical step is to investigate `memblock_reserved_kern_size`. I need to understand its usage patterns within the kernel. Where is it being consulted or modified? Are there any other critical code paths that depend on its value? Time to trace it down and map out its uses.



14/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "memblock_reserved_kern_size"
}

Results:
{
  "Output": "include/linux/memblock.h=488=phys_addr_t memblock_reserved_size(void);\ninclude/linux/memblock.h:489:phys_addr_t memblock_reserved_kern_size(phys_addr_t limit, int nid);\ninclude/linux/memblock.h-490-unsigned long memblock_estimated_nr_free_pages(void);\n--\nmm/memblock.c=1898=phys_addr_t __init_memblock memblock_reserved_size(void)\n--\nmm/memblock.c-1902-\nmm/memblock.c:1903:phys_addr_t __init_memblock memblock_reserved_kern_size(phys_addr_t limit, int nid)\nmm/memblock.c-1904-{\n--\nmm/memblock.c=1937=unsigned long __init memblock_estimated_nr_free_pages(void)\n--\nmm/memblock.c-1939-\treturn PHYS_PFN(memblock_phys_mem_size() -\nmm/memblock.c:1940:\t\t\tmemblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE, NUMA_NO_NODE));\nmm/memblock.c-1941-}\n"
}

15/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 10684
							output: 21
							thoughts: 404
						

Thoughts:
**Analyzing this Patch: A Deep Dive into Memory Management**

Okay, let's dissect this patch. My immediate question is, what's its overall purpose? Right off the bat, I see the changes touch `kernel/liveupdate/kexec_handover.c` and `mm/memblock.c`. Let's zero in on those files.

In `mm/memblock.c`, I notice `memblock_reserved_hugetlb_size` has been removed. Interesting... Meanwhile, `kernel/liveupdate/kexec_handover.c` introduces `memblock_reserved_size_nid`. Let's take a look at the code:

```c
static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,
							      enum memblock_flags region_type)
{
	struct memblock_region *r;
	phys_addr_t total = 0;

	for_each_reserved_mem_region(r) {
		phys_addr_t size = r->size;

		if (r->base > limit)
			break;

		if (r->base + r->size > limit)
			size = limit - r->base;

#ifdef CONFIG_NUMA
		if (nid == memblock_get_region_node(r))
#endif
			if (r->flags & region_type)
				total += size;
	}

	return total;
}
```

Okay, `memblock_reserved_size_nid` is `static` and marked with `__init_memblock`. That's a red flag! Then, I see it's called in `scratch_size_update` (also `__init`) and `scratch_size_node` (similarly `__init`). Now, this raises a serious concern: is anything in this patch actually *reachable* after the initial boot sequence? It seems like it's all tied to initialization, but I need to be sure.

I'm getting a bad feeling here. Let's make absolutely sure about the definition of `__init_memblock`. What's the context for its use? Time to check that macro definition. We need to be sure there isn't some clever trick here.



16/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "#define __init_memblock"
}

Results:
{
  "Output": "include/linux/memblock.h=114=extern struct memblock memblock;\n--\ninclude/linux/memblock.h-116-#ifndef CONFIG_ARCH_KEEP_MEMBLOCK\ninclude/linux/memblock.h:117:#define __init_memblock __meminit\ninclude/linux/memblock.h-118-#define __initdata_memblock __meminitdata\ninclude/linux/memblock.h=119=void memblock_discard(void);\ninclude/linux/memblock.h-120-#else\ninclude/linux/memblock.h:121:#define __init_memblock\ninclude/linux/memblock.h-122-#define __initdata_memblock\n"
}

17/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 11799
							output: 36
							thoughts: 273
						

Thoughts:
**My Analysis of Potential Memory Block Vulnerabilities**

Okay, so I'm looking at how the kernel initializes and manages its memory blocks. The first thing that jumps out is that `__init_memblock` is basically the same as `__meminit`, unless we have `CONFIG_ARCH_KEEP_MEMBLOCK` defined. On amd64, that memblock is eventually freed post-boot, usually through `memblock_free_all` or `memblock_discard`. That's standard behavior.

But here's where my expert instincts kick in: even if it *is* an `__init_memblock` or `__init` region, can we *ever* trigger functions like `scratch_size_update`, `scratch_size_node`, or `memblock_reserved_size_nid` from user space? Could a syscall, or even something clever from syzkaller, potentially manipulate these functions? This is where vulnerabilities often hide.

Time to trace the call paths. I need to understand who calls these potentially sensitive functions. I've already done the basic `grep` search for callers of `scratch_size_` functions. The results show `kho_reserve_scratch` calling both `scratch_size_update` and `scratch_size_node`. That's a good starting point.

Now, who calls `kho_reserve_scratch`? Ah, only `kho_memory_init()`! That narrows it down considerably. Let's see where *that* function is called.

Okay, let's open `mm/mm_init.c` and check out the `mm_core_init()` function, line 2717. I'm going to carefully review that function now to understand the context in which `kho_memory_init()` is invoked, and therefore, how these memory block operations are set up.



18/2 2026/09/24 22:06 tool read-file
0m Args:
{
  "File": "mm/mm_init.c",
  "FirstLine": 2690,
  "LineCount": 40
}

Results:
{
  "Contents": "2690:\t * Set up kernel memory allocators\n2691:\t */\n2692:\tvoid __init mm_core_init(void)\n2693:\t{\n2694:\t\tarch_mm_preinit();\n2695:\t\tinit_zero_page_pfn();\n2696:\t\n2697:\t\t/* Initializations relying on SMP setup */\n2698:\t\tBUILD_BUG_ON(MAX_ZONELISTS \u003e 2);\n2699:\t\tbuild_all_zonelists(NULL);\n2700:\t\tpage_alloc_init_cpuhp();\n2701:\t\talloc_tag_sec_init();\n2702:\t\t/*\n2703:\t\t * page_ext requires contiguous pages,\n2704:\t\t * bigger than MAX_PAGE_ORDER unless SPARSEMEM.\n2705:\t\t */\n2706:\t\tpage_ext_init_flatmem();\n2707:\t\tmem_debugging_and_hardening_init();\n2708:\t\tkfence_alloc_pool_and_metadata();\n2709:\t\treport_meminit();\n2710:\t\tkmsan_init_shadow();\n2711:\t\tstack_depot_early_init();\n2712:\t\n2713:\t\t/*\n2714:\t\t * KHO memory setup must happen while memblock is still active, but\n2715:\t\t * as close as possible to buddy initialization\n2716:\t\t */\n2717:\t\tkho_memory_init();\n2718:\t\n2719:\t\tmemblock_free_all();\n2720:\t\tmem_init();\n2721:\t\tkmem_cache_init();\n2722:\t\t/*\n2723:\t\t * page_owner must be initialized after buddy is ready, and also after\n2724:\t\t * slab is ready so that stack_depot_init() works properly\n2725:\t\t */\n2726:\t\tpage_ext_init_flatmem_late();\n2727:\t\tkmemleak_init();\n2728:\t\tptlock_cache_init();\n2729:\t\tpgtable_cache_init();\n"
}

19/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13070
							output: 27
							thoughts: 967
						

Thoughts:
**Analyzing Kernel Memory Initialization and Live Update for Potential Fuzzing Targets**

Okay, so I'm looking at this code and immediately my attention is drawn to lines 2717-2719, where `kho_memory_init()`, `memblock_free_all()`, and `mem_init()` are called. My initial thought is that `kho_memory_init()` is called only once during early kernel initialization, specifically within `mm_core_init()`, before the buddy allocator takes over and before userspace even starts. This is important to understand the overall memory layout.

The core question now: is there any other place where `memblock_reserved_size_nid`, `scratch_size_update`, or `scratch_size_node` are called? I check, and those are all static inside `kernel/liveupdate/kexec_handover.c`. And `kho_reserve_scratch` is also `static` in that same file, and `kho_memory_init` is called *only* from `mm_core_init` in `mm/mm_init.c`. This restricts the scope of their usage.

Now, my mind shifts to the fuzzing context. Can `scratch_size_update`, `scratch_size_node`, or `memblock_reserved_size_nid` be reached by syzkaller at runtime? Remember, syzkaller is a runtime fuzzer, operating after the kernel has booted into userspace. By the time syzkaller kicks in: `mm_core_init()` is done, `kho_memory_init()` is done, `memblock_free_all()` has already freed the `memblock` structures, and `__init` functions and data have already been discarded (through `free_initmem()`). This raises concerns about the reachability of those functions.

My focus goes back to `memblock_reserved_hugetlb_size`, which was deleted. It was used by `scratch_size_update` and `scratch_size_node`. I need to ensure there are no other callers or changes in this patch that are of concern. I carefully review the diff, especially changes in `include/linux/memblock.h` and `kernel/liveupdate/kexec_handover.c`.

With the diff in mind, I need to zoom out a bit. What's the deal with `liveupdate` and `kexec` handover in general? Does kexec handover have any runtime operations that might touch this scratch size or anything calculated here? I need to search `kernel/liveupdate/kexec_handover.c` for any occurrences of `scratch_size_` or just `scratch`. That's where I need to focus my attention to determine if this might be a vulnerability.



20/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "scratch_size",
  "PathPrefix": "kernel/liveupdate"
}

Results:
{
  "Output": "kernel/liveupdate/kexec_handover.c=682=static unsigned int scratch_scale __initdata = 200;\nkernel/liveupdate/kexec_handover.c:683:static phys_addr_t scratch_size_global __initdata;\nkernel/liveupdate/kexec_handover.c:684:static phys_addr_t scratch_size_pernode __initdata;\nkernel/liveupdate/kexec_handover.c:685:static phys_addr_t scratch_size_lowmem __initdata;\nkernel/liveupdate/kexec_handover.c-686-\nkernel/liveupdate/kexec_handover.c:687:static int __init kho_parse_scratch_size(char *p)\nkernel/liveupdate/kexec_handover.c-688-{\n--\nkernel/liveupdate/kexec_handover.c-740-\nkernel/liveupdate/kexec_handover.c:741:\tscratch_size_lowmem = sizes[0];\nkernel/liveupdate/kexec_handover.c:742:\tscratch_size_global = sizes[1];\nkernel/liveupdate/kexec_handover.c:743:\tscratch_size_pernode = sizes[2];\nkernel/liveupdate/kexec_handover.c-744-\tscratch_scale = 0;\n--\nkernel/liveupdate/kexec_handover.c-746-\tpr_notice(\"scratch areas: lowmem: %lluMiB global: %lluMiB pernode: %lldMiB\\n\",\nkernel/liveupdate/kexec_handover.c:747:\t\t  (u64)(scratch_size_lowmem \u003e\u003e 20),\nkernel/liveupdate/kexec_handover.c:748:\t\t  (u64)(scratch_size_global \u003e\u003e 20),\nkernel/liveupdate/kexec_handover.c:749:\t\t  (u64)(scratch_size_pernode \u003e\u003e 20));\nkernel/liveupdate/kexec_handover.c-750-\n--\nkernel/liveupdate/kexec_handover.c-752-}\nkernel/liveupdate/kexec_handover.c:753:early_param(\"kho_scratch\", kho_parse_scratch_size);\nkernel/liveupdate/kexec_handover.c-754-\nkernel/liveupdate/kexec_handover.c=755=static phys_addr_t __init_memblock memblock_reserved_size_nid(phys_addr_t limit, int nid,\n--\nkernel/liveupdate/kexec_handover.c-779-\nkernel/liveupdate/kexec_handover.c:780:static void __init scratch_size_update(void)\nkernel/liveupdate/kexec_handover.c-781-{\n--\nkernel/liveupdate/kexec_handover.c-794-\t\tsize = size * scratch_scale / 100;\nkernel/liveupdate/kexec_handover.c:795:\t\tscratch_size_lowmem = size;\nkernel/liveupdate/kexec_handover.c-796-\n--\nkernel/liveupdate/kexec_handover.c-800-\t\t\t\t\t\t   MEMBLOCK_RSRV_HUGETLB);\nkernel/liveupdate/kexec_handover.c:801:\t\tsize = size * scratch_scale / 100 - scratch_size_lowmem;\nkernel/liveupdate/kexec_handover.c:802:\t\tscratch_size_global = size;\nkernel/liveupdate/kexec_handover.c-803-\t}\n--\nkernel/liveupdate/kexec_handover.c-808-\t */\nkernel/liveupdate/kexec_handover.c:809:\tscratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES);\nkernel/liveupdate/kexec_handover.c:810:\tscratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES);\nkernel/liveupdate/kexec_handover.c-811-}\nkernel/liveupdate/kexec_handover.c-812-\nkernel/liveupdate/kexec_handover.c:813:static phys_addr_t __init scratch_size_node(int nid)\nkernel/liveupdate/kexec_handover.c-814-{\n--\nkernel/liveupdate/kexec_handover.c-824-\t} else {\nkernel/liveupdate/kexec_handover.c:825:\t\tsize = scratch_size_pernode;\nkernel/liveupdate/kexec_handover.c-826-\t}\n--\nkernel/liveupdate/kexec_handover.c=856=static void __init kho_reserve_scratch(void)\n--\nkernel/liveupdate/kexec_handover.c-863-\nkernel/liveupdate/kexec_handover.c:864:\tscratch_size_update();\nkernel/liveupdate/kexec_handover.c-865-\n--\nkernel/liveupdate/kexec_handover.c-878-\t */\nkernel/liveupdate/kexec_handover.c:879:\tsize = scratch_size_lowmem;\nkernel/liveupdate/kexec_handover.c-880-\taddr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0,\n--\nkernel/liveupdate/kexec_handover.c-891-\t/* reserve large contiguous area for allocations without nid */\nkernel/liveupdate/kexec_handover.c:892:\tsize = scratch_size_global;\nkernel/liveupdate/kexec_handover.c-893-\taddr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES);\n--\nkernel/liveupdate/kexec_handover.c-907-\tfor_each_node_state(nid, N_MEMORY) {\nkernel/liveupdate/kexec_handover.c:908:\t\tsize = scratch_size_node(nid);\nkernel/liveupdate/kexec_handover.c-909-\t\taddr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES,\n--\nkernel/liveupdate/kexec_handover.c=2061=int kho_fill_kimage(struct kimage *image)\nkernel/liveupdate/kexec_handover.c-2062-{\nkernel/liveupdate/kexec_handover.c:2063:\tssize_t scratch_size;\nkernel/liveupdate/kexec_handover.c-2064-\tint err = 0;\n--\nkernel/liveupdate/kexec_handover.c-2071-\nkernel/liveupdate/kexec_handover.c:2072:\tscratch_size = sizeof(*kho_scratch) * kho_scratch_cnt;\nkernel/liveupdate/kexec_handover.c-2073-\tscratch = (struct kexec_buf){\n--\nkernel/liveupdate/kexec_handover.c-2075-\t\t.buffer = kho_scratch,\nkernel/liveupdate/kexec_handover.c:2076:\t\t.bufsz = scratch_size,\nkernel/liveupdate/kexec_handover.c-2077-\t\t.mem = KEXEC_BUF_MEM_UNKNOWN,\nkernel/liveupdate/kexec_handover.c:2078:\t\t.memsz = scratch_size,\nkernel/liveupdate/kexec_handover.c-2079-\t\t.buf_align = SZ_64K, /* Makes it easier to map */\n"
}

21/2 2026/09/24 22:06 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16585
							output: 20
							thoughts: 272
						

Thoughts:
**My Initial Assessment of Memory Initialization**

Okay, so I'm looking at these memory initialization variables and functions, and it's pretty clear where they fit in. I see that `scratch_size_global`, `scratch_size_pernode`, and `scratch_size_lowmem` are all declared with `__initdata`. That makes sense; they're probably initialized during early boot and then become read-only. `scratch_size_update` and `scratch_size_node` are `__init`, so they are for initial setup. And `memblock_reserved_size_nid` is `__init_memblock`, indicating it's related to the memblock allocator and is initialized very early.

All of these are called within `kho_reserve_scratch`, which is itself `__init`. This function is then called by `kho_memory_init`, also `__init`, so it all happens during early kernel initialization. And `kho_memory_init` is called exclusively in `mm_core_init`, which is, predictably, marked `__init`. This confirms it's all part of the initial memory layout and setup during kernel boot. I'm seeing a clear pattern here: these are all initialization-related things.

Wait, what was that deleted code I saw?

That `memblock_reserved_hugetlb_size` declaration, which was in `mm/memblock.c` and declared `__init_memblock`, and the declaration was even removed from `include/linux/memblock.h`. That's important. It might tell me the context.

I need to make sure `memblock` is not used at runtime. So that's the key question. Can any of these `memblock` functions, or anything modified here, be called after boot? It looks like I need to see if the `memblock` functions are used at runtime in AMD64. To do that, I'll need to check if amd64 has `CONFIG_ARCH_KEEP_MEMBLOCK`, and what its state is.



22/2 2026/09/24 22:06 tool grepper
0m Args:
{
  "Expression": "ARCH_KEEP_MEMBLOCK"
}

Results:
{
  "Output": "arch/arm/Kconfig=2=config ARM\n--\narch/arm/Kconfig-35-\tselect ARCH_HAS_GCOV_PROFILE_ALL\narch/arm/Kconfig:36:\tselect ARCH_KEEP_MEMBLOCK\narch/arm/Kconfig-37-\tselect ARCH_HAS_UBSAN\n--\narch/arm64/Kconfig=2=config ARM64\n--\narch/arm64/Kconfig-63-\tselect ARCH_HAVE_TRACE_MMIO_ACCESS\narch/arm64/Kconfig:64:\tselect ARCH_KEEP_MEMBLOCK\narch/arm64/Kconfig-65-\tselect ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE\n--\narch/loongarch/Kconfig=2=config LOONGARCH\n--\narch/loongarch/Kconfig-58-\tselect ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION\narch/loongarch/Kconfig:59:\tselect ARCH_KEEP_MEMBLOCK\narch/loongarch/Kconfig-60-\tselect ARCH_MIGHT_HAVE_PC_PARPORT\n--\narch/mips/Kconfig=2=config MIPS\n--\narch/mips/Kconfig-21-\tselect ARCH_HAS_GCOV_PROFILE_ALL\narch/mips/Kconfig:22:\tselect ARCH_KEEP_MEMBLOCK\narch/mips/Kconfig-23-\tselect ARCH_USE_BUILTIN_BSWAP\n--\narch/powerpc/Kconfig=118=config PPC\n--\narch/powerpc/Kconfig-163-\tselect ARCH_HAVE_EXTRA_ELF_NOTES        if SPU_BASE\narch/powerpc/Kconfig:164:\tselect ARCH_KEEP_MEMBLOCK\narch/powerpc/Kconfig-165-\tselect ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE\tif PPC_RADIX_MMU\n--\narch/riscv/Kconfig=13=config RISCV\n--\narch/riscv/Kconfig-60-\tselect ARCH_HAVE_NMI_SAFE_CMPXCHG\narch/riscv/Kconfig:61:\tselect ARCH_KEEP_MEMBLOCK if ACPI || KEXEC\narch/riscv/Kconfig-62-\tselect ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE\tif 64BIT \u0026\u0026 MMU\n--\narch/x86/Kconfig=1864=config INTEL_TDX_HOST\n--\narch/x86/Kconfig-1869-\tdepends on X86_X2APIC\narch/x86/Kconfig:1870:\tselect ARCH_KEEP_MEMBLOCK\narch/x86/Kconfig-1871-\tdepends on CONTIG_ALLOC\n--\ndrivers/firmware/efi/efi.c=1073=static int efi_mem_reserve_iomem(phys_addr_t addr, u64 size)\n--\ndrivers/firmware/efi/efi.c-1095-\t */\ndrivers/firmware/efi/efi.c:1096:\tif (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) \u0026\u0026 !ret)\ndrivers/firmware/efi/efi.c-1097-\t\tmemblock_reserve(addr, size);\n--\ninclude/linux/memblock.h=114=extern struct memblock memblock;\ninclude/linux/memblock.h-115-\ninclude/linux/memblock.h:116:#ifndef CONFIG_ARCH_KEEP_MEMBLOCK\ninclude/linux/memblock.h-117-#define __init_memblock __meminit\n--\nkernel/kexec_file.c=560=static int locate_mem_hole_callback(struct resource *res, void *arg)\n--\nkernel/kexec_file.c-586-\nkernel/kexec_file.c:587:#ifdef CONFIG_ARCH_KEEP_MEMBLOCK\nkernel/kexec_file.c-588-static int kexec_walk_memblock(struct kexec_buf *kbuf,\n--\nkernel/kexec_file.c=716=int kexec_locate_mem_hole(struct kexec_buf *kbuf)\n--\nkernel/kexec_file.c-738-\nkernel/kexec_file.c:739:\tif (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))\nkernel/kexec_file.c-740-\t\tret = kexec_walk_resources(kbuf, locate_mem_hole_callback);\n--\nmm/Kconfig=486=config MEMBLOCK_KHO_SCRATCH\n--\nmm/Kconfig-491-# Also, memblocks are updated with memory hot(un)plug.\nmm/Kconfig:492:config ARCH_KEEP_MEMBLOCK\nmm/Kconfig-493-\tbool\n--\nmm/memblock.c-100- *\nmm/memblock.c:101: * Unless an architecture enables %CONFIG_ARCH_KEEP_MEMBLOCK, the\nmm/memblock.c-102- * memblock data structures (except \"physmem\") will be discarded after the\n--\nmm/memblock.c=364=static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r)\n--\nmm/memblock.c-380-\nmm/memblock.c:381:#ifndef CONFIG_ARCH_KEEP_MEMBLOCK\nmm/memblock.c-382-/**\n--\nmm/memblock.c=932=unsigned long free_reserved_area(void *start, void *end, int poison, const char *s)\n--\nmm/memblock.c-950-\nmm/memblock.c:951:\tif (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) {\nmm/memblock.c-952-\t\tif (start_pa \u003c end_pa)\n--\nmm/memblock.c=988=int __init_memblock memblock_phys_free(phys_addr_t base, phys_addr_t size)\n--\nmm/memblock.c-997-\nmm/memblock.c:998:\tif (!slab_is_available() || IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))\nmm/memblock.c-999-\t\tret = memblock_remove_range(\u0026memblock.reserved, base, size);\n--\nmm/memblock.c=2855=__setup(\"reserve_mem=\", reserve_mem);\n--\nmm/memblock.c-2857-#ifdef CONFIG_DEBUG_FS\nmm/memblock.c:2858:#ifdef CONFIG_ARCH_KEEP_MEMBLOCK\nmm/memblock.c-2859-static const char * const flagname[] = {\n--\nmm/memblock.c=2923=static inline void memblock_debugfs_expose_arrays(struct dentry *root) { }\nmm/memblock.c-2924-\nmm/memblock.c:2925:#endif /* CONFIG_ARCH_KEEP_MEMBLOCK */\nmm/memblock.c-2926-\n--\nmm/memblock.c=2947=static int __init memblock_init_debugfs(void)\n--\nmm/memblock.c-2950-\nmm/memblock.c:2951:\tif (!IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK) \u0026\u0026 !reserved_mem_count)\nmm/memblock.c-2952-\t\treturn 0;\n--\nmm/memory_hotplug.c=1497=static int __add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags,\n--\nmm/memory_hotplug.c-1527-\nmm/memory_hotplug.c:1528:\tif (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK)) {\nmm/memory_hotplug.c-1529-\t\tif (res-\u003eflags \u0026 IORESOURCE_SYSRAM_DRIVER_MANAGED)\n--\nmm/memory_hotplug.c-1598-error_memblock_remove:\nmm/memory_hotplug.c:1599:\tif (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))\nmm/memory_hotplug.c-1600-\t\tmemblock_remove(start, size);\n--\nmm/memory_hotplug.c=2291=static int try_remove_memory(u64 start, u64 size)\n--\nmm/memory_hotplug.c-2331-\nmm/memory_hotplug.c:2332:\tif (IS_ENABLED(CONFIG_ARCH_KEEP_MEMBLOCK))\nmm/memory_hotplug.c-2333-\t\tmemblock_remove(start, size);\n"
}

23/2 2026/09/24 22:07 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 19489
							output: 26
							thoughts: 2678
						

Thoughts:
**Analysis of Patch Reachability and Fuzzing Applicability**

Okay, so I've been taking a good look at this patch and trying to determine if fuzzing it is worthwhile, and the critical question is reachability. The patch modifies `memblock_reserved_size_nid` within `kernel/liveupdate/kexec_handover.c`, and uses it to replace previous calls to `memblock_reserved_kern_size` and `memblock_reserved_hugetlb_size`. I see that `memblock_reserved_size_nid` is called within `scratch_size_update` and `scratch_size_node`, but that it is only called from within `kho_reserve_scratch`, which in turn is only called by `kho_memory_init`. Then I see that `kho_memory_init` is called by `mm_core_init`, which is, itself, an `__init` function. Also, I see `kho_parse_scratch_size` is called during early boot.

The diff also shows that the patch *removed* `memblock_reserved_hugetlb_size` and *didn't* modify `memblock_reserved_kern_size`. This, combined with the other info, makes me strongly consider if the code is actually reachable by fuzzing.

My primary concern is that anything related to `__init` is generally unreachable after the kernel has completed its initial boot process, and I am fairly certain that this code is called before the kernel is completely initialized. Syzkaller, my primary fuzzing tool, operates by executing system calls from userspace. It's designed to reach code through the standard execution paths *after* the kernel is fully operational. Anything executed before that point is going to be missed.

Specifically, the "core triage philosophy" explicitly states that code falling into "Unreachable execution paths" should not be fuzzed. `__init` code, which is freed after the initialization, clearly falls under this category. Syzkaller, as I understand it, would not be able to interact with this code because it relies on the kernel being initialized and running. Any attempt to touch these sections after boot would result in a crash, but that isn't really relevant if the code cannot be accessed from a regular syzkaller run.

So, unless this `kexec_handover` code somehow hooks into a later lifecycle stage (after early boot) *or* there is some explicit mechanism for syzkaller to trigger early-boot code (which is very unlikely), then the modified code is not reachable by fuzzing, and I will be returning "WorthFuzzing=false". I'll need to do more digging to check the lifecycle of `kexec_handover`, but I am fairly confident.



24/2 2026/09/24 22:07 tool grepper
0m Args:
{
  "Expression": "kho_",
  "PathPrefix": "kernel/liveupdate"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 1563 lines.\nUse more precise expression if possible.\n\nkernel/liveupdate/Makefile=3=luo-y :=\t\t\t\t\t\t\t\t\\\nkernel/liveupdate/Makefile:4:\t\tkho_block.o\t\t\t\t\t\t\\\nkernel/liveupdate/Makefile-5-\t\tluo_core.o\t\t\t\t\t\t\\\n--\nkernel/liveupdate/kexec_handover.c-19-#include \u003clinux/kexec_handover.h\u003e\nkernel/liveupdate/kexec_handover.c:20:#include \u003clinux/kho_radix_tree.h\u003e\nkernel/liveupdate/kexec_handover.c-21-#include \u003clinux/utsname.h\u003e\n--\nkernel/liveupdate/kexec_handover.c=50=static_assert(SCRATCH_ALIGNMENT_BYTES \u003e= CMA_MIN_ALIGNMENT_BYTES);\n--\nkernel/liveupdate/kexec_handover.c-58- */\nkernel/liveupdate/kexec_handover.c:59:union kho_page_info {\nkernel/liveupdate/kexec_handover.c-60-\tunsigned long page_private;\n--\nkernel/liveupdate/kexec_handover.c-66-\nkernel/liveupdate/kexec_handover.c:67:static_assert(sizeof(union kho_page_info) == sizeof(((struct page *)0)-\u003eprivate));\nkernel/liveupdate/kexec_handover.c-68-\nkernel/liveupdate/kexec_handover.c:69:static bool kho_enable __ro_after_init = IS_ENABLED(CONFIG_KEXEC_HANDOVER_ENABLE_DEFAULT);\nkernel/liveupdate/kexec_handover.c-70-\nkernel/liveupdate/kexec_handover.c:71:bool kho_is_enabled(void)\nkernel/liveupdate/kexec_handover.c-72-{\nkernel/liveupdate/kexec_handover.c:73:\treturn kho_enable;\nkernel/liveupdate/kexec_handover.c-74-}\nkernel/liveupdate/kexec_handover.c:75:EXPORT_SYMBOL_GPL(kho_is_enabled);\nkernel/liveupdate/kexec_handover.c-76-\nkernel/liveupdate/kexec_handover.c:77:static int __init kho_parse_enable(char *p)\nkernel/liveupdate/kexec_handover.c-78-{\nkernel/liveupdate/kexec_handover.c:79:\treturn kstrtobool(p, \u0026kho_enable);\nkernel/liveupdate/kexec_handover.c-80-}\nkernel/liveupdate/kexec_handover.c:81:early_param(\"kho\", kho_parse_enable);\nkernel/liveupdate/kexec_handover.c-82-\nkernel/liveupdate/kexec_handover.c:83:struct kho_out {\nkernel/liveupdate/kexec_handover.c-84-\tvoid *fdt;\n--\nkernel/liveupdate/kexec_handover.c-86-\nkernel/liveupdate/kexec_handover.c:87:\tstruct kho_radix_tree radix_tree;\nkernel/liveupdate/kexec_handover.c:88:\tstruct kho_debugfs dbg;\nkernel/liveupdate/kexec_handover.c-89-};\nkernel/liveupdate/kexec_handover.c-90-\nkernel/liveupdate/kexec_handover.c:91:static struct kho_out kho_out = {\nkernel/liveupdate/kexec_handover.c:92:\t.lock = __MUTEX_INITIALIZER(kho_out.lock),\nkernel/liveupdate/kexec_handover.c-93-\t.radix_tree = {\nkernel/liveupdate/kexec_handover.c:94:\t\t.lock = __MUTEX_INITIALIZER(kho_out.radix_tree.lock),\nkernel/liveupdate/kexec_handover.c-95-\t},\n--\nkernel/liveupdate/kexec_handover.c-97-\nkernel/liveupdate/kexec_handover.c:98:struct kho_in {\nkernel/liveupdate/kexec_handover.c-99-\tphys_addr_t fdt_phys;\n--\nkernel/liveupdate/kexec_handover.c-102-\tu32 kexec_count;\nkernel/liveupdate/kexec_handover.c:103:\tstruct kho_debugfs dbg;\nkernel/liveupdate/kexec_handover.c:104:\tstruct kho_radix_tree radix_tree;\nkernel/liveupdate/kexec_handover.c-105-};\nkernel/liveupdate/kexec_handover.c-106-\nkernel/liveupdate/kexec_handover.c:107:static struct kho_in kho_in = {\nkernel/liveupdate/kexec_handover.c-108-};\nkernel/liveupdate/kexec_handover.c-109-\nkernel/liveupdate/kexec_handover.c:110:static const void *kho_get_fdt(void)\nkernel/liveupdate/kexec_handover.c-111-{\nkernel/liveupdate/kexec_handover.c:112:\treturn kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL;\nkernel/liveupdate/kexec_handover.c-113-}\n--\nkernel/liveupdate/kexec_handover.c-115-/**\nkernel/liveupdate/kexec_handover.c:116: * kho_encode_radix_key - Encodes a physical address and order into a radix key.\nkernel/liveupdate/kexec_handover.c-117- * @phys: The physical address of the page.\n--\nkernel/liveupdate/kexec_handover.c-125- */\nkernel/liveupdate/kexec_handover.c:126:static unsigned long kho_encode_radix_key(phys_addr_t phys, unsigned int order)\nkernel/liveupdate/kexec_handover.c-127-{\n--\nkernel/liveupdate/kexec_handover.c-138-/**\nkernel/liveupdate/kexec_handover.c:139: * kho_decode_radix_key - Decodes a radix key back into a physical address and order.\nkernel/liveupdate/kexec_handover.c-140- * @key: The unsigned long key to decode.\n--\nkernel/liveupdate/kexec_handover.c-143- *\nkernel/liveupdate/kexec_handover.c:144: * This function reverses the encoding performed by kho_encode_radix_key(),\nkernel/liveupdate/kexec_handover.c-145- * extracting the original physical address and page order from a given key.\n--\nkernel/liveupdate/kexec_handover.c-148- */\nkernel/liveupdate/kexec_handover.c:149:static phys_addr_t kho_decode_radix_key(unsigned long key, unsigned int *order)\nkernel/liveupdate/kexec_handover.c-150-{\n--\nkernel/liveupdate/kexec_handover.c-162-\nkernel/liveupdate/kexec_handover.c:163:static unsigned long kho_radix_get_bitmap_index(unsigned long key)\nkernel/liveupdate/kexec_handover.c-164-{\n--\nkernel/liveupdate/kexec_handover.c-167-\nkernel/liveupdate/kexec_handover.c:168:static unsigned long kho_radix_get_table_index(unsigned long key,\nkernel/liveupdate/kexec_handover.c-169-\t\t\t\t\t       unsigned int level)\n--\nkernel/liveupdate/kexec_handover.c-176-\nkernel/liveupdate/kexec_handover.c:177:static void __ref *kho_radix_alloc_node(void)\nkernel/liveupdate/kexec_handover.c-178-{\nkernel/liveupdate/kexec_handover.c:179:\tstruct kho_radix_node *node;\nkernel/liveupdate/kexec_handover.c-180-\nkernel/liveupdate/kexec_handover.c-181-\tif (slab_is_available())\nkernel/liveupdate/kexec_handover.c:182:\t\tnode = (struct kho_radix_node *)get_zeroed_page(GFP_KERNEL);\nkernel/liveupdate/kexec_handover.c-183-\telse\n--\nkernel/liveupdate/kexec_handover.c-188-\nkernel/liveupdate/kexec_handover.c:189:static void __ref kho_radix_free_node(struct kho_radix_node *node)\nkernel/liveupdate/kexec_handover.c-190-{\n--\nkernel/liveupdate/kexec_handover.c-197-/**\nkernel/liveupdate/kexec_handover.c:198: * kho_radix_add_key - Add a key to the radix tree.\nkernel/liveupdate/kexec_handover.c-199- * @tree: The KHO radix tree.\n--\nkernel/liveupdate/kexec_handover.c-213- */\nkernel/liveupdate/kexec_handover.c:214:int kho_radix_add_key(struct kho_radix_tree *tree, unsigned long key)\nkernel/liveupdate/kexec_handover.c-215-{\nkernel/liveupdate/kexec_handover.c-216-\t/* Newly allocated nodes for error cleanup */\nkernel/liveupdate/kexec_handover.c:217:\tstruct kho_radix_node *intermediate_nodes[KHO_TREE_MAX_DEPTH] = { 0 };\nkernel/liveupdate/kexec_handover.c:218:\tstruct kho_radix_node *anchor_node = NULL;\nkernel/liveupdate/kexec_handover.c:219:\tstruct kho_radix_node *node = tree-\u003eroot;\nkernel/liveupdate/kexec_handover.c:220:\tstruct kho_radix_node *new_node;\nkernel/liveupdate/kexec_handover.c-221-\tunsigned int i, idx, anchor_idx;\nkernel/liveupdate/kexec_handover.c:222:\tstruct kho_radix_leaf *leaf;\nkernel/liveupdate/kexec_handover.c-223-\tint err = 0;\n--\nkernel/liveupdate/kexec_handover.c-236-\tfor (i = KHO_TREE_MAX_DEPTH - 1; i \u003e 0; i--) {\nkernel/liveupdate/kexec_handover.c:237:\t\tidx = kho_radix_get_table_index(key, i);\nkernel/liveupdate/kexec_handover.c-238-\n--\nkernel/liveupdate/kexec_handover.c-244-\t\t/* Next node is empty, create a new node for it */\nkernel/liveupdate/kexec_handover.c:245:\t\tnew_node = kho_radix_alloc_node();\nkernel/liveupdate/kexec_handover.c-246-\t\tif (!new_node) {\n--\nkernel/liveupdate/kexec_handover.c-266-\t/* Handle the leaf level bitmap (level 0) */\nkernel/liveupdate/kexec_handover.c:267:\tidx = kho_radix_get_bitmap_index(key);\nkernel/liveupdate/kexec_handover.c:268:\tleaf = (struct kho_radix_leaf *)node;\nkernel/liveupdate/kexec_handover.c-269-\t__set_bit(idx, leaf-\u003ebitmap);\n--\nkernel/liveupdate/kexec_handover.c-275-\t\tif (intermediate_nodes[i])\nkernel/liveupdate/kexec_handover.c:276:\t\t\tkho_radix_free_node(intermediate_nodes[i]);\nkernel/liveupdate/kexec_handover.c-277-\t}\n--\nkernel/liveupdate/kexec_handover.c-282-}\nkernel/liveupdate/kexec_handover.c:283:EXPORT_SYMBOL_GPL(kho_radix_add_key);\nkernel/liveupdate/kexec_handover.c-284-\nkernel/liveupdate/kexec_handover.c-285-/**\nkernel/liveupdate/kexec_handover.c:286: * kho_radix_del_key - Removes the key from the radix tree.\nkernel/liveupdate/kexec_handover.c-287- * @tree: The KHO radix tree.\n--\nkernel/liveupdate/kexec_handover.c-293- */\nkernel/liveupdate/kexec_handover.c:294:void kho_radix_del_key(struct kho_radix_tree *tree, unsigned long key)\nkernel/liveupdate/kexec_handover.c-295-{\nkernel/liveupdate/kexec_handover.c:296:\tstruct kho_radix_node *node = tree-\u003eroot;\nkernel/liveupdate/kexec_handover.c:297:\tstruct kho_radix_leaf *leaf;\nkernel/liveupdate/kexec_handover.c-298-\tunsigned int i, idx;\n--\nkernel/liveupdate/kexec_handover.c-312-\tfor (i = KHO_TREE_MAX_DEPTH - 1; i \u003e 0; i--) {\nkernel/liveupdate/kexec_handover.c:313:\t\tidx = kho_radix_get_table_index(key, i);\nkernel/liveupdate/kexec_handover.c-314-\n--\nkernel/liveupdate/kexec_handover.c-325-\t/* Handle the leaf level bitmap (level 0) */\nkernel/liveupdate/kexec_handover.c:326:\tleaf = (struct kho_radix_leaf *)node;\nkernel/liveupdate/kexec_handover.c:327:\tidx = kho_radix_get_bitmap_index(key);\nkernel/liveupdate/kexec_handover.c-328-\t__clear_bit(idx, leaf-\u003ebitmap);\nkernel/liveupdate/kexec_handover.c-329-}\nkernel/liveupdate/kexec_handover.c:330:EXPORT_SYMBOL_GPL(kho_radix_del_key);\nkernel/liveupdate/kexec_handover.c-331-\nkernel/liveupdate/kexec_handover.c:332:static void __kho_radix_destroy_tree(struct kho_radix_node *root,\nkernel/liveupdate/kexec_handover.c-333-\t\t\t\t     unsigned int level)\n--\nkernel/liveupdate/kexec_handover.c-337-\tif (level == 0) {\nkernel/liveupdate/kexec_handover.c:338:\t\tkho_radix_free_node(root);\nkernel/liveupdate/kexec_handover.c-339-\t\treturn;\n--\nkernel/liveupdate/kexec_handover.c-343-\t\tif (root-\u003etable[i])\nkernel/liveupdate/kexec_handover.c:344:\t\t\t__kho_radix_destroy_tree(phys_to_virt(root-\u003etable[i]),\nkernel/liveupdate/kexec_handover.c-345-\t\t\t\t\t\t level - 1);\n--\nkernel/liveupdate/kexec_handover.c-347-\nkernel/liveupdate/kexec_handover.c:348:\tkho_radix_free_node(root);\nkernel/liveupdate/kexec_handover.c-349-}\n--\nkernel/liveupdate/kexec_handover.c-351-/**\nkernel/liveupdate/kexec_handover.c:352: * kho_radix_init_tree - initialize the radix tree.\nkernel/liveupdate/kexec_handover.c-353- * @tree:   the tree to initialize.\n--\nkernel/liveupdate/kexec_handover.c-361- */\nkernel/liveupdate/kexec_handover.c:362:int kho_radix_init_tree(struct kho_radix_tree *tree, struct kho_radix_node *root)\nkernel/liveupdate/kexec_handover.c-363-{\nkernel/liveupdate/kexec_handover.c-364-\tif (!root)\nkernel/liveupdate/kexec_handover.c:365:\t\troot = kho_radix_alloc_node();\nkernel/liveupdate/kexec_handover.c-366-\tif (!root)\n--\nkernel/liveupdate/kexec_handover.c-372-}\nkernel/liveupdate/kexec_handover.c:373:EXPORT_SYMBOL_GPL(kho_radix_init_tree);\nkernel/liveupdate/kexec_handover.c-374-\nkernel/liveupdate/kexec_handover.c-375-/**\nkernel/liveupdate/kexec_handover.c:376: * kho_radix_destroy_tree - Destroy the radix tree\nkernel/liveupdate/kexec_handover.c-377- * @tree: The radix tree to destroy\n--\nkernel/liveupdate/kexec_handover.c-380- */\nkernel/liveupdate/kexec_handover.c:381:void kho_radix_destroy_tree(struct kho_radix_tree *tree)\nkernel/liveupdate/kexec_handover.c-382-{\n--\nkernel/liveupdate/kexec_handover.c-385-\nkernel/liveupdate/kexec_handover.c:386:\t__kho_radix_destroy_tree(tree-\u003eroot, KHO_TREE_MAX_DEPTH - 1);\nkernel/liveupdate/kexec_handover.c-387-\ttree-\u003eroot = NULL;\nkernel/liveupdate/kexec_handover.c-388-}\nkernel/liveupdate/kexec_handover.c:389:EXPORT_SYMBOL_GPL(kho_radix_destroy_tree);\nkernel/liveupdate/kexec_handover.c-390-\nkernel/liveupdate/kexec_handover.c:391:static int kho_radix_walk_leaf(struct kho_radix_leaf *leaf, unsigned long key,\nkernel/liveupdate/kexec_handover.c:392:\t\t\t       const struct kho_radix_walk_cb *cb, void *data)\nkernel/liveupdate/kexec_handover.c-393-{\n--\nkernel/liveupdate/kexec_handover.c-415-\nkernel/liveupdate/kexec_handover.c:416:static int __kho_radix_walk_tree(struct kho_radix_node *root,\nkernel/liveupdate/kexec_handover.c-417-\t\t\t\t unsigned int level, unsigned long start,\nkernel/liveupdate/kexec_handover.c:418:\t\t\t\t const struct kho_radix_walk_cb *cb, void *data)\nkernel/liveupdate/kexec_handover.c-419-{\nkernel/liveupdate/kexec_handover.c:420:\tstruct kho_radix_node *node;\nkernel/liveupdate/kexec_handover.c:421:\tstruct kho_radix_leaf *leaf;\nkernel/liveupdate/kexec_handover.c-422-\tunsigned long key, i;\n--\nkernel/liveupdate/kexec_handover.c-446-\t\t\t */\nkernel/liveupdate/kexec_handover.c:447:\t\t\tleaf = (struct kho_radix_leaf *)node;\nkernel/liveupdate/kexec_handover.c:448:\t\t\terr = kho_radix_walk_leaf(leaf, key, cb, data);\nkernel/liveupdate/kexec_handover.c-449-\t\t} else {\nkernel/liveupdate/kexec_handover.c:450:\t\t\terr  = __kho_radix_walk_tree(node, level - 1,\nkernel/liveupdate/kexec_handover.c-451-\t\t\t\t\t\t     key, cb, data);\n--\nkernel/liveupdate/kexec_handover.c-461-/**\nkernel/liveupdate/kexec_handover.c:462: * kho_radix_walk_tree - Traverses the radix tree and calls a callback for each key.\nkernel/liveupdate/kexec_handover.c-463- * @tree: A pointer to the KHO radix tree to walk.\n--\nkernel/liveupdate/kexec_handover.c-472- */\nkernel/liveupdate/kexec_handover.c:473:int kho_radix_walk_tree(struct kho_radix_tree *tree,\nkernel/liveupdate/kexec_handover.c:474:\t\t\tconst struct kho_radix_walk_cb *cb, void *data)\nkernel/liveupdate/kexec_handover.c-475-{\n--\nkernel/liveupdate/kexec_handover.c-480-\nkernel/liveupdate/kexec_handover.c:481:\treturn __kho_radix_walk_tree(tree-\u003eroot, KHO_TREE_MAX_DEPTH - 1, 0, cb,\nkernel/liveupdate/kexec_handover.c-482-\t\t\t\t     data);\nkernel/liveupdate/kexec_handover.c-483-}\nkernel/liveupdate/kexec_handover.c:484:EXPORT_SYMBOL_GPL(kho_radix_walk_tree);\nkernel/liveupdate/kexec_handover.c-485-\nkernel/liveupdate/kexec_handover.c-486-/* For physically contiguous 0-order pages. */\nkernel/liveupdate/kexec_handover.c:487:static void kho_init_pages(struct page *page, unsigned long nr_pages)\nkernel/liveupdate/kexec_handover.c-488-{\n--\nkernel/liveupdate/kexec_handover.c-495-\nkernel/liveupdate/kexec_handover.c:496:static void kho_init_folio(struct page *page, unsigned int order)\nkernel/liveupdate/kexec_handover.c-497-{\n--\nkernel/liveupdate/kexec_handover.c-512-\nkernel/liveupdate/kexec_handover.c:513:static struct page *kho_restore_page(phys_addr_t phys, bool is_folio)\nkernel/liveupdate/kexec_handover.c-514-{\n--\nkernel/liveupdate/kexec_handover.c-516-\tunsigned long nr_pages;\nkernel/liveupdate/kexec_handover.c:517:\tunion kho_page_info info;\nkernel/liveupdate/kexec_handover.c-518-\n--\nkernel/liveupdate/kexec_handover.c-535-\tif (is_folio)\nkernel/liveupdate/kexec_handover.c:536:\t\tkho_init_folio(page, info.order);\nkernel/liveupdate/kexec_handover.c-537-\telse\nkernel/liveupdate/kexec_handover.c:538:\t\tkho_init_pages(page, nr_pages);\nkernel/liveupdate/kexec_handover.c-539-\n--\nkernel/liveupdate/kexec_handover.c-544-/**\nkernel/liveupdate/kexec_handover.c:545: * kho_restore_folio - recreates the folio from the preserved memory.\nkernel/liveupdate/kexec_handover.c-546- * @phys: physical address of the folio.\n--\nkernel/liveupdate/kexec_handover.c-549- */\nkernel/liveupdate/kexec_handover.c:550:struct folio *kho_restore_folio(phys_addr_t phys)\nkernel/liveupdate/kexec_handover.c-551-{\nkernel/liveupdate/kexec_handover.c:552:\tstruct page *page = kho_restore_page(phys, true);\nkernel/liveupdate/kexec_handover.c-553-\n--\nkernel/liveupdate/kexec_handover.c-555-}\nkernel/liveupdate/kexec_handover.c:556:EXPORT_SYMBOL_GPL(kho_restore_folio);\nkernel/liveupdate/kexec_handover.c-557-\nkernel/liveupdate/kexec_handover.c-558-/**\nkernel/liveupdate/kexec_handover.c:559: * kho_restore_pages - restore list of contiguous order 0 pages.\nkernel/liveupdate/kexec_handover.c-560- * @phys: physical address of the first page.\n--\nkernel/liveupdate/kexec_handover.c-563- * Restore a contiguous list of order 0 pages that was preserved with\nkernel/liveupdate/kexec_handover.c:564: * kho_preserve_pages().\nkernel/liveupdate/kexec_handover.c-565- *\n--\nkernel/liveupdate/kexec_handover.c-567- */\nkernel/liveupdate/kexec_handover.c:568:struct page *kho_restore_pages(phys_addr_t phys, unsigned long nr_pages)\nkernel/liveupdate/kexec_handover.c-569-{\n--\nkernel/liveupdate/kexec_handover.c-576-\t\t\tmin(count_trailing_zeros(pfn), ilog2(end_pfn - pfn));\nkernel/liveupdate/kexec_handover.c:577:\t\tstruct page *page = kho_restore_page(PFN_PHYS(pfn), false);\nkernel/liveupdate/kexec_handover.c-578-\n--\nkernel/liveupdate/kexec_handover.c-585-}\nkernel/liveupdate/kexec_handover.c:586:EXPORT_SYMBOL_GPL(kho_restore_pages);\nkernel/liveupdate/kexec_handover.c-587-\n--\nkernel/liveupdate/kexec_handover.c-593- * Ensure all the struct pages in the preservation are\nkernel/liveupdate/kexec_handover.c:594: * initialized. kho_preserved_memory_reserve() marks the reservation as noinit\nkernel/liveupdate/kexec_handover.c-595- * to make sure they don't get re-initialized later.\nkernel/liveupdate/kexec_handover.c-596- */\nkernel/liveupdate/kexec_handover.c:597:static struct page *__init kho_get_preserved_page(phys_addr_t phys,\nkernel/liveupdate/kexec_handover.c-598-\t\t\t\t\t\t  unsigned int order)\n--\nkernel/liveupdate/kexec_handover.c-612-\nkernel/liveupdate/kexec_handover.c:613:static int __init kho_preserved_memory_reserve(unsigned long key, void *data)\nkernel/liveupdate/kexec_handover.c-614-{\nkernel/liveupdate/kexec_handover.c:615:\tunion kho_page_info info;\nkernel/liveupdate/kexec_handover.c-616-\tstruct page *page;\n--\nkernel/liveupdate/kexec_handover.c-620-\nkernel/liveupdate/kexec_handover.c:621:\tphys = kho_decode_radix_key(key, \u0026order);\nkernel/liveupdate/kexec_handover.c-622-\nkernel/liveupdate/kexec_handover.c-623-\tsz = 1UL \u003c\u003c (order + PAGE_SHIFT);\nkernel/liveupdate/kexec_handover.c:624:\tpage = kho_get_preserved_page(phys, order);\nkernel/liveupdate/kexec_handover.c-625-\n--\nkernel/liveupdate/kexec_handover.c-636-/* Returns physical address of the preserved memory map from FDT */\nkernel/liveupdate/kexec_handover.c:637:static phys_addr_t __init kho_get_mem_map_phys(const void *fdt)\nkernel/liveupdate/kexec_handover.c-638-{\n--\nkernel/liveupdate/kexec_handover.c-650-\nkernel/liveupdate/kexec_handover.c:651:static void __init *kho_get_mem_map(const void *fdt)\nkernel/liveupdate/kexec_handover.c-652-{\nkernel/liveupdate/kexec_handover.c:653:\tphys_addr_t phys = kho_get_mem_map_phys(fdt);\nkernel/liveupdate/kexec_handover.c-654-\n--\nkernel/liveupdate/kexec_handover.c-665- */\nkernel/liveupdate/kexec_handover.c:666:struct kho_scratch *kho_scratch;\nkernel/liveupdate/kexec_handover.c:667:unsigned int kho_scratch_cnt;\nkernel/liveupdate/kexec_handover.c-668-\n--\nkernel/liveupdate/kexec_handover.c-672- *\nkernel/liveupdate/kexec_handover.c:673: * kho_scratch=N%\nkernel/liveupdate/kexec_handover.c-674- *\n--\nkernel/liveupdate/kexec_handover.c-677- *\nkernel/liveupdate/kexec_handover.c:678: * kho_scratch=l[KMG],n[KMG],m[KMG]\nkernel/liveupdate/kexec_handover.c-679- *\n--\nkernel/liveupdate/kexec_handover.c=685=static phys_addr_t scratch_size_lowmem __initdata;\nkernel/liveupdate/kexec_handover.c-686-\nkernel/liveupdate/kexec_handover.c:687:static int __init kho_parse_scratch_size(char *p)\nkernel/liveupdate/kexec_handover.c-688-{\n--\nkernel/liveupdate/kexec_handover.c-752-}\nkernel/liveupdate/kexec_handover.c:753:early_param(\"kho_scratch\", kho_parse_scratch_size);\nkernel/liveupdate/kexec_handover.c-754-\n--\nkernel/liveupdate/kexec_handover.c=813=static phys_addr_t __init scratch_size_node(int nid)\n--\nkernel/liveupdate/kexec_handover.c-830-\nkernel/liveupdate/kexec_handover.c:831:bool kho_scratch_overlap(phys_addr_t phys, size_t size)\nkernel/liveupdate/kexec_handover.c-832-{\n--\nkernel/liveupdate/kexec_handover.c-835-\nkernel/liveupdate/kexec_handover.c:836:\tfor (i = 0; i \u003c kho_scratch_cnt; i++) {\nkernel/liveupdate/kexec_handover.c:837:\t\tscratch_start = kho_scratch[i].addr;\nkernel/liveupdate/kexec_handover.c:838:\t\tscratch_end = kho_scratch[i].addr + kho_scratch[i].size;\nkernel/liveupdate/kexec_handover.c-839-\n--\nkernel/liveupdate/kexec_handover.c-847-/**\nkernel/liveupdate/kexec_handover.c:848: * kho_reserve_scratch - Reserve a contiguous chunk of memory for kexec\nkernel/liveupdate/kexec_handover.c-849- *\n--\nkernel/liveupdate/kexec_handover.c-855- */\nkernel/liveupdate/kexec_handover.c:856:static void __init kho_reserve_scratch(void)\nkernel/liveupdate/kexec_handover.c-857-{\n--\nkernel/liveupdate/kexec_handover.c-860-\nkernel/liveupdate/kexec_handover.c:861:\tif (!kho_enable)\nkernel/liveupdate/kexec_handover.c-862-\t\treturn;\n--\nkernel/liveupdate/kexec_handover.c-866-\t/* FIXME: deal with node hot-plug/remove */\nkernel/liveupdate/kexec_handover.c:867:\tkho_scratch_cnt = nodes_weight(node_states[N_MEMORY]) + 2;\nkernel/liveupdate/kexec_handover.c:868:\tsize = kho_scratch_cnt * sizeof(*kho_scratch);\nkernel/liveupdate/kexec_handover.c:869:\tkho_scratch = memblock_alloc(size, PAGE_SIZE);\nkernel/liveupdate/kexec_handover.c:870:\tif (!kho_scratch) {\nkernel/liveupdate/kexec_handover.c-871-\t\tpr_err(\"Failed to reserve scratch array\\n\");\n--\nkernel/liveupdate/kexec_handover.c-886-\nkernel/liveupdate/kexec_handover.c:887:\tkho_scratch[i].addr = addr;\nkernel/liveupdate/kexec_handover.c:888:\tkho_scratch[i].size = size;\nkernel/liveupdate/kexec_handover.c-889-\ti++;\n--\nkernel/liveupdate/kexec_handover.c-898-\nkernel/liveupdate/kexec_handover.c:899:\tkho_scratch[i].addr = addr;\nkernel/liveupdate/kexec_handover.c:900:\tkho_scratch[i].size = size;\nkernel/liveupdate/kexec_handover.c-901-\ti++;\n--\nkernel/liveupdate/kexec_handover.c-916-\nkernel/liveupdate/kexec_handover.c:917:\t\tkho_scratch[i].addr = addr;\nkernel/liveupdate/kexec_handover.c:918:\t\tkho_scratch[i].size = size;\nkernel/liveupdate/kexec_handover.c-919-\t\ti++;\n--\nkernel/liveupdate/kexec_handover.c-925-\tfor (i--; i \u003e= 0; i--)\nkernel/liveupdate/kexec_handover.c:926:\t\tmemblock_phys_free(kho_scratch[i].addr, kho_scratch[i].size);\nkernel/liveupdate/kexec_handover.c-927-err_free_scratch_desc:\nkernel/liveupdate/kexec_handover.c:928:\tmemblock_free(kho_scratch, kho_scratch_cnt * sizeof(*kho_scratch));\nkernel/liveupdate/kexec_handover.c-929-err_disable_kho:\nkernel/liveupdate/kexec_handover.c-930-\tpr_warn(\"Failed to reserve scratch area, disabling kexec handover\\n\");\nkernel/liveupdate/kexec_handover.c:931:\tkho_enable = false;\nkernel/liveupdate/kexec_handover.c-932-}\n--\nkernel/liveupdate/kexec_handover.c-943-/* Called for the KHO preserved memory radix tree. */\nkernel/liveupdate/kexec_handover.c:944:static int __init kho_ext_walk_leaf(unsigned long key, void *data)\nkernel/liveupdate/kexec_handover.c-945-{\nkernel/liveupdate/kexec_handover.c:946:\tstruct kho_radix_tree *busy_blocks = data;\nkernel/liveupdate/kexec_handover.c-947-\tphys_addr_t start, end;\n--\nkernel/liveupdate/kexec_handover.c-954-\t */\nkernel/liveupdate/kexec_handover.c:955:\tstart = kho_decode_radix_key(key, \u0026order);\nkernel/liveupdate/kexec_handover.c-956-\tend = start + (1UL \u003c\u003c (order + PAGE_SHIFT));\n--\nkernel/liveupdate/kexec_handover.c-958-\twhile (start \u003c end) {\nkernel/liveupdate/kexec_handover.c:959:\t\terr = kho_radix_add_key(busy_blocks, start \u003e\u003e KHO_SCRATCH_EXT_BLKSHIFT);\nkernel/liveupdate/kexec_handover.c-960-\t\tif (err)\n--\nkernel/liveupdate/kexec_handover.c-969-/* Called for the KHO preserved memory radix tree. */\nkernel/liveupdate/kexec_handover.c:970:static int __init kho_ext_walk_node(phys_addr_t phys, void *data)\nkernel/liveupdate/kexec_handover.c-971-{\nkernel/liveupdate/kexec_handover.c:972:\tstruct kho_radix_tree *busy_blocks = data;\nkernel/liveupdate/kexec_handover.c-973-\nkernel/liveupdate/kexec_handover.c:974:\treturn kho_radix_add_key(busy_blocks, phys \u003e\u003e KHO_SCRATCH_EXT_BLKSHIFT);\nkernel/liveupdate/kexec_handover.c-975-}\n--\nkernel/liveupdate/kexec_handover.c-977-/* Called for the busy block radix tree. */\nkernel/liveupdate/kexec_handover.c:978:static int __init kho_ext_mark_scratch(unsigned long key, void *data)\nkernel/liveupdate/kexec_handover.c-979-{\n--\nkernel/liveupdate/kexec_handover.c-984-\tif (start \u003e *prev_end) {\nkernel/liveupdate/kexec_handover.c:985:\t\terr = memblock_mark_kho_scratch(*prev_end, start - *prev_end);\nkernel/liveupdate/kexec_handover.c-986-\t\tif (err)\n--\nkernel/liveupdate/kexec_handover.c-994-/*\nkernel/liveupdate/kexec_handover.c:995: * kho_extend_scratch - Extend the scratch regions\nkernel/liveupdate/kexec_handover.c-996- *\n--\nkernel/liveupdate/kexec_handover.c-1018- */\nkernel/liveupdate/kexec_handover.c:1019:static void __init kho_extend_scratch(void)\nkernel/liveupdate/kexec_handover.c-1020-{\nkernel/liveupdate/kexec_handover.c:1021:\tconst struct kho_radix_walk_cb kho_cb = {\nkernel/liveupdate/kexec_handover.c:1022:\t\t.leaf = kho_ext_walk_leaf,\nkernel/liveupdate/kexec_handover.c:1023:\t\t.node = kho_ext_walk_node,\nkernel/liveupdate/kexec_handover.c-1024-\t};\nkernel/liveupdate/kexec_handover.c:1025:\tconst struct kho_radix_walk_cb ext_cb = {\nkernel/liveupdate/kexec_handover.c:1026:\t\t.leaf = kho_ext_mark_scratch,\nkernel/liveupdate/kexec_handover.c-1027-\t};\nkernel/liveupdate/kexec_handover.c-1028-\tstatic struct lock_class_key busy_radix_class;\nkernel/liveupdate/kexec_handover.c:1029:\tstruct kho_radix_tree busy_blocks;\nkernel/liveupdate/kexec_handover.c-1030-\tphys_addr_t prev_end = 0;\n--\nkernel/liveupdate/kexec_handover.c-1032-\nkernel/liveupdate/kexec_handover.c:1033:\terr = kho_radix_init_tree(\u0026busy_blocks, NULL);\nkernel/liveupdate/kexec_handover.c-1034-\tif (err)\n--\nkernel/liveupdate/kexec_handover.c-1037-\t/*\nkernel/liveupdate/kexec_handover.c:1038:\t * The walk of kho_in.radix_tree adds keys to busy_blocks. The walk\nkernel/liveupdate/kexec_handover.c:1039:\t * takes the kho_in radix tree lock and adding the key takes busy_blocks\nkernel/liveupdate/kexec_handover.c:1040:\t * lock. Since both are struct kho_radix_tree and share the same lock\nkernel/liveupdate/kexec_handover.c-1041-\t * class, lockdep gets confused. Set a different class for\n--\nkernel/liveupdate/kexec_handover.c-1046-\t/* Walk the KHO radix tree to find busy blocks. */\nkernel/liveupdate/kexec_handover.c:1047:\terr = kho_radix_walk_tree(\u0026kho_in.radix_tree, \u0026kho_cb, \u0026busy_blocks);\nkernel/liveupdate/kexec_handover.c-1048-\tif (err)\n--\nkernel/liveupdate/kexec_handover.c-1051-\t/* Walk the busy blocks and mark everything between keys as scratch. */\nkernel/liveupdate/kexec_handover.c:1052:\terr = kho_radix_walk_tree(\u0026busy_blocks, \u0026ext_cb, \u0026prev_end);\nkernel/liveupdate/kexec_handover.c-1053-\tif (err)\n--\nkernel/liveupdate/kexec_handover.c-1057-\tif (prev_end \u003c memblock_end_of_DRAM())\nkernel/liveupdate/kexec_handover.c:1058:\t\terr = memblock_mark_kho_scratch(prev_end, memblock_end_of_DRAM() - prev_end);\nkernel/liveupdate/kexec_handover.c-1059-\n--\nkernel/liveupdate/kexec_handover.c-1061-out:\nkernel/liveupdate/kexec_handover.c:1062:\tkho_radix_destroy_tree(\u0026busy_blocks);\nkernel/liveupdate/kexec_handover.c-1063-print:\n--\nkernel/liveupdate/kexec_handover.c-1068-/**\nkernel/liveupdate/kexec_handover.c:1069: * kho_add_subtree - record the physical address of a sub blob in KHO root tree.\nkernel/liveupdate/kexec_handover.c-1070- * @name: name of the sub tree.\n--\nkernel/liveupdate/kexec_handover.c-1083- */\nkernel/liveupdate/kexec_handover.c:1084:int kho_add_subtree(const char *name, void *blob, size_t size)\nkernel/liveupdate/kexec_handover.c-1085-{\nkernel/liveupdate/kexec_handover.c-1086-\tphys_addr_t phys = virt_to_phys(blob);\nkernel/liveupdate/kexec_handover.c:1087:\tvoid *root_fdt = kho_out.fdt;\nkernel/liveupdate/kexec_handover.c-1088-\tu64 size_u64 = size;\n--\nkernel/liveupdate/kexec_handover.c-1091-\nkernel/liveupdate/kexec_handover.c:1092:\tguard(mutex)(\u0026kho_out.lock);\nkernel/liveupdate/kexec_handover.c-1093-\n--\nkernel/liveupdate/kexec_handover.c-1114-\nkernel/liveupdate/kexec_handover.c:1115:\tWARN_ON_ONCE(kho_debugfs_blob_add(\u0026kho_out.dbg, name, blob,\nkernel/liveupdate/kexec_handover.c-1116-\t\t\t\t\t  size, false));\n--\nkernel/liveupdate/kexec_handover.c-1127-}\nkernel/liveupdate/kexec_handover.c:1128:EXPORT_SYMBOL_GPL(kho_add_subtree);\nkernel/liveupdate/kexec_handover.c-1129-\nkernel/liveupdate/kexec_handover.c:1130:void kho_remove_subtree(void *blob)\nkernel/liveupdate/kexec_handover.c-1131-{\nkernel/liveupdate/kexec_handover.c-1132-\tphys_addr_t target_phys = virt_to_phys(blob);\n\n"
}

25/2 2026/09/24 22:07 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 34108
							output: 35
							thoughts: 2338
						

Thoughts:
**Fuzzing Analysis of Live Update Kexec Handover Code**

Okay, let's break this down. My initial thought is to figure out if this patch is worth fuzzing, and the triage criteria gives us a good framework.  The `kexec_handover.c` file is interesting, with its runtime functions like `kho_add_subtree` and `kho_fill_kimage`.  However, I need to focus on *what* the patch actually changed.

I'm carefully reviewing the "NEGATIVE CRITERIA" from the fuzzing triage philosophy. The key is to determine if the modified code falls into any of those negative categories. I need to be sure.  Section 1 emphasizes reachability; fuzzing is only useful for executable code in standard environments. Section 2 lays out the "NEGATIVE CRITERIA" in detail.

The mention of "early boot code" gave me pause. I quickly realized Syzkaller is a *runtime* fuzzer. It operates *after* the initial boot sequence. It can't, by its very nature, touch anything in the `__init` memory region because that is freed after boot. The `.init.text` and `.init.data` are long gone. Any attempt to call `__init` functions from Syzkaller would be an illegal memory access.

Now, let's get specific to the patch. It seems that three files are modified, `include/linux/memblock.h`, `mm/memblock.c`, and `kernel/liveupdate/kexec_handover.c`. `memblock_reserved_hugetlb_size` has been removed from `include/linux/memblock.h` and `mm/memblock.c`. In `kernel/liveupdate/kexec_handover.c` I can see `memblock_reserved_size_nid` being used. The changes involve `scratch_size_update` and `scratch_size_node` which compute sizes related to scratch memory. Those sizes are, in turn, used in `kho_reserve_scratch` to allocate a region of memory, `kho_scratch`.  Critically, `kho_scratch` is used at runtime by `kho_scratch_overlap` and `kho_fill_kimage`.

So, the critical question is: Does the *patch* change the runtime *use* of `kho_scratch` or its associated functions? No, the patch changed the way the sizes are calculated, before the `kho_scratch` memory region is reserved. The calculations themselves are done with `__init` functions.

Before the patch:
`scratch_size_update`:

```c
size = memblock_reserved_kern_size(ARCH_LOW_ADDRESS_LIMIT,
							   NUMA_NO_NODE);
		size -= memblock_reserved_hugetlb_size(ARCH_LOW_ADDRESS_LIMIT,
							   NUMA_NO_NODE);
```

and `scratch_size_node`:
```c
size = memblock_reserved_kern_size(MEMBLOCK_ALLOC_ANYWHERE,
							   nid);
		/* Do not count HugeTLB pages. */
		size -= memblock_reserved_hugetlb_size(MEMBLOCK_ALLOC_ANYWHERE,
							   nid);
```

So the code has changed to use `memblock_reserved_size_nid` instead of `memblock_reserved_kern_size` and `memblock_reserved_hugetlb_size`.

**In short, this is an `__init` change that computes memory sizes for a runtime structure, but it does not change any runtime code directly. This patch, by itself, is NOT worth fuzzing.**



26/2 2026/09/24 22:07 tool read-file
0m Args:
{
  "File": "mm/memblock.c",
  "FirstLine": 1900,
  "LineCount": 40
}

Results:
{
  "Contents": "1900:\t\treturn memblock.reserved.total_size;\n1901:\t}\n1902:\t\n1903:\tphys_addr_t __init_memblock memblock_reserved_kern_size(phys_addr_t limit, int nid)\n1904:\t{\n1905:\t\tstruct memblock_region *r;\n1906:\t\tphys_addr_t total = 0;\n1907:\t\n1908:\t\tfor_each_reserved_mem_region(r) {\n1909:\t\t\tphys_addr_t size = r-\u003esize;\n1910:\t\n1911:\t\t\tif (r-\u003ebase \u003e limit)\n1912:\t\t\t\tbreak;\n1913:\t\n1914:\t\t\tif (r-\u003ebase + r-\u003esize \u003e limit)\n1915:\t\t\t\tsize = limit - r-\u003ebase;\n1916:\t\n1917:\t\t\tif (nid == memblock_get_region_node(r) || !numa_valid_node(nid))\n1918:\t\t\t\tif (r-\u003eflags \u0026 MEMBLOCK_RSRV_KERN)\n1919:\t\t\t\t\ttotal += size;\n1920:\t\t}\n1921:\t\n1922:\t\treturn total;\n1923:\t}\n1924:\t\n1925:\t/**\n1926:\t * memblock_estimated_nr_free_pages - return estimated number of free pages\n1927:\t * from memblock point of view\n1928:\t *\n1929:\t * During bootup, subsystems might need a rough estimate of the number of free\n1930:\t * pages in the whole system, before precise numbers are available from the\n1931:\t * buddy. Especially with CONFIG_DEFERRED_STRUCT_PAGE_INIT, the numbers\n1932:\t * obtained from the buddy might be very imprecise during bootup.\n1933:\t *\n1934:\t * Return:\n1935:\t * An estimated number of free pages from memblock point of view.\n1936:\t */\n1937:\tunsigned long __init memblock_estimated_nr_free_pages(void)\n1938:\t{\n1939:\t\treturn PHYS_PFN(memblock_phys_mem_size() -\n"
}

27/2 2026/09/24 22:07 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37875
							output: 23
							thoughts: 6811
						

Thoughts:
**Analysis of Memblock Reservation Size Calculation and Kexec Handover**

Okay, so I've been digging into this patch. The author removed `memblock_reserved_hugetlb_size` and replaced it with a more generic helper, `memblock_reserved_size_nid`, within `kexec_handover.c`.  All these involved functions are marked as `__init` or `__init_memblock`, and the core functions are called during early boot.  The critical path seems to be `start_kernel` -> `mm_core_init` -> `kho_memory_init` -> `kho_reserve_scratch` -> (`scratch_size_update` / `scratch_size_node`) -> `memblock_reserved_size_nid`. After `kho_memory_init` runs, `memblock_free_all` releases the memory, and `free_initmem` frees the `.init` sections.

My initial thought was, "This is early boot code. Syzkaller can't touch it."  The instructions are clear on that: REACHABILITY is key. Syzkaller, operating in a virtualized environment, can only reach code that executes *after* the initial boot sequence. Section 2's negative criteria spell it out explicitly, including "Unreachable execution paths".

But the prompt also says, "Do NOT blindly trust "NO FUNCTIONAL CHANGE" or "REFACTORING" claims." So I have to be extremely careful. I needed to ensure that this isn't a case of introducing new invariants in code that *is* reachable, even if the code itself seems disconnected.

I examined the diff, and the critical code is this NUMA logic difference:  `memblock_reserved_size_nid`'s `if (nid == memblock_get_region_node(r))` versus the old `if (nid == memblock_get_region_node(r) || !numa_valid_node(nid))`.  Crucially, `scratch_size_update` and `scratch_size_node` *do* call this with `NUMA_NO_NODE` as `nid`. This change breaks how NUMA_NO_NODE is handled, as `memblock_get_region_node` will now never match a `NUMA_NO_NODE` value. Before, all nodes were matched if numa was not valid. But is this bug *fuzzable*?

I walked through the call chain again to be 100% sure: NO other callers than the boot-time functions.  Because the functions are marked `__init`, and `free_initmem` is called, it's PHYSICALLY impossible for syzkaller to reach this code. I checked specifically for any scenario where the `kexec` or `kho` system might call these functions *later*, after syzkaller starts. It does NOT.

Syzkaller instruments running kernels. The instructions are very explicit about "structurally unreachable" code, and I believe this definitively falls into that category. So, even though there's a bug with how NUMA_NO_NODE is handled, it's not a bug that can be exploited via syzkaller, or by anything running *after* the kernel starts.



28/2 2026/09/24 22:07 tool grepper
0m Args:
{
  "Expression": "KEXEC_HANDOVER_ENABLE_DEFAULT"
}

Results:
{
  "Output": "kernel/liveupdate/Kconfig=27=config KEXEC_HANDOVER_DEBUGFS\n--\nkernel/liveupdate/Kconfig-37-\nkernel/liveupdate/Kconfig:38:config KEXEC_HANDOVER_ENABLE_DEFAULT\nkernel/liveupdate/Kconfig-39-\tbool \"Enable kexec handover by default\"\n--\nkernel/liveupdate/kexec_handover.c=67=static_assert(sizeof(union kho_page_info) == sizeof(((struct page *)0)-\u003eprivate));\nkernel/liveupdate/kexec_handover.c-68-\nkernel/liveupdate/kexec_handover.c:69:static bool kho_enable __ro_after_init = IS_ENABLED(CONFIG_KEXEC_HANDOVER_ENABLE_DEFAULT);\nkernel/liveupdate/kexec_handover.c-70-\n--\ntools/testing/selftests/liveupdate/config=4=CONFIG_KEXEC_HANDOVER=y\ntools/testing/selftests/liveupdate/config:5:CONFIG_KEXEC_HANDOVER_ENABLE_DEFAULT=y\ntools/testing/selftests/liveupdate/config-6-CONFIG_KEXEC_HANDOVER_DEBUGFS=y\n"
}

29/2 2026/09/24 22:07 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 45697
							output: 192
							thoughts: 1423
						

Thoughts:
**Analysis of Fuzzing Applicability**

Okay, here's the breakdown of my thinking. I'm looking at this patch and determining whether it's suitable for fuzzing with syzkaller. The initial question is whether `WorthFuzzing` should be true or false.  We're dealing with `kho_scratch` being reserved via `memblock_alloc` during boot. The key is in those `FocusSymbols`. If `WorthFuzzing` were true, we *would* need to extract specific kernel functions.

The document states we should:
*   AVOID UBIQUITOUS LIFECYCLE HOT-PATHS
*   TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS
*   HANDLE STATIC INLINE FUNCTIONS IN HEADERS (.h)

Let's look at the symbols added/altered:
*   `memblock_reserved_size_nid` (static `__init_memblock`)
*   `scratch_size_update` (static `__init`)
*   `scratch_size_node` (static `__init`)
*   `memblock_reserved_hugetlb_size` (removed)

Right away, a red flag is up: none of these functions exist after boot! They're `__init` functions.  Syzkaller uses symbol coverage to track test execution, but there is no point in putting those symbols in FocusSymbols because syzkaller will never hit them.  If we considered including callers, like `kho_reserve_scratch` or `kho_memory_init` or `mm_core_init`, those functions are also `__init` functions.

My next line of thinking: Can Syzkaller even fuzz early boot code?  The answer is no. Syzkaller is a system call fuzzer, running in user space via `syz-executor`. It's fundamentally incapable of reaching code that executes *before* user space is up. The documentation is clear: unreachable execution paths should not be fuzzed. Early boot is unreachable. This is especially true for paths involving the `__init` functions executed during `start_kernel` / `mm_core_init` and discarded before user space initialization.

Let's do a quick double-check:
1.  The patch consolidates memblock reserved size calculation, removing `memblock_reserved_hugetlb_size` and introducing `memblock_reserved_size_nid()` in `kexec_handover.c`. `memblock_reserved_size_nid()` handles both `MEMBLOCK_RSRV_KERN` and `MEMBLOCK_RSRV_HUGETLB`. Calls to `memblock_reserved_kern_size` and `memblock_reserved_hugetlb_size` are replaced with `memblock_reserved_size_nid`.
2.  Are functions annotated? Yes, `memblock_reserved_size_nid` is `__init_memblock`, `scratch_size_update` is `__init`, and `scratch_size_node` is `__init`.  Callers `kho_reserve_scratch`, `kho_memory_init`, and `mm_core_init` are all `__init` as well.
3.  Are these functions called at runtime? Nope.  All callers are strictly in early boot: `start_kernel` -> `mm_core_init` -> `kho_memory_init` -> `kho_reserve_scratch` -> `scratch_size_update` / `scratch_size_node`. Memblock itself is freed *immediately* after `kho_memory_init()` in `mm_core_init()`, before slab and user processes exist.  All `__init` text and data is freed before user space starts (`free_initmem()`).
4.  Therefore, the code is boot-time initialization code (`__init` / memblock setup) that executes once and is discarded. Syzkaller can't reach it.

Hence, `WorthFuzzing` must be `false`.

The call to `set-results` should be:
*   `WorthFuzzing`: false
*   `Reasoning`: The patch modifies early boot memory initialization logic for kexec handover scratch size calculation (`scratch_size_update`, `scratch_size_node`, and `memblock_reserved_size_nid`). All modified and added functions are marked `__init` / `__init_memblock` and are called exclusively during early boot (`mm_core_init()`) before user space begins and before memblock memory is released to the buddy allocator (`memblock_free_all()`). Because this code executes strictly during kernel boot and is discarded prior to runtime, it is structurally unreachable by syzkaller syscall fuzzing.
*   `FocusSymbols`: []
*   


30/2 2026/09/24 22:08 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch refactors the calculation of kexec handover (KHO) scratch sizes by introducing a static helper memblock_reserved_size_nid() in kernel/liveupdate/kexec_handover.c and removing memblock_reserved_hugetlb_size() from mm/memblock.c. All modified and added functions (memblock_reserved_size_nid, scratch_size_update, scratch_size_node) are annotated with __init / __init_memblock and are executed strictly during early boot via mm_core_init() before userspace starts and before memblock memory is discarded (memblock_free_all()). Because this boot-time code is structurally unreachable at runtime by syzkaller syscall fuzzing, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch refactors the calculation of kexec handover (KHO) scratch sizes by introducing a static helper memblock_reserved_size_nid() in kernel/liveupdate/kexec_handover.c and removing memblock_reserved_hugetlb_size() from mm/memblock.c. All modified and added functions (memblock_reserved_size_nid, scratch_size_update, scratch_size_node) are annotated with __init / __init_memblock and are executed strictly during early boot via mm_core_init() before userspace starts and before memblock memory is discarded (memblock_free_all()). Because this boot-time code is structurally unreachable at runtime by syzkaller syscall fuzzing, it does not warrant fuzzing.",
  "WorthFuzzing": false
}

31/1 2026/09/24 22:08 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

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)