AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "MEMORY_HOTPLUG",
    "SPARSEMEM_VMEMMAP"
  ],
  "FocusSymbols": [
    "memory_block_online",
    "memory_block_offline",
    "find_smallest_section_pfn",
    "find_biggest_section_pfn",
    "remove_pfn_range_from_zone",
    "move_pfn_range_to_zone",
    "update_zone_online_memmap_pages",
    "page_alloc_init_late",
    "adjust_present_page_count"
  ],
  "Reasoning": "The patch modifies core memory management and memory hotplug logic, specifically zone contiguity tracking and section PFN finding. These paths can be reached during memory hotplug operations, which can be triggered in virtualized environments.",
  "WorthFuzzing": true
}

1/1 2026/07/24 13:26 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit ca363a12f438c6552038a6517a4ef2248c64c449\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Jul 24 13:26:05 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/Documentation/mm/physical_memory.rst b/Documentation/mm/physical_memory.rst\nindex b76183545e5bf5..0aa65e6b549957 100644\n--- a/Documentation/mm/physical_memory.rst\n+++ b/Documentation/mm/physical_memory.rst\n@@ -483,6 +483,19 @@ General\n   ``present_pages`` should use ``get_online_mems()`` to get a stable value. It\n   is initialized by ``calculate_node_totalpages()``.\n \n+``pages_with_online_memmap``\n+  Tracks pages within the zone that have an online memory map (present pages\n+  and memory holes whose memory map has been initialized). When\n+  ``spanned_pages`` == ``pages_with_online_memmap``, ``pfn_to_page()`` can be\n+  performed without further checks on any PFN within the zone span.\n+\n+  Note: this counter may temporarily undercount when pages with an online\n+  memory map exist outside the current zone span. This can only happen during\n+  boot, when initializing the memory map of pages that do not fall into any\n+  zone span. Growing the zone to cover such pages and later shrinking it back\n+  may result in a \"too small\" value. This is safe: it merely prevents\n+  detecting a contiguous zone.\n+\n ``present_early_pages``\n   The present pages existing within the zone located on memory available since\n   early boot, excluding hotplugged memory. Defined only when\ndiff --git a/drivers/base/memory.c b/drivers/base/memory.c\nindex bcfe2d9f4adbd9..237ace4353724c 100644\n--- a/drivers/base/memory.c\n+++ b/drivers/base/memory.c\n@@ -246,6 +246,7 @@ static int memory_block_online(struct memory_block *mem)\n \t\tnr_vmemmap_pages = mem-\u003ealtmap-\u003efree;\n \n \tmem_hotplug_begin();\n+\tclear_zone_contiguous(zone);\n \tif (nr_vmemmap_pages) {\n \t\tret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);\n \t\tif (ret)\n@@ -270,6 +271,7 @@ static int memory_block_online(struct memory_block *mem)\n \n \tmem-\u003ezone = zone;\n out:\n+\tset_zone_contiguous(zone);\n \tmem_hotplug_done();\n \treturn ret;\n }\n@@ -282,6 +284,7 @@ static int memory_block_offline(struct memory_block *mem)\n \tunsigned long start_pfn = section_nr_to_pfn(mem-\u003estart_section_nr);\n \tunsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;\n \tunsigned long nr_vmemmap_pages = 0;\n+\tstruct zone *zone;\n \tint ret;\n \n \tif (!mem-\u003ezone)\n@@ -294,7 +297,9 @@ static int memory_block_offline(struct memory_block *mem)\n \tif (mem-\u003ealtmap)\n \t\tnr_vmemmap_pages = mem-\u003ealtmap-\u003efree;\n \n+\tzone = mem-\u003ezone;\n \tmem_hotplug_begin();\n+\tclear_zone_contiguous(zone);\n \tif (nr_vmemmap_pages)\n \t\tadjust_present_page_count(pfn_to_page(start_pfn), mem-\u003egroup,\n \t\t\t\t\t  -nr_vmemmap_pages);\n@@ -314,6 +319,7 @@ static int memory_block_offline(struct memory_block *mem)\n \n \tmem-\u003ezone = NULL;\n out:\n+\tset_zone_contiguous(zone);\n \tmem_hotplug_done();\n \treturn ret;\n }\ndiff --git a/include/linux/mmzone.h b/include/linux/mmzone.h\nindex ca27121871475c..ac1467ac33364a 100644\n--- a/include/linux/mmzone.h\n+++ b/include/linux/mmzone.h\n@@ -1032,6 +1032,20 @@ struct zone {\n \t * cma pages is present pages that are assigned for CMA use\n \t * (MIGRATE_CMA).\n \t *\n+\t * pages_with_online_memmap tracks pages within the zone that have\n+\t * an online memory map (present pages and memory holes whose memory\n+\t * map has been initialized). When spanned_pages ==\n+\t * pages_with_online_memmap, pfn_to_page() can be performed without\n+\t * further checks on any PFN within the zone span.\n+\t *\n+\t * Note: this counter may temporarily undercount when pages with an\n+\t * online memory map exist outside the current zone span. This can\n+\t * only happen during boot, when initializing the memory map of\n+\t * pages that do not fall into any zone span. Growing the zone to\n+\t * cover such pages and later shrinking it back may result in a\n+\t * \"too small\" value. This is safe: it merely prevents detecting a\n+\t * contiguous zone.\n+\t *\n \t * So present_pages may be used by memory hotplug or memory power\n \t * management logic to figure out unmanaged pages by checking\n \t * (present_pages - managed_pages). And managed_pages should be used\n@@ -1056,6 +1070,7 @@ struct zone {\n \tatomic_long_t\t\tmanaged_pages;\n \tunsigned long\t\tspanned_pages;\n \tunsigned long\t\tpresent_pages;\n+\tunsigned long\t\tpages_with_online_memmap;\n #if defined(CONFIG_MEMORY_HOTPLUG)\n \tunsigned long\t\tpresent_early_pages;\n #endif\n@@ -1681,6 +1696,38 @@ static inline bool zone_is_zone_device(const struct zone *zone)\n }\n #endif\n \n+/**\n+ * zone_is_contiguous - test whether a zone is contiguous\n+ * @zone: the zone to test.\n+ *\n+ * In a contiguous zone, it is valid to call pfn_to_page() on any PFN in the\n+ * spanned zone without requiring pfn_valid() or pfn_to_online_page() checks.\n+ *\n+ * Note that missing synchronization with memory offlining makes any PFN\n+ * traversal prone to races.\n+ *\n+ * ZONE_DEVICE zones are always marked non-contiguous.\n+ *\n+ * Return: true if contiguous, otherwise false.\n+ */\n+static inline bool zone_is_contiguous(const struct zone *zone)\n+{\n+\treturn zone-\u003econtiguous;\n+}\n+\n+static inline void set_zone_contiguous(struct zone *zone)\n+{\n+\tif (zone_is_zone_device(zone))\n+\t\treturn;\n+\tif (zone-\u003espanned_pages == zone-\u003epages_with_online_memmap)\n+\t\tzone-\u003econtiguous = true;\n+}\n+\n+static inline void clear_zone_contiguous(struct zone *zone)\n+{\n+\tzone-\u003econtiguous = false;\n+}\n+\n /*\n  * Returns true if a zone has pages managed by the buddy allocator.\n  * All the reclaim decisions have to use this function rather than\ndiff --git a/mm/internal.h b/mm/internal.h\nindex 181e79f1d6a207..f932b7577c92ac 100644\n--- a/mm/internal.h\n+++ b/mm/internal.h\n@@ -805,21 +805,15 @@ extern struct page *__pageblock_pfn_to_page(unsigned long start_pfn,\n static inline struct page *pageblock_pfn_to_page(unsigned long start_pfn,\n \t\t\t\tunsigned long end_pfn, struct zone *zone)\n {\n-\tif (zone-\u003econtiguous)\n+\tif (zone_is_contiguous(zone))\n \t\treturn pfn_to_page(start_pfn);\n \n \treturn __pageblock_pfn_to_page(start_pfn, end_pfn, zone);\n }\n \n-void set_zone_contiguous(struct zone *zone);\n bool pfn_range_intersects_zones(int nid, unsigned long start_pfn,\n \t\t\t   unsigned long nr_pages);\n \n-static inline void clear_zone_contiguous(struct zone *zone)\n-{\n-\tzone-\u003econtiguous = false;\n-}\n-\n extern int __isolate_free_page(struct page *page, unsigned int order);\n extern void __putback_isolated_page(struct page *page, unsigned int order,\n \t\t\t\t    int mt);\ndiff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c\nindex 7ac19fab226324..3a281d59520710 100644\n--- a/mm/memory_hotplug.c\n+++ b/mm/memory_hotplug.c\n@@ -427,17 +427,24 @@ static unsigned long find_smallest_section_pfn(int nid, struct zone *zone,\n \t\t\t\t     unsigned long start_pfn,\n \t\t\t\t     unsigned long end_pfn)\n {\n-\tfor (; start_pfn \u003c end_pfn; start_pfn += PAGES_PER_SUBSECTION) {\n-\t\tif (unlikely(!pfn_to_online_page(start_pfn)))\n-\t\t\tcontinue;\n+\tunsigned long next_pfn;\n \n-\t\tif (unlikely(pfn_to_nid(start_pfn) != nid))\n-\t\t\tcontinue;\n+\tfor (; start_pfn \u003c end_pfn; start_pfn = next_pfn) {\n+\t\tunsigned long tail_pfn;\n+\n+\t\tnext_pfn = start_pfn + PAGES_PER_SUBSECTION;\n+\t\ttail_pfn = next_pfn - 1;\n \n-\t\tif (zone != page_zone(pfn_to_page(start_pfn)))\n+\t\tif (unlikely(!pfn_to_online_page(start_pfn)))\n \t\t\tcontinue;\n \n-\t\treturn start_pfn;\n+\t\tif (likely(pfn_to_nid(start_pfn) == nid) \u0026\u0026\n+\t\t    zone == page_zone(pfn_to_page(start_pfn)))\n+\t\t\treturn start_pfn;\n+\n+\t\tif (likely(pfn_to_nid(tail_pfn) == nid) \u0026\u0026\n+\t\t    zone == page_zone(pfn_to_page(tail_pfn)))\n+\t\t\treturn start_pfn;\n \t}\n \n \treturn 0;\n@@ -448,21 +455,26 @@ static unsigned long find_biggest_section_pfn(int nid, struct zone *zone,\n \t\t\t\t    unsigned long start_pfn,\n \t\t\t\t    unsigned long end_pfn)\n {\n-\tunsigned long pfn;\n+\tunsigned long pfn, prev_pfn;\n \n \t/* pfn is the end pfn of a memory section. */\n \tpfn = end_pfn - 1;\n-\tfor (; pfn \u003e= start_pfn; pfn -= PAGES_PER_SUBSECTION) {\n-\t\tif (unlikely(!pfn_to_online_page(pfn)))\n-\t\t\tcontinue;\n+\tfor (; pfn \u003e= start_pfn; pfn = prev_pfn) {\n+\t\tunsigned long head_pfn;\n \n-\t\tif (unlikely(pfn_to_nid(pfn) != nid))\n-\t\t\tcontinue;\n+\t\tprev_pfn = pfn - PAGES_PER_SUBSECTION;\n+\t\thead_pfn = prev_pfn + 1;\n \n-\t\tif (zone != page_zone(pfn_to_page(pfn)))\n+\t\tif (unlikely(!pfn_to_online_page(pfn)))\n \t\t\tcontinue;\n \n-\t\treturn pfn;\n+\t\tif (likely(pfn_to_nid(pfn) == nid) \u0026\u0026\n+\t\t    zone == page_zone(pfn_to_page(pfn)))\n+\t\t\treturn pfn;\n+\n+\t\tif (likely(pfn_to_nid(head_pfn) == nid) \u0026\u0026\n+\t\t    zone == page_zone(pfn_to_page(head_pfn)))\n+\t\t\treturn pfn;\n \t}\n \n \treturn 0;\n@@ -557,18 +569,13 @@ void remove_pfn_range_from_zone(struct zone *zone,\n \n \t/*\n \t * Zone shrinking code cannot properly deal with ZONE_DEVICE. So\n-\t * we will not try to shrink the zones - which is okay as\n-\t * set_zone_contiguous() cannot deal with ZONE_DEVICE either way.\n+\t * we will not try to shrink it.\n \t */\n \tif (zone_is_zone_device(zone))\n \t\treturn;\n \n-\tclear_zone_contiguous(zone);\n-\n \tshrink_zone_span(zone, start_pfn, start_pfn + nr_pages);\n \tupdate_pgdat_span(pgdat);\n-\n-\tset_zone_contiguous(zone);\n }\n \n /**\n@@ -746,8 +753,6 @@ void move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,\n \tstruct pglist_data *pgdat = zone-\u003ezone_pgdat;\n \tint nid = pgdat-\u003enode_id;\n \n-\tclear_zone_contiguous(zone);\n-\n \tif (zone_is_empty(zone))\n \t\tinit_currently_empty_zone(zone, start_pfn, nr_pages);\n \tresize_zone_range(zone, start_pfn, nr_pages);\n@@ -775,8 +780,6 @@ void move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,\n \tmemmap_init_range(nr_pages, nid, zone_idx(zone), start_pfn, 0,\n \t\t\t MEMINIT_HOTPLUG, altmap, migratetype,\n \t\t\t isolate_pageblock);\n-\n-\tset_zone_contiguous(zone);\n }\n \n struct auto_movable_stats {\n@@ -1072,6 +1075,7 @@ void adjust_present_page_count(struct page *page, struct memory_group *group,\n \tif (early_section(__pfn_to_section(page_to_pfn(page))))\n \t\tzone-\u003epresent_early_pages += nr_pages;\n \tzone-\u003epresent_pages += nr_pages;\n+\tzone-\u003epages_with_online_memmap += nr_pages;\n \tzone-\u003ezone_pgdat-\u003enode_present_pages += nr_pages;\n \n \tif (group \u0026\u0026 movable)\ndiff --git a/mm/mm_init.c b/mm/mm_init.c\nindex f1afe023e4e721..73d1fde1704050 100644\n--- a/mm/mm_init.c\n+++ b/mm/mm_init.c\n@@ -916,6 +916,40 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone\n \t}\n }\n \n+static void __init update_zone_online_memmap_pages(struct zone *zone,\n+\t\t\t\t\t\t   unsigned long start_pfn,\n+\t\t\t\t\t\t   unsigned long end_pfn,\n+\t\t\t\t\t\t   unsigned long *hole_pfn)\n+{\n+#ifdef CONFIG_SPARSEMEM_VMEMMAP\n+\tunsigned long zone_start_pfn = zone-\u003ezone_start_pfn;\n+\tunsigned long zone_end_pfn = zone_start_pfn + zone-\u003espanned_pages;\n+\tunsigned long sub_start, sub_end;\n+\n+\tsub_start = max(ALIGN_DOWN(start_pfn, PAGES_PER_SUBSECTION),\n+\t\t\tzone_start_pfn);\n+\tsub_end = min(ALIGN(end_pfn, PAGES_PER_SUBSECTION), zone_end_pfn);\n+\n+\t/*\n+\t * For an in-zone hole smaller than a subsection, the adjacent\n+\t * memblock ranges share a subsection. Avoid counting the same\n+\t * subsection's pages twice.\n+\t */\n+\tif (*hole_pfn \u003e zone_start_pfn) {\n+\t\tunsigned long prev_sub_end;\n+\n+\t\tprev_sub_end = min(ALIGN(*hole_pfn, PAGES_PER_SUBSECTION),\n+\t\t\t\t   zone_end_pfn);\n+\t\tsub_start = max(sub_start, prev_sub_end);\n+\t}\n+\n+\tif (sub_start \u003c sub_end)\n+\t\tzone-\u003epages_with_online_memmap += sub_end - sub_start;\n+#else\n+\tzone-\u003epages_with_online_memmap += end_pfn - start_pfn;\n+#endif\n+}\n+\n static void __init memmap_init_zone_range(struct zone *zone,\n \t\t\t\t\t  unsigned long start_pfn,\n \t\t\t\t\t  unsigned long end_pfn,\n@@ -932,6 +966,8 @@ static void __init memmap_init_zone_range(struct zone *zone,\n \tif (start_pfn \u003e= end_pfn)\n \t\treturn;\n \n+\tupdate_zone_online_memmap_pages(zone, start_pfn, end_pfn, hole_pfn);\n+\n \tmemmap_init_range(end_pfn - start_pfn, nid, zone_id, start_pfn,\n \t\t\t  zone_end_pfn, MEMINIT_EARLY, NULL, mt, false);\n \n@@ -2195,28 +2231,6 @@ void __init init_cma_pageblock(struct page *page)\n }\n #endif\n \n-void set_zone_contiguous(struct zone *zone)\n-{\n-\tunsigned long block_start_pfn = zone-\u003ezone_start_pfn;\n-\tunsigned long block_end_pfn;\n-\n-\tblock_end_pfn = pageblock_end_pfn(block_start_pfn);\n-\tfor (; block_start_pfn \u003c zone_end_pfn(zone);\n-\t\t\tblock_start_pfn = block_end_pfn,\n-\t\t\t block_end_pfn += pageblock_nr_pages) {\n-\n-\t\tblock_end_pfn = min(block_end_pfn, zone_end_pfn(zone));\n-\n-\t\tif (!__pageblock_pfn_to_page(block_start_pfn,\n-\t\t\t\t\t     block_end_pfn, zone))\n-\t\t\treturn;\n-\t\tcond_resched();\n-\t}\n-\n-\t/* We confirm that there is no hole */\n-\tzone-\u003econtiguous = true;\n-}\n-\n /*\n  * Check if a PFN range intersects multiple zones on one or more\n  * NUMA nodes. Specify the @nid argument if it is known that this\n"
}

2/1 2026/07/24 13:26 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "MEMORY_HOTPLUG",
    "SPARSEMEM_VMEMMAP"
  ],
  "FocusSymbols": [
    "memory_block_online",
    "memory_block_offline",
    "find_smallest_section_pfn",
    "find_biggest_section_pfn",
    "remove_pfn_range_from_zone",
    "move_pfn_range_to_zone",
    "update_zone_online_memmap_pages",
    "page_alloc_init_late",
    "adjust_present_page_count"
  ],
  "Reasoning": "The patch modifies core memory management and memory hotplug logic, specifically zone contiguity tracking and section PFN finding. These paths can be reached during memory hotplug operations, which can be triggered in virtualized environments.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit ca363a12f438c6552038a6517a4ef2248c64c449
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Jul 24 13:26:05 2026 +0000

    syz-cluster: applied patch under review

diff --git a/Documentation/mm/physical_memory.rst b/Documentation/mm/physical_memory.rst
index b76183545e5bf5..0aa65e6b549957 100644
--- a/Documentation/mm/physical_memory.rst
+++ b/Documentation/mm/physical_memory.rst
@@ -483,6 +483,19 @@ General
   ``present_pages`` should use ``get_online_mems()`` to get a stable value. It
   is initialized by ``calculate_node_totalpages()``.
 
+``pages_with_online_memmap``
+  Tracks pages within the zone that have an online memory map (present pages
+  and memory holes whose memory map has been initialized). When
+  ``spanned_pages`` == ``pages_with_online_memmap``, ``pfn_to_page()`` can be
+  performed without further checks on any PFN within the zone span.
+
+  Note: this counter may temporarily undercount when pages with an online
+  memory map exist outside the current zone span. This can only happen during
+  boot, when initializing the memory map of pages that do not fall into any
+  zone span. Growing the zone to cover such pages and later shrinking it back
+  may result in a "too small" value. This is safe: it merely prevents
+  detecting a contiguous zone.
+
 ``present_early_pages``
   The present pages existing within the zone located on memory available since
   early boot, excluding hotplugged memory. Defined only when
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index bcfe2d9f4adbd9..237ace4353724c 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -246,6 +246,7 @@ static int memory_block_online(struct memory_block *mem)
 		nr_vmemmap_pages = mem->altmap->free;
 
 	mem_hotplug_begin();
+	clear_zone_contiguous(zone);
 	if (nr_vmemmap_pages) {
 		ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);
 		if (ret)
@@ -270,6 +271,7 @@ static int memory_block_online(struct memory_block *mem)
 
 	mem->zone = zone;
 out:
+	set_zone_contiguous(zone);
 	mem_hotplug_done();
 	return ret;
 }
@@ -282,6 +284,7 @@ static int memory_block_offline(struct memory_block *mem)
 	unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
 	unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
 	unsigned long nr_vmemmap_pages = 0;
+	struct zone *zone;
 	int ret;
 
 	if (!mem->zone)
@@ -294,7 +297,9 @@ static int memory_block_offline(struct memory_block *mem)
 	if (mem->altmap)
 		nr_vmemmap_pages = mem->altmap->free;
 
+	zone = mem->zone;
 	mem_hotplug_begin();
+	clear_zone_contiguous(zone);
 	if (nr_vmemmap_pages)
 		adjust_present_page_count(pfn_to_page(start_pfn), mem->group,
 					  -nr_vmemmap_pages);
@@ -314,6 +319,7 @@ static int memory_block_offline(struct memory_block *mem)
 
 	mem->zone = NULL;
 out:
+	set_zone_contiguous(zone);
 	mem_hotplug_done();
 	return ret;
 }
diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h
index ca27121871475c..ac1467ac33364a 100644
--- a/include/linux/mmzone.h
+++ b/include/linux/mmzone.h
@@ -1032,6 +1032,20 @@ struct zone {
 	 * cma pages is present pages that are assigned for CMA use
 	 * (MIGRATE_CMA).
 	 *
+	 * pages_with_online_memmap tracks pages within the zone that have
+	 * an online memory map (present pages and memory holes whose memory
+	 * map has been initialized). When spanned_pages ==
+	 * pages_with_online_memmap, pfn_to_page() can be performed without
+	 * further checks on any PFN within the zone span.
+	 *
+	 * Note: this counter may temporarily undercount when pages with an
+	 * online memory map exist outside the current zone span. This can
+	 * only happen during boot, when initializing the memory map of
+	 * pages that do not fall into any zone span. Growing the zone to
+	 * cover such pages and later shrinking it back may result in a
+	 * "too small" value. This is safe: it merely prevents detecting a
+	 * contiguous zone.
+	 *
 	 * So present_pages may be used by memory hotplug or memory power
 	 * management logic to figure out unmanaged pages by checking
 	 * (present_pages - managed_pages). And managed_pages should be used
@@ -1056,6 +1070,7 @@ struct zone {
 	atomic_long_t		managed_pages;
 	unsigned long		spanned_pages;
 	unsigned long		present_pages;
+	unsigned long		pages_with_online_memmap;
 #if defined(CONFIG_MEMORY_HOTPLUG)
 	unsigned long		present_early_pages;
 #endif
@@ -1681,6 +1696,38 @@ static inline bool zone_is_zone_device(const struct zone *zone)
 }
 #endif
 
+/**
+ * zone_is_contiguous - test whether a zone is contiguous
+ * @zone: the zone to test.
+ *
+ * In a contiguous zone, it is valid to call pfn_to_page() on any PFN in the
+ * spanned zone without requiring pfn_valid() or pfn_to_online_page() checks.
+ *
+ * Note that missing synchronization with memory offlining makes any PFN
+ * traversal prone to races.
+ *
+ * ZONE_DEVICE zones are always marked non-contiguous.
+ *
+ * Return: true if contiguous, otherwise false.
+ */
+static inline bool zone_is_contiguous(const struct zone *zone)
+{
+	return zone->contiguous;
+}
+
+static inline void set_zone_contiguous(struct zone *zone)
+{
+	if (zone_is_zone_device(zone))
+		return;
+	if (zone->spanned_pages == zone->pages_with_online_memmap)
+		zone->contiguous = true;
+}
+
+static inline void clear_zone_contiguous(struct zone *zone)
+{
+	zone->contiguous = false;
+}
+
 /*
  * Returns true if a zone has pages managed by the buddy allocator.
  * All the reclaim decisions have to use this function rather than
diff --git a/mm/internal.h b/mm/internal.h
index 181e79f1d6a207..f932b7577c92ac 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -805,21 +805,15 @@ extern struct page *__pageblock_pfn_to_page(unsigned long start_pfn,
 static inline struct page *pageblock_pfn_to_page(unsigned long start_pfn,
 				unsigned long end_pfn, struct zone *zone)
 {
-	if (zone->contiguous)
+	if (zone_is_contiguous(zone))
 		return pfn_to_page(start_pfn);
 
 	return __pageblock_pfn_to_page(start_pfn, end_pfn, zone);
 }
 
-void set_zone_contiguous(struct zone *zone);
 bool pfn_range_intersects_zones(int nid, unsigned long start_pfn,
 			   unsigned long nr_pages);
 
-static inline void clear_zone_contiguous(struct zone *zone)
-{
-	zone->contiguous = false;
-}
-
 extern int __isolate_free_page(struct page *page, unsigned int order);
 extern void __putback_isolated_page(struct page *page, unsigned int order,
 				    int mt);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 7ac19fab226324..3a281d59520710 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -427,17 +427,24 @@ static unsigned long find_smallest_section_pfn(int nid, struct zone *zone,
 				     unsigned long start_pfn,
 				     unsigned long end_pfn)
 {
-	for (; start_pfn < end_pfn; start_pfn += PAGES_PER_SUBSECTION) {
-		if (unlikely(!pfn_to_online_page(start_pfn)))
-			continue;
+	unsigned long next_pfn;
 
-		if (unlikely(pfn_to_nid(start_pfn) != nid))
-			continue;
+	for (; start_pfn < end_pfn; start_pfn = next_pfn) {
+		unsigned long tail_pfn;
+
+		next_pfn = start_pfn + PAGES_PER_SUBSECTION;
+		tail_pfn = next_pfn - 1;
 
-		if (zone != page_zone(pfn_to_page(start_pfn)))
+		if (unlikely(!pfn_to_online_page(start_pfn)))
 			continue;
 
-		return start_pfn;
+		if (likely(pfn_to_nid(start_pfn) == nid) &&
+		    zone == page_zone(pfn_to_page(start_pfn)))
+			return start_pfn;
+
+		if (likely(pfn_to_nid(tail_pfn) == nid) &&
+		    zone == page_zone(pfn_to_page(tail_pfn)))
+			return start_pfn;
 	}
 
 	return 0;
@@ -448,21 +455,26 @@ static unsigned long find_biggest_section_pfn(int nid, struct zone *zone,
 				    unsigned long start_pfn,
 				    unsigned long end_pfn)
 {
-	unsigned long pfn;
+	unsigned long pfn, prev_pfn;
 
 	/* pfn is the end pfn of a memory section. */
 	pfn = end_pfn - 1;
-	for (; pfn >= start_pfn; pfn -= PAGES_PER_SUBSECTION) {
-		if (unlikely(!pfn_to_online_page(pfn)))
-			continue;
+	for (; pfn >= start_pfn; pfn = prev_pfn) {
+		unsigned long head_pfn;
 
-		if (unlikely(pfn_to_nid(pfn) != nid))
-			continue;
+		prev_pfn = pfn - PAGES_PER_SUBSECTION;
+		head_pfn = prev_pfn + 1;
 
-		if (zone != page_zone(pfn_to_page(pfn)))
+		if (unlikely(!pfn_to_online_page(pfn)))
 			continue;
 
-		return pfn;
+		if (likely(pfn_to_nid(pfn) == nid) &&
+		    zone == page_zone(pfn_to_page(pfn)))
+			return pfn;
+
+		if (likely(pfn_to_nid(head_pfn) == nid) &&
+		    zone == page_zone(pfn_to_page(head_pfn)))
+			return pfn;
 	}
 
 	return 0;
@@ -557,18 +569,13 @@ void remove_pfn_range_from_zone(struct zone *zone,
 
 	/*
 	 * Zone shrinking code cannot properly deal with ZONE_DEVICE. So
-	 * we will not try to shrink the zones - which is okay as
-	 * set_zone_contiguous() cannot deal with ZONE_DEVICE either way.
+	 * we will not try to shrink it.
 	 */
 	if (zone_is_zone_device(zone))
 		return;
 
-	clear_zone_contiguous(zone);
-
 	shrink_zone_span(zone, start_pfn, start_pfn + nr_pages);
 	update_pgdat_span(pgdat);
-
-	set_zone_contiguous(zone);
 }
 
 /**
@@ -746,8 +753,6 @@ void move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,
 	struct pglist_data *pgdat = zone->zone_pgdat;
 	int nid = pgdat->node_id;
 
-	clear_zone_contiguous(zone);
-
 	if (zone_is_empty(zone))
 		init_currently_empty_zone(zone, start_pfn, nr_pages);
 	resize_zone_range(zone, start_pfn, nr_pages);
@@ -775,8 +780,6 @@ void move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,
 	memmap_init_range(nr_pages, nid, zone_idx(zone), start_pfn, 0,
 			 MEMINIT_HOTPLUG, altmap, migratetype,
 			 isolate_pageblock);
-
-	set_zone_contiguous(zone);
 }
 
 struct auto_movable_stats {
@@ -1072,6 +1075,7 @@ void adjust_present_page_count(struct page *page, struct memory_group *group,
 	if (early_section(__pfn_to_section(page_to_pfn(page))))
 		zone->present_early_pages += nr_pages;
 	zone->present_pages += nr_pages;
+	zone->pages_with_online_memmap += nr_pages;
 	zone->zone_pgdat->node_present_pages += nr_pages;
 
 	if (group && movable)
diff --git a/mm/mm_init.c b/mm/mm_init.c
index f1afe023e4e721..73d1fde1704050 100644
--- a/mm/mm_init.c
+++ b/mm/mm_init.c
@@ -916,6 +916,40 @@ void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone
 	}
 }
 
+static void __init update_zone_online_memmap_pages(struct zone *zone,
+						   unsigned long start_pfn,
+						   unsigned long end_pfn,
+						   unsigned long *hole_pfn)
+{
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+	unsigned long zone_start_pfn = zone->zone_start_pfn;
+	unsigned long zone_end_pfn = zone_start_pfn + zone->spanned_pages;
+	unsigned long sub_start, sub_end;
+
+	sub_start = max(ALIGN_DOWN(start_pfn, PAGES_PER_SUBSECTION),
+			zone_start_pfn);
+	sub_end = min(ALIGN(end_pfn, PAGES_PER_SUBSECTION), zone_end_pfn);
+
+	/*
+	 * For an in-zone hole smaller than a subsection, the adjacent
+	 * memblock ranges share a subsection. Avoid counting the same
+	 * subsection's pages twice.
+	 */
+	if (*hole_pfn > zone_start_pfn) {
+		unsigned long prev_sub_end;
+
+		prev_sub_end = min(ALIGN(*hole_pfn, PAGES_PER_SUBSECTION),
+				   zone_end_pfn);
+		sub_start = max(sub_start, prev_sub_end);
+	}
+
+	if (sub_start < sub_end)
+		zone->pages_with_online_memmap += sub_end - sub_start;
+#else
+	zone->pages_with_online_memmap += end_pfn - start_pfn;
+#endif
+}
+
 static void __init memmap_init_zone_range(struct zone *zone,
 					  unsigned long start_pfn,
 					  unsigned long end_pfn,
@@ -932,6 +966,8 @@ static void __init memmap_init_zone_range(struct zone *zone,
 	if (start_pfn >= end_pfn)
 		return;
 
+	update_zone_online_memmap_pages(zone, start_pfn, end_pfn, hole_pfn);
+
 	memmap_init_range(end_pfn - start_pfn, nid, zone_id, start_pfn,
 			  zone_end_pfn, MEMINIT_EARLY, NULL, mt, false);
 
@@ -2195,28 +2231,6 @@ void __init init_cma_pageblock(struct page *page)
 }
 #endif
 
-void set_zone_contiguous(struct zone *zone)
-{
-	unsigned long block_start_pfn = zone->zone_start_pfn;
-	unsigned long block_end_pfn;
-
-	block_end_pfn = pageblock_end_pfn(block_start_pfn);
-	for (; block_start_pfn < zone_end_pfn(zone);
-			block_start_pfn = block_end_pfn,
-			 block_end_pfn += pageblock_nr_pages) {
-
-		block_end_pfn = min(block_end_pfn, zone_end_pfn(zone));
-
-		if (!__pageblock_pfn_to_page(block_start_pfn,
-					     block_end_pfn, zone))
-			return;
-		cond_resched();
-	}
-
-	/* We confirm that there is no hole */
-	zone->contiguous = true;
-}
-
 /*
  * Check if a PFN range intersects multiple zones on one or more
  * NUMA nodes. Specify the @nid argument if it is known that this

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

Args:
null

Results:
null

Tokens:
							input: 6432
							output: 29
							thoughts: 282
						

Thoughts:
**Analyzing a Kernel Memory Hotplug Patch**

Okay, so I'm looking at this patch. It's touching some pretty crucial stuff – memory hotplug and how the kernel tracks zone contiguity. Interesting. This patch introduces a new counter, `pages_with_online_memmap`, within the `struct zone`. I need to understand that. Basically it is going to replace the current way of verifying contiguous memory by comparing the total number of pages spanned by the zone against the number of pages with online memory maps.

Right, so they've changed how `set_zone_contiguous` operates. That's a fundamental shift, and I need to see if this is going to be faster, slower, and the implications. Instead of iterating through pageblocks to look for holes, it's just a simple comparison now. I need to think this through.

Okay, and it looks like they’ve optimized `find_smallest_section_pfn` and `find_biggest_section_pfn` in `mm/memory_hotplug.c` as well. Seems like it has some nice optimizations in there.

My immediate thought is, can we actually *reach* this code? Is it functional? I mean, it's poking at the core of memory management and hotplug, so I'd say yeah, it's definitely reachable. Memory hotplug isn't some obscure feature; you can trigger it in virtualized environments, right? Sysfs or ACPI, that’s going to trip all this. And the changes to those `find_...` functions? Totally functional during hotplug. And `set_zone_contiguous` too.

Now, what about fuzzing this? Worth the effort? Absolutely. Any change in this area can have nasty consequences if there are holes, so let’s see what we can do in that regard.

Let's look for the configuration options. Obvious candidate is `CONFIG_MEMORY_HOTPLUG`, right? Time to check `mm/memory_hotplug.c` and dig deeper.



4/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "CONFIG_MEMORY_HOTPLUG",
  "PathPrefix": "mm/"
}

Results:
{
  "Output": "mm/Makefile=62=page-alloc-$(CONFIG_SHUFFLE_PAGE_ALLOCATOR) += shuffle.o\n--\nmm/Makefile-64-# Give 'memory_hotplug' its own module-parameter namespace\nmm/Makefile:65:memory-hotplug-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o\nmm/Makefile-66-\n--\nmm/hugetlb_vmemmap.c=99=static int vmemmap_pmd_entry(pmd_t *pmd, unsigned long addr,\n--\nmm/hugetlb_vmemmap.c-125-\t */\nmm/hugetlb_vmemmap.c:126:\tif (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) \u0026\u0026 unlikely(!vmemmap_walk-\u003enr_walked)) {\nmm/hugetlb_vmemmap.c-127-\t\tstruct page *page = head ? head + pte_index(addr) :\n--\nmm/kasan/shadow.c=163=void kasan_unpoison(const void *addr, size_t size, bool init)\n--\nmm/kasan/shadow.c-184-\nmm/kasan/shadow.c:185:#ifdef CONFIG_MEMORY_HOTPLUG\nmm/kasan/shadow.c-186-static bool shadow_mapped(unsigned long addr)\n--\nmm/memory_hotplug.c=649=EXPORT_SYMBOL_GPL(restore_online_page_callback);\nmm/memory_hotplug.c-650-\nmm/memory_hotplug.c:651:/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */\nmm/memory_hotplug.c-652-void generic_online_page(struct page *page, unsigned int order)\n--\nmm/memory_hotplug.c=1141=int online_pages(unsigned long pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-1255-\nmm/memory_hotplug.c:1256:/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */\nmm/memory_hotplug.c-1257-static pg_data_t *hotadd_init_pgdat(int nid)\n--\nmm/memory_hotplug.c=1444=static int create_altmaps_and_memory_blocks(int nid, struct memory_group *group,\n--\nmm/memory_hotplug.c-1495- *\nmm/memory_hotplug.c:1496: * we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG\nmm/memory_hotplug.c-1497- */\n--\nmm/mm_init.c=1277=static void __init reset_memoryless_node_totalpages(struct pglist_data *pgdat)\n--\nmm/mm_init.c-1284-\t\tz-\u003epresent_pages = 0;\nmm/mm_init.c:1285:#if defined(CONFIG_MEMORY_HOTPLUG)\nmm/mm_init.c-1286-\t\tz-\u003epresent_early_pages = 0;\n--\nmm/mm_init.c=1319=static void __init calculate_node_totalpages(struct pglist_data *pgdat,\n--\nmm/mm_init.c-1348-\t\tzone-\u003epresent_pages = real_size;\nmm/mm_init.c:1349:#if defined(CONFIG_MEMORY_HOTPLUG)\nmm/mm_init.c-1350-\t\tzone-\u003epresent_early_pages = real_size;\n--\nmm/mm_init.c=1506=void __init set_pageblock_order(void)\n--\nmm/mm_init.c-1518- */\nmm/mm_init.c:1519:#ifdef CONFIG_MEMORY_HOTPLUG\nmm/mm_init.c-1520-void __ref free_area_init_core_hotplug(struct pglist_data *pgdat)\n--\nmm/page_alloc.c=1588=void __meminit __free_pages_core(struct page *page, unsigned int order,\n--\nmm/page_alloc.c-1602-\t */\nmm/page_alloc.c:1603:\tif (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) \u0026\u0026\nmm/page_alloc.c-1604-\t    unlikely(context == MEMINIT_HOTPLUG)) {\n--\nmm/sparse-vmemmap.c=604=void __init sparse_init_subsection_map(unsigned long pfn, unsigned long nr_pages)\n--\nmm/sparse-vmemmap.c-626-\nmm/sparse-vmemmap.c:627:#ifdef CONFIG_MEMORY_HOTPLUG\nmm/sparse-vmemmap.c-628-\n--\nmm/sparse-vmemmap.c=917=void sparse_remove_section(unsigned long pfn, unsigned long nr_pages,\n--\nmm/sparse-vmemmap.c-926-}\nmm/sparse-vmemmap.c:927:#endif /* CONFIG_MEMORY_HOTPLUG */\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 8474
							output: 31
							thoughts: 132
						

Thoughts:
**Analyzing the Memory Hotplug Patch**

Okay, so this patch is touching a good number of files – specifically, `mm/memory_hotplug.c`, `mm/mm_init.c`, `drivers/base/memory.c`, `include/linux/mmzone.h`, and `mm/internal.h`. Based on the file list, the core of this seems to be memory hotplug and the related zone contiguity tracking. That makes sense; keeping track of physical memory regions when you're adding or removing them on the fly is crucial for efficient resource management.

The primary configuration option to watch out for will be `CONFIG_MEMORY_HOTPLUG`, obviously. I'll need to keep an eye on how it's used throughout the changes.

I see that `CONFIG_SPARSEMEM_VMEMMAP` is also implicated, specifically within the `update_zone_online_memmap_pages` function in `mm/mm_init.c`. That tells me there might be interaction between this hotplug work and sparse memory configurations which is something I need to keep an eye out for.

Now, I'll focus on `drivers/base/memory.c` specifically, because I suspect it might contain some conditional logic or initialization based on specific configuration options. I'll have to delve into that to determine if there are any interesting conditionals I need to understand.



6/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "CONFIG_MEMORY_HOTPLUG",
  "PathPrefix": "drivers/base/"
}

Results:
{
  "Output": "drivers/base/Makefile=15=obj-$(CONFIG_NUMA)\t+= node.o\ndrivers/base/Makefile:16:obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o\ndrivers/base/Makefile-17-ifeq ($(CONFIG_SYSFS),y)\n--\ndrivers/base/memory.c=207=int memory_notify(enum memory_block_state state, void *v)\n--\ndrivers/base/memory.c-211-\ndrivers/base/memory.c:212:#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 defined(CONFIG_MEMORY_HOTPLUG)\ndrivers/base/memory.c-213-static unsigned long memblk_nr_poison(struct memory_block *mem);\n--\ndrivers/base/memory.c=1202=int walk_dynamic_memory_groups(int nid, walk_memory_groups_func_t func,\n--\ndrivers/base/memory.c-1223-\ndrivers/base/memory.c:1224:#if defined(CONFIG_MEMORY_FAILURE) \u0026\u0026 defined(CONFIG_MEMORY_HOTPLUG)\ndrivers/base/memory.c-1225-void memblk_nr_poison_inc(unsigned long pfn)\n--\ndrivers/base/node.c=109=static const struct attribute_group *node_access_node_groups[] = {\n--\ndrivers/base/node.c-114-\ndrivers/base/node.c:115:#ifdef CONFIG_MEMORY_HOTPLUG\ndrivers/base/node.c-116-static BLOCKING_NOTIFIER_HEAD(node_chain);\n--\ndrivers/base/node.c=764=int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)\n--\ndrivers/base/node.c-782-\ndrivers/base/node.c:783:#ifdef CONFIG_MEMORY_HOTPLUG\ndrivers/base/node.c-784-static void do_register_memory_block_under_node(int nid,\n--\ndrivers/base/node.c=864=void register_memory_blocks_under_node_hotplug(int nid, unsigned long start_pfn,\n--\ndrivers/base/node.c-870-}\ndrivers/base/node.c:871:#endif /* CONFIG_MEMORY_HOTPLUG */\ndrivers/base/node.c-872-\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 9522
							output: 32
							thoughts: 324
						

Thoughts:
**Analyzing Memory Hotplug Implementation**

Okay, so I'm diving into this memory hotplug situation, and the first thing that jumps out is `CONFIG_MEMORY_HOTPLUG`, obviously. But hold on, I see `CONFIG_SPARSEMEM_VMEMMAP` mentioned too. I need to figure out if that's a *hard* requirement or if it just changes the implementation.

Looking at the code in `mm/mm_init.c`, specifically the `update_zone_online_memmap_pages` function, it's clear that `CONFIG_SPARSEMEM_VMEMMAP` definitely affects the code path. Without it, we're doing a simple increment. With it... well, it's different. Not strictly *required*, it seems, but definitely changes the behavior significantly. I think we can include that without a problem.

Now, let's nail down which functions are actually the core of this. From what I see, I'll need to focus on `memory_block_online`, `memory_block_offline`, `find_smallest_section_pfn`, `find_biggest_section_pfn`, `remove_pfn_range_from_zone`, `move_pfn_range_to_zone`, `update_zone_online_memmap_pages` and `memmap_init_zone_range`. These seem like the key players for managing memory sections.

Time to see how those `memory_block_online` and `memory_block_offline` functions are being used. Are they exported, or are they static within their module? Let's check the source, which is located in `drivers/base/memory.c`. Yep, static. Okay, that means I need to figure out the callers now. Let's trace their usage and see how these functions are hooked into the overall memory management system.



8/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "memory_block_online",
  "PathPrefix": "drivers/base/memory.c"
}

Results:
{
  "Output": "drivers/base/memory.c=215=static inline unsigned long memblk_nr_poison(struct memory_block *mem)\n--\ndrivers/base/memory.c-223- */\ndrivers/base/memory.c:224:static int memory_block_online(struct memory_block *mem)\ndrivers/base/memory.c-225-{\n--\ndrivers/base/memory.c=332=memory_block_action(struct memory_block *mem, unsigned long action)\n--\ndrivers/base/memory.c-337-\tcase MEM_ONLINE:\ndrivers/base/memory.c:338:\t\tret = memory_block_online(mem);\ndrivers/base/memory.c-339-\t\tbreak;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 10472
							output: 36
							thoughts: 98
						

Thoughts:
Here's my analysis of the situation:

**Fuzzing the Memory Subsystem**

Okay, so `memory_block_action` is the function we're interested in.  It's triggered by `memory_subsys_online` and `memory_subsys_offline`.  Aha! And *those* functions are called when sysfs `/sys/devices/system/memory/memoryX/state` is written to.  That's my attack vector!  If I want to fuzz this properly, I need to focus on hammering `memory_block_online` and `memory_block_offline`.  That's where the interesting logic is.  This sounds like a good approach.

Now, let's also take a look at the `find_smallest_section_pfn` and `find_biggest_section_pfn` functions. These could be potential areas for interesting issues too, and I'd like to understand what role they play in the overall flow.



10/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "find_smallest_section_pfn",
  "PathPrefix": "mm/memory_hotplug.c"
}

Results:
{
  "Output": "mm/memory_hotplug.c=381=int __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-425-/* find the smallest valid pfn in the range [start_pfn, end_pfn) */\nmm/memory_hotplug.c:426:static unsigned long find_smallest_section_pfn(int nid, struct zone *zone,\nmm/memory_hotplug.c-427-\t\t\t\t     unsigned long start_pfn,\n--\nmm/memory_hotplug.c=483=static void shrink_zone_span(struct zone *zone, unsigned long start_pfn,\n--\nmm/memory_hotplug.c-495-\t\t */\nmm/memory_hotplug.c:496:\t\tpfn = find_smallest_section_pfn(nid, zone, end_pfn,\nmm/memory_hotplug.c-497-\t\t\t\t\t\tzone_end_pfn(zone));\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 11073
							output: 38
							thoughts: 109
						

Thoughts:
**Tracing the Memory Hotplug Path**

Okay, so I'm diving into this memory hotplug functionality, specifically looking at how we're removing page frame numbers (PFNs) from a zone. My immediate focus is on the static functions within `mm/memory_hotplug.c` that get invoked during this process. I've traced the call stack, so I know these static functions are triggered by `shrink_zone_span`. That in turn is called by `remove_pfn_range_from_zone`. Diving deeper, I see that `remove_pfn_range_from_zone` is a direct caller of `__remove_pages`, which is a key function in this process. And finally, `__remove_pages` is the direct parent of `offline_pages` and `arch_remove_memory`, which are the primary functions for removing memory.

Now, let's turn our attention to `move_pfn_range_to_zone`. I need to understand how we're reallocating the memory that has been removed.



12/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "move_pfn_range_to_zone",
  "PathPrefix": "mm/memory_hotplug.c"
}

Results:
{
  "Output": "mm/memory_hotplug.c=734=static inline void section_taint_zone_device(unsigned long pfn)\n--\nmm/memory_hotplug.c-747- */\nmm/memory_hotplug.c:748:void move_pfn_range_to_zone(struct zone *zone, unsigned long start_pfn,\nmm/memory_hotplug.c-749-\t\t\t\t  unsigned long nr_pages,\n--\nmm/memory_hotplug.c=1087=int mhp_init_memmap_on_memory(unsigned long pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-1096-\nmm/memory_hotplug.c:1097:\tmove_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_UNMOVABLE,\nmm/memory_hotplug.c-1098-\t\t\t       false);\n--\nmm/memory_hotplug.c=1141=int online_pages(unsigned long pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-1168-\t/* associate pfn range with the zone */\nmm/memory_hotplug.c:1169:\tmove_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_MOVABLE,\nmm/memory_hotplug.c-1170-\t\t\t       true);\n--\nmm/memory_hotplug.c=2189=void try_offline_node(int nid)\n--\nmm/memory_hotplug.c-2194-\t * If the node still spans pages (especially ZONE_DEVICE), don't\nmm/memory_hotplug.c:2195:\t * offline it. A node spans memory after move_pfn_range_to_zone(),\nmm/memory_hotplug.c-2196-\t * e.g., after the memory block was onlined.\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 11936
							output: 37
							thoughts: 147
						

Thoughts:
**Analyzing Memory Management Function Calls**

Okay, so I'm digging into how the memory management system is working here. It looks like `move_pfn_range_to_zone` is a key function, and it's being called from two main places: `mhp_init_memmap_on_memory` and `online_pages`. That tells me these are the points where we're shifting around physical page frames (PFNs) between memory zones.

To really understand what's going on, I need to zoom in on the surrounding code. Specifically, the related functions are the ones I should check, given the call hierarchy of `move_pfn_range_to_zone`. These will be crucial for understanding how memory blocks are brought online and offline, and how PFN ranges are identified and manipulated:

-   `memory_block_online`
-   `memory_block_offline`
-   `find_smallest_section_pfn`
-   `find_biggest_section_pfn`
-   `remove_pfn_range_from_zone`
-   `move_pfn_range_to_zone`
-   `update_zone_online_memmap_pages`

I think it makes sense to confirm if `update_zone_online_memmap_pages` is a static function. Knowing if it is static will help with the scope analysis.



14/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "update_zone_online_memmap_pages",
  "PathPrefix": "mm/mm_init.c"
}

Results:
{
  "Output": "mm/mm_init.c=852=void __meminit memmap_init_range(unsigned long size, int nid, unsigned long zone,\n--\nmm/mm_init.c-918-\nmm/mm_init.c:919:static void __init update_zone_online_memmap_pages(struct zone *zone,\nmm/mm_init.c-920-\t\t\t\t\t\t   unsigned long start_pfn,\n--\nmm/mm_init.c=953=static void __init memmap_init_zone_range(struct zone *zone,\n--\nmm/mm_init.c-968-\nmm/mm_init.c:969:\tupdate_zone_online_memmap_pages(zone, start_pfn, end_pfn, hole_pfn);\nmm/mm_init.c-970-\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 12643
							output: 32
							thoughts: 227
						

Thoughts:
**My Assessment of the Patch's Vulnerability**

Okay, yes, this definitely looks like it involves static configurations. Hmm, let me see... are there *other* configuration options in play here? Ah, I see `CONFIG_SPARSEMEM_VMEMMAP` is being utilized. Right, I should definitely incorporate that into my understanding. Plus, of course, the big one, `CONFIG_MEMORY_HOTPLUG`.

So, let's break this down clearly. Based on my analysis, the following applies:

*   **WorthFuzzing**: True, absolutely.
*   **Reasoning**: This patch is touching fundamental memory management mechanisms. Specifically, it seems to be affecting how the system tracks zone contiguity and how it locates section PFNs. This is the kind of area where subtle issues can have a massive impact. Furthermore, these code paths are likely involved with hotplug operations, a common feature found in virtualized environments.
*   **FocusSymbols**: Okay, so the key functions I need to scrutinize are:
    *   `memory_block_online`
    *   `memory_block_offline`
    *   `find_smallest_section_pfn`
    *   `find_biggest_section_pfn`
    *   `remove_pfn_range_from_zone`
    *   `move_pfn_range_to_zone`
    *   `update_zone_online_memmap_pages`
*   **EnableConfigs**: The necessary configurations to trigger the potential vulnerability are:
    *   `MEMORY_HOTPLUG`
    *   `SPARSEMEM_VMEMMAP`

I'm just going to double check real quick to make sure `SPARSEMEM_VMEMMAP` is actually a valid configuration option within the kernel. Can't be too careful!



16/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "config SPARSEMEM_VMEMMAP",
  "PathPrefix": "mm/Kconfig"
}

Results:
{
  "Output": "mm/Kconfig=451=config SPARSEMEM_EXTREME\n--\nmm/Kconfig-454-\nmm/Kconfig:455:config SPARSEMEM_VMEMMAP_ENABLE\nmm/Kconfig-456-\tbool\nmm/Kconfig-457-\nmm/Kconfig:458:config SPARSEMEM_VMEMMAP\nmm/Kconfig-459-\tdef_bool y\n--\nmm/Kconfig-465-\nmm/Kconfig:466:config SPARSEMEM_VMEMMAP_PREINIT\nmm/Kconfig-467-\tbool\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 13465
							output: 25
							thoughts: 160
						

Thoughts:
**Analyzing Memory Block Management in Linux**

Okay, so `SPARSEMEM_VMEMMAP` is definitely a valid configuration; that's good to establish right away. Now, I need to make sure I have the correct function names for the memory block online/offline operations. A quick check of `drivers/base/memory.c` confirms that `memory_block_online` and `memory_block_offline` are, in fact, the functions I'm looking for.

Next, I need to understand how `set_zone_contiguous` and `clear_zone_contiguous` are being used. Ah, right, they're defined as inline functions in `include/linux/mmzone.h`. That's a bit of a wrinkle for direct fuzzing, but it's not a showstopper. Because they are inlined, I'll need to focus my fuzzing efforts on their calling functions. Since `memory_block_online` and `memory_block_offline` *do* call them, those will be my primary targets.

To ensure I'm getting a complete picture, I should now check for any other callers of `set_zone_contiguous` and `clear_zone_contiguous`. I need to ensure there are no other potential interactions here that are escaping my notice.



18/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "set_zone_contiguous",
  "PathPrefix": ""
}

Results:
{
  "Output": "drivers/base/memory.c=224=static int memory_block_online(struct memory_block *mem)\n--\ndrivers/base/memory.c-273-out:\ndrivers/base/memory.c:274:\tset_zone_contiguous(zone);\ndrivers/base/memory.c-275-\tmem_hotplug_done();\n--\ndrivers/base/memory.c=282=static int memory_block_offline(struct memory_block *mem)\n--\ndrivers/base/memory.c-321-out:\ndrivers/base/memory.c:322:\tset_zone_contiguous(zone);\ndrivers/base/memory.c-323-\tmem_hotplug_done();\n--\ninclude/linux/mmzone.h=1713=static inline bool zone_is_contiguous(const struct zone *zone)\n--\ninclude/linux/mmzone.h-1717-\ninclude/linux/mmzone.h:1718:static inline void set_zone_contiguous(struct zone *zone)\ninclude/linux/mmzone.h-1719-{\n--\nmm/mm_init.c=2260=void __init page_alloc_init_late(void)\n--\nmm/mm_init.c-2296-\tfor_each_populated_zone(zone)\nmm/mm_init.c:2297:\t\tset_zone_contiguous(zone);\nmm/mm_init.c-2298-\n"
}

19/2 2026/07/24 13:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14302
							output: 25
							thoughts: 37
						

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

**Adding `set_zone_contiguous` to the Focus List**

Alright, so `page_alloc_init_late` is calling `set_zone_contiguous`, which is good to know, and useful. I should definitely add that to my mental FocusSymbols list. That’s probably important for memory management, and I want to keep those key functions easily accessible in my mind when I'm dealing with allocation and contiguous memory.

Now, I should quickly check on `clear_zone_contiguous` just to make sure I understand the relationship and whether there are any potential gotchas there. I should probably have both of these functions easily accessible in my mind.



20/2 2026/07/24 13:26 tool grepper
0m Args:
{
  "Expression": "clear_zone_contiguous",
  "PathPrefix": ""
}

Results:
{
  "Output": "drivers/base/memory.c=224=static int memory_block_online(struct memory_block *mem)\n--\ndrivers/base/memory.c-248-\tmem_hotplug_begin();\ndrivers/base/memory.c:249:\tclear_zone_contiguous(zone);\ndrivers/base/memory.c-250-\tif (nr_vmemmap_pages) {\n--\ndrivers/base/memory.c=282=static int memory_block_offline(struct memory_block *mem)\n--\ndrivers/base/memory.c-301-\tmem_hotplug_begin();\ndrivers/base/memory.c:302:\tclear_zone_contiguous(zone);\ndrivers/base/memory.c-303-\tif (nr_vmemmap_pages)\n--\ninclude/linux/mmzone.h=1718=static inline void set_zone_contiguous(struct zone *zone)\n--\ninclude/linux/mmzone.h-1725-\ninclude/linux/mmzone.h:1726:static inline void clear_zone_contiguous(struct zone *zone)\ninclude/linux/mmzone.h-1727-{\n"
}

21/2 2026/07/24 13:26 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14820
							output: 35
							thoughts: 42
						

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

**Analyzing `clear_zone_contiguous` Callers and `adjust_present_page_count`**

Right, so I'm focusing on the `clear_zone_contiguous` function. Seems like the code is telling me that only `memory_block_online` and `memory_block_offline` are calling that function. That's a good starting point for understanding its scope and impact. It means I can probably keep my analysis pretty focused, at least initially. Those two callers are the key.

Now, where does that lead me? Ah, yes, let's pivot and take a look at `adjust_present_page_count`. I need to figure out how that is related to `clear_zone_contiguous` and the context those calls are being made. I suspect there might be some interplay there, maybe even a synchronization issue or some subtlety in how page counts are managed, given the "online" and "offline" naming. Could be something interesting to dig into. Let's see...



22/2 2026/07/24 13:27 tool grepper
0m Args:
{
  "Expression": "adjust_present_page_count",
  "PathPrefix": "mm/memory_hotplug.c"
}

Results:
{
  "Output": "mm/memory_hotplug.c=1045=struct zone *zone_for_pfn_range(enum mmop online_type, int nid,\n--\nmm/memory_hotplug.c-1064- */\nmm/memory_hotplug.c:1065:void adjust_present_page_count(struct page *page, struct memory_group *group,\nmm/memory_hotplug.c-1066-\t\t\t       long nr_pages)\n--\nmm/memory_hotplug.c=1141=int online_pages(unsigned long pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-1204-\tonline_pages_range(pfn, nr_pages);\nmm/memory_hotplug.c:1205:\tadjust_present_page_count(pfn_to_page(pfn), group, nr_pages);\nmm/memory_hotplug.c-1206-\n--\nmm/memory_hotplug.c=1906=int offline_pages(unsigned long start_pfn, unsigned long nr_pages,\n--\nmm/memory_hotplug.c-2066-\tadjust_managed_page_count(pfn_to_page(start_pfn), -managed_pages);\nmm/memory_hotplug.c:2067:\tadjust_present_page_count(pfn_to_page(start_pfn), group, -nr_pages);\nmm/memory_hotplug.c-2068-\n"
}

23/2 2026/07/24 13:27 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15510
							output: 188
							thoughts: 148
						

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

**Analyzing and Solidifying Memory Management Focus Symbols**

Right, so `adjust_present_page_count` gets updated to also manage `zone->pages_with_online_memmap`. I should definitely add that to the `FocusSymbols` list, that's crucial. Let's see... looking over the existing list:

*   `memory_block_online`
*   `memory_block_offline`
*   `find_smallest_section_pfn`
*   `find_biggest_section_pfn`
*   `remove_pfn_range_from_zone`
*   `move_pfn_range_to_zone`
*   `update_zone_online_memmap_pages`
*   `page_alloc_init_late`
*   `adjust_present_page_count`

Yeah, this is looking pretty good. This covers a comprehensive set of functions related to online memory, memory block management, PFN manipulation, and initialization, and the recent inclusion of `adjust_present_page_count` looks correct. This looks like a solid, well-rounded set of symbols to focus on for this particular work. Now I can move on to actually setting the results.



24/2 2026/07/24 13:27 tool set-results
0m Args:
{
  "EnableConfigs": [
    "MEMORY_HOTPLUG",
    "SPARSEMEM_VMEMMAP"
  ],
  "FocusSymbols": [
    "memory_block_online",
    "memory_block_offline",
    "find_smallest_section_pfn",
    "find_biggest_section_pfn",
    "remove_pfn_range_from_zone",
    "move_pfn_range_to_zone",
    "update_zone_online_memmap_pages",
    "page_alloc_init_late",
    "adjust_present_page_count"
  ],
  "Reasoning": "The patch modifies core memory management and memory hotplug logic, specifically zone contiguity tracking and section PFN finding. These paths can be reached during memory hotplug operations, which can be triggered in virtualized environments.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "MEMORY_HOTPLUG",
    "SPARSEMEM_VMEMMAP"
  ],
  "FocusSymbols": [
    "memory_block_online",
    "memory_block_offline",
    "find_smallest_section_pfn",
    "find_biggest_section_pfn",
    "remove_pfn_range_from_zone",
    "move_pfn_range_to_zone",
    "update_zone_online_memmap_pages",
    "page_alloc_init_late",
    "adjust_present_page_count"
  ],
  "Reasoning": "The patch modifies core memory management and memory hotplug logic, specifically zone contiguity tracking and section PFN finding. These paths can be reached during memory hotplug operations, which can be triggered in virtualized environments.",
  "WorthFuzzing": true
}

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