AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/26 09:10 flow patch-triage
7m Args:
null

Results:
{
  "EnableConfigs": [
    "MMU",
    "SMP"
  ],
  "FocusSymbols": [
    "__vmap_pages_range_noflush",
    "vmap_pages_range_noflush",
    "vmap_page_range",
    "__vunmap_range_noflush",
    "vunmap_range_noflush",
    "kmsan_vmap_pages_range_noflush",
    "kmsan_vunmap_range_noflush",
    "kmsan_ioremap_page_range"
  ],
  "KMSANReasoning": "The patch refactors kernel virtual memory mapping and unmapping functions (vmap, ioremap, vunmap, and percpu-vm) so that partially mapped page table ranges automatically unwind themselves on failure. It also introduces a `can_resched` flag to avoid calling `cond_resched()` in atomic contexts and updates the KMSAN shadow/origin mapping hooks to reflect these signature changes and unwind shadow mappings on error.\n\nKMSAN is specifically designed to detect uninitialized memory reads and kernel-to-user memory info-leaks. The changes in this patch:\n1. Do not copy any data to user space (no copy_to_user, netlink skb, ioctls, etc.).\n2. Do not introduce any variables or structures that could be read while uninitialized; all new and modified local variables (such as err, start, can_resched) are explicitly initialized.\n3. Primarily affect page table manipulation, error unwinding, and context scheduling (sleeping vs atomic).\n\nAny potential bugs resulting from these changes (such as sleeping in atomic context, improper TLB flushing, or invalid page table references) are covered by standard kernel detectors such as LOCKDEP, CONFIG_DEBUG_VM, and KASAN. Therefore, dedicated KMSAN fuzzing is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies core memory management (mm/vmalloc.c, mm/percpu-vm.c, and mm/kmsan) on reachable code paths for amd64. It adds new runtime assertions (WARN_ON_ONCE checking address alignment and bounds in vunmap_pte_range, vmap_pages_pte_range, and __vmap_pages_range_noflush), implements automatic teardown of partially mapped page tables on mapping failure in __vmap_pages_range_noflush and vmap_page_range, adjusts percpu error unwinding to avoid double unmapping, and modifies __vunmap_range_noflush to safely support atomic callers by conditionally disabling cond_resched(). These changes alter core VM invariants and error recovery paths, making this series high-value for fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/10/26 09:10 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 4b57052221731ef57556636f96cf8a2a9b97ac25\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Oct 26 09:10:02 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c\nindex 5c064485197a9..93029d85e6eaf 100644\n--- a/arch/powerpc/kernel/isa-bridge.c\n+++ b/arch/powerpc/kernel/isa-bridge.c\n@@ -46,9 +46,8 @@ static void remap_isa_base(phys_addr_t pa, unsigned long size)\n \tWARN_ON_ONCE(size \u0026 ~PAGE_MASK);\n \n \tif (slab_is_available()) {\n-\t\tif (vmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,\n-\t\t\t\t    pgprot_noncached(PAGE_KERNEL)))\n-\t\t\tvunmap_range(ISA_IO_BASE, ISA_IO_BASE + size);\n+\t\tvmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,\n+\t\t\t\tpgprot_noncached(PAGE_KERNEL));\n \t} else {\n \t\tearly_ioremap_range(ISA_IO_BASE, pa, size,\n \t\t\t\tpgprot_noncached(PAGE_KERNEL));\ndiff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c\nindex e27342ef128b8..f74269636d5ec 100644\n--- a/arch/powerpc/kernel/pci_64.c\n+++ b/arch/powerpc/kernel/pci_64.c\n@@ -139,10 +139,8 @@ void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size)\n \n \taddr = (unsigned long)area-\u003eaddr;\n \tif (ioremap_page_range(addr, addr + size, paddr,\n-\t\t\tpgprot_noncached(PAGE_KERNEL))) {\n-\t\tvunmap_range(addr, addr + size);\n+\t\t\tpgprot_noncached(PAGE_KERNEL)))\n \t\treturn NULL;\n-\t}\n \n \treturn (void __iomem *)addr;\n }\ndiff --git a/include/linux/kmsan.h b/include/linux/kmsan.h\nindex 7da9fd506b390..0016c032d1e79 100644\n--- a/include/linux/kmsan.h\n+++ b/include/linux/kmsan.h\n@@ -150,11 +150,14 @@ int __must_check kmsan_vmap_pages_range_noflush(unsigned long start,\n  * kmsan_vunmap_kernel_range_noflush() - Notify KMSAN about a vunmap.\n  * @start: start of vunmapped range.\n  * @end:   end of vunmapped range.\n+ * @can_resched: whether the unmap walk may call cond_resched(); pass\n+ * false from atomic context.\n  *\n  * KMSAN unmaps the contiguous metadata ranges created by\n  * kmsan_map_kernel_range_noflush().\n  */\n-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end);\n+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,\n+\t\t\t\tbool can_resched);\n \n /**\n  * kmsan_ioremap_page_range() - Notify KMSAN about a ioremap_page_range() call.\n@@ -355,7 +358,8 @@ static inline int __must_check kmsan_vmap_pages_range_noflush(\n }\n \n static inline void kmsan_vunmap_range_noflush(unsigned long start,\n-\t\t\t\t\t      unsigned long end)\n+\t\t\t\t\t      unsigned long end,\n+\t\t\t\t\t      bool can_resched)\n {\n }\n \ndiff --git a/mm/kmsan/hooks.c b/mm/kmsan/hooks.c\nindex a706666db0973..0cd1edaa1120c 100644\n--- a/mm/kmsan/hooks.c\n+++ b/mm/kmsan/hooks.c\n@@ -136,10 +136,16 @@ static unsigned long vmalloc_origin(unsigned long addr)\n \t\t\t\t\t\t KMSAN_META_ORIGIN);\n }\n \n-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)\n+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,\n+\t\t\t\tbool can_resched)\n {\n-\t__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end));\n-\t__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end));\n+\tif (!kmsan_enabled)\n+\t\treturn;\n+\n+\t__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end),\n+\t\t\t       can_resched);\n+\t__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end),\n+\t\t\t       can_resched);\n \tflush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));\n \tflush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));\n }\n@@ -168,8 +174,9 @@ static void kmsan_iounmap_pages(unsigned long start, unsigned long end)\n \t\tif (origin)\n \t\t\t__free_pages(origin, KMSAN_IOREMAP_META_ORDER);\n \t}\n-\t__vunmap_range_noflush(shadow_start, shadow_end);\n-\t__vunmap_range_noflush(origin_start, origin_end);\n+\t/* ioremap metadata mappings are always GFP_KERNEL, so resched is safe. */\n+\t__vunmap_range_noflush(shadow_start, shadow_end, true);\n+\t__vunmap_range_noflush(origin_start, origin_end, true);\n \tflush_tlb_kernel_range(shadow_start, shadow_end);\n \tflush_tlb_kernel_range(origin_start, origin_end);\n }\n@@ -204,7 +211,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,\n \t\tmapped = __vmap_pages_range_noflush(\n \t\t\tvmalloc_shadow(start + off),\n \t\t\tvmalloc_shadow(start + off + PAGE_SIZE), prot, \u0026shadow,\n-\t\t\tPAGE_SHIFT);\n+\t\t\tPAGE_SHIFT, gfp_mask);\n \t\tif (mapped) {\n \t\t\terr = mapped;\n \t\t\tgoto ret;\n@@ -214,7 +221,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,\n \t\tmapped = __vmap_pages_range_noflush(\n \t\t\tvmalloc_origin(start + off),\n \t\t\tvmalloc_origin(start + off + PAGE_SIZE), prot, \u0026origin,\n-\t\t\tPAGE_SHIFT);\n+\t\t\tPAGE_SHIFT, gfp_mask);\n \t\tif (mapped) {\n \t\t\terr = mapped;\n \t\t\tgoto ret;\ndiff --git a/mm/kmsan/shadow.c b/mm/kmsan/shadow.c\nindex 0c88d89bf0d68..452760a333e4f 100644\n--- a/mm/kmsan/shadow.c\n+++ b/mm/kmsan/shadow.c\n@@ -247,7 +247,7 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,\n \torigin_end = vmalloc_meta((void *)end, KMSAN_META_ORIGIN);\n \tkmsan_enter_runtime();\n \tmapped = __vmap_pages_range_noflush(shadow_start, shadow_end, prot,\n-\t\t\t\t\t    s_pages, page_shift);\n+\t\t\t\t\t    s_pages, page_shift, gfp_mask);\n \tkmsan_leave_runtime();\n \tif (mapped) {\n \t\terr = mapped;\n@@ -255,9 +255,14 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,\n \t}\n \tkmsan_enter_runtime();\n \tmapped = __vmap_pages_range_noflush(origin_start, origin_end, prot,\n-\t\t\t\t\t    o_pages, page_shift);\n+\t\t\t\t\t    o_pages, page_shift, gfp_mask);\n \tkmsan_leave_runtime();\n \tif (mapped) {\n+\t\t/* Undo the shadow mapping set up above. */\n+\t\tkmsan_enter_runtime();\n+\t\t__vunmap_range_noflush(shadow_start, shadow_end,\n+\t\t\t\t       gfpflags_allow_blocking(gfp_mask));\n+\t\tkmsan_leave_runtime();\n \t\terr = mapped;\n \t\tgoto ret;\n \t}\ndiff --git a/mm/percpu-vm.c b/mm/percpu-vm.c\nindex 509d8901835cd..59d3a336a6bd1 100644\n--- a/mm/percpu-vm.c\n+++ b/mm/percpu-vm.c\n@@ -252,10 +252,11 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages,\n \treturn 0;\n err:\n \tfor_each_possible_cpu(tcpu) {\n-\t\t__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),\n-\t\t\t\t   page_end - page_start);\n+\t\t/* The failing CPU's partial mapping undoes itself */\n \t\tif (tcpu == cpu)\n \t\t\tbreak;\n+\t\t__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),\n+\t\t\t\t   page_end - page_start);\n \t}\n \tpcpu_post_unmap_tlb_flush(chunk, page_start, page_end);\n \treturn err;\ndiff --git a/mm/vmalloc.c b/mm/vmalloc.c\nindex 44e0eb32708f1..2b68f7263d531 100644\n--- a/mm/vmalloc.c\n+++ b/mm/vmalloc.c\n@@ -359,11 +359,17 @@ int vmap_page_range(unsigned long addr, unsigned long end,\n \n \terr = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),\n \t\t\t\t ioremap_max_page_shift);\n-\tflush_cache_vmap(addr, end);\n \tif (!err)\n \t\terr = kmsan_ioremap_page_range(addr, end, phys_addr, prot,\n \t\t\t\t\t       ioremap_max_page_shift);\n-\treturn err;\n+\tif (err) {\n+\t\t/* Callers are ioremap and arch setup code, always sleepable. */\n+\t\t__vunmap_range_noflush(addr, end, true);\n+\t\treturn err;\n+\t}\n+\n+\tflush_cache_vmap(addr, end);\n+\treturn 0;\n }\n \n int ioremap_page_range(unsigned long addr, unsigned long end,\n@@ -393,6 +399,11 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,\n \tpte_t ptent;\n \tunsigned long size = PAGE_SIZE;\n \n+\tif (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))\n+\t\tend = addr + PAGE_ALIGN_DOWN(end - addr);\n+\tif (end == addr)\n+\t\treturn;\n+\n \tpte = pte_offset_kernel(pmd, addr);\n \tlazy_mmu_mode_enable();\n \n@@ -418,7 +429,7 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,\n }\n \n static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,\n-\t\t\t     pgtbl_mod_mask *mask)\n+\t\t\t     pgtbl_mod_mask *mask, bool can_resched)\n {\n \tpmd_t *pmd;\n \tunsigned long next;\n@@ -440,12 +451,14 @@ static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,\n \t\t\tcontinue;\n \t\tvunmap_pte_range(pmd, addr, next, mask);\n \n-\t\tcond_resched();\n+\t\t/* The undo path may run in atomic context. */\n+\t\tif (can_resched)\n+\t\t\tcond_resched();\n \t} while (pmd++, addr = next, addr != end);\n }\n \n static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,\n-\t\t\t     pgtbl_mod_mask *mask)\n+\t\t\t     pgtbl_mod_mask *mask, bool can_resched)\n {\n \tpud_t *pud;\n \tunsigned long next;\n@@ -465,12 +478,12 @@ static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,\n \t\t}\n \t\tif (pud_none_or_clear_bad(pud))\n \t\t\tcontinue;\n-\t\tvunmap_pmd_range(pud, addr, next, mask);\n+\t\tvunmap_pmd_range(pud, addr, next, mask, can_resched);\n \t} while (pud++, addr = next, addr != end);\n }\n \n static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,\n-\t\t\t     pgtbl_mod_mask *mask)\n+\t\t\t     pgtbl_mod_mask *mask, bool can_resched)\n {\n \tp4d_t *p4d;\n \tunsigned long next;\n@@ -485,7 +498,7 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,\n \n \t\tif (p4d_none_or_clear_bad(p4d))\n \t\t\tcontinue;\n-\t\tvunmap_pud_range(p4d, addr, next, mask);\n+\t\tvunmap_pud_range(p4d, addr, next, mask, can_resched);\n \t} while (p4d++, addr = next, addr != end);\n }\n \n@@ -501,7 +514,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,\n  *\n  * This is an internal function only. Do not use outside mm/.\n  */\n-void __vunmap_range_noflush(unsigned long start, unsigned long end)\n+void __vunmap_range_noflush(unsigned long start, unsigned long end,\n+\t\t\t    bool can_resched)\n {\n \tunsigned long next;\n \tpgd_t *pgd;\n@@ -516,7 +530,7 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)\n \t\t\tmask |= PGTBL_PGD_MODIFIED;\n \t\tif (pgd_none_or_clear_bad(pgd))\n \t\t\tcontinue;\n-\t\tvunmap_p4d_range(pgd, addr, next, \u0026mask);\n+\t\tvunmap_p4d_range(pgd, addr, next, \u0026mask, can_resched);\n \t} while (pgd++, addr = next, addr != end);\n \n \tif (mask \u0026 ARCH_PAGE_TABLE_SYNC_MASK)\n@@ -525,8 +539,9 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)\n \n void vunmap_range_noflush(unsigned long start, unsigned long end)\n {\n-\tkmsan_vunmap_range_noflush(start, end);\n-\t__vunmap_range_noflush(start, end);\n+\t/* The unmap paths are sleepable, keep the resched point. */\n+\tkmsan_vunmap_range_noflush(start, end, true);\n+\t__vunmap_range_noflush(start, end, true);\n }\n \n /**\n@@ -552,6 +567,9 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,\n \tint err = 0;\n \tpte_t *pte;\n \n+\tif (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))\n+\t\treturn -EINVAL;\n+\n \t/*\n \t * nr is a running index into the array which helps higher level\n \t * callers keep track of where we're up to.\n@@ -595,16 +613,18 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,\n {\n \tpmd_t *pmd;\n \tunsigned long next;\n+\tint err = 0;\n \n \tpmd = pmd_alloc_track(\u0026init_mm, pud, addr, mask);\n \tif (!pmd)\n \t\treturn -ENOMEM;\n \tdo {\n \t\tnext = pmd_addr_end(addr, end);\n-\t\tif (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))\n-\t\t\treturn -ENOMEM;\n+\t\terr = vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask);\n+\t\tif (err)\n+\t\t\tbreak;\n \t} while (pmd++, addr = next, addr != end);\n-\treturn 0;\n+\treturn err;\n }\n \n static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,\n@@ -613,16 +633,18 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,\n {\n \tpud_t *pud;\n \tunsigned long next;\n+\tint err = 0;\n \n \tpud = pud_alloc_track(\u0026init_mm, p4d, addr, mask);\n \tif (!pud)\n \t\treturn -ENOMEM;\n \tdo {\n \t\tnext = pud_addr_end(addr, end);\n-\t\tif (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))\n-\t\t\treturn -ENOMEM;\n+\t\terr = vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask);\n+\t\tif (err)\n+\t\t\tbreak;\n \t} while (pud++, addr = next, addr != end);\n-\treturn 0;\n+\treturn err;\n }\n \n static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,\n@@ -631,16 +653,18 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,\n {\n \tp4d_t *p4d;\n \tunsigned long next;\n+\tint err = 0;\n \n \tp4d = p4d_alloc_track(\u0026init_mm, pgd, addr, mask);\n \tif (!p4d)\n \t\treturn -ENOMEM;\n \tdo {\n \t\tnext = p4d_addr_end(addr, end);\n-\t\tif (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))\n-\t\t\treturn -ENOMEM;\n+\t\terr = vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask);\n+\t\tif (err)\n+\t\t\tbreak;\n \t} while (p4d++, addr = next, addr != end);\n-\treturn 0;\n+\treturn err;\n }\n \n static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,\n@@ -677,33 +701,51 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,\n  * The caller is responsible for calling flush_cache_vmap() after this\n  * function returns successfully and before the addresses are accessed.\n  *\n+ * On failure the mapping is torn down before returning, so a retry\n+ * sees a clean range. When @gfp_mask cannot block, the teardown\n+ * skips cond_resched(), it may run in atomic context.\n+ *\n  * This is an internal function only. Do not use outside mm/.\n  */\n int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift)\n+\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n+\t\tgfp_t gfp_mask)\n {\n \tunsigned int i, nr = (end - addr) \u003e\u003e PAGE_SHIFT;\n+\tunsigned long start = addr;\n+\tint err = 0;\n \n-\tif (WARN_ON_ONCE(page_shift \u003c PAGE_SHIFT))\n+\tif (WARN_ON_ONCE(addr \u003e= end))\n \t\treturn -EINVAL;\n \n-\tif (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||\n-\t\t\tpage_shift == PAGE_SHIFT)\n-\t\treturn vmap_small_pages_range_noflush(addr, end, prot, pages);\n+\tif (WARN_ON_ONCE(!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(end)))\n+\t\treturn -EINVAL;\n \n-\tfor (i = 0; i \u003c nr; i += 1U \u003c\u003c (page_shift - PAGE_SHIFT)) {\n-\t\tint err;\n+\tif (WARN_ON_ONCE(page_shift \u003c PAGE_SHIFT))\n+\t\treturn -EINVAL;\n \n-\t\terr = vmap_range_noflush(addr, addr + (1UL \u003c\u003c page_shift),\n+\tif (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||\n+\t\t\tpage_shift == PAGE_SHIFT) {\n+\t\terr = vmap_small_pages_range_noflush(addr, end, prot, pages);\n+\t} else {\n+\t\tfor (i = 0; i \u003c nr; i += 1U \u003c\u003c (page_shift - PAGE_SHIFT)) {\n+\t\t\terr = vmap_range_noflush(addr,\n+\t\t\t\t\taddr + (1UL \u003c\u003c page_shift),\n \t\t\t\t\tpage_to_phys(pages[i]), prot,\n \t\t\t\t\tpage_shift);\n-\t\tif (err)\n-\t\t\treturn err;\n+\t\t\tif (err)\n+\t\t\t\tbreak;\n \n-\t\taddr += 1UL \u003c\u003c page_shift;\n+\t\t\taddr += 1UL \u003c\u003c page_shift;\n+\t\t}\n \t}\n \n-\treturn 0;\n+\t/* Undo the PTEs installed before the failure. */\n+\tif (err)\n+\t\t__vunmap_range_noflush(start, end,\n+\t\t\t\t       gfpflags_allow_blocking(gfp_mask));\n+\n+\treturn err;\n }\n \n int vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n@@ -715,7 +757,18 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n \n \tif (ret)\n \t\treturn ret;\n-\treturn __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);\n+\n+\tret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,\n+\t\t\t\t\t gfp_mask);\n+\t/*\n+\t * The page tables undo themselves on failure. Tear down the\n+\t * metadata that was fully set up before the mapping failed.\n+\t */\n+\tif (ret)\n+\t\tkmsan_vunmap_range_noflush(addr, end,\n+\t\t\t\t\t   gfpflags_allow_blocking(gfp_mask));\n+\n+\treturn ret;\n }\n \n static int __vmap_pages_range(unsigned long addr, unsigned long end,\n@@ -725,8 +778,11 @@ static int __vmap_pages_range(unsigned long addr, unsigned long end,\n \tint err;\n \n \terr = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);\n+\tif (err)\n+\t\treturn err;\n+\n \tflush_cache_vmap(addr, end);\n-\treturn err;\n+\treturn 0;\n }\n \n /**\ndiff --git a/mm/vmalloc.h b/mm/vmalloc.h\nindex d8372a4f65c4c..9ea28845f7133 100644\n--- a/mm/vmalloc.h\n+++ b/mm/vmalloc.h\n@@ -40,10 +40,12 @@ void clear_vm_uninitialized_flag(struct vm_struct *vm);\n \n int __must_check __vmap_pages_range_noflush(unsigned long addr,\n \t\tunsigned long end, pgprot_t prot,\n-\t\tstruct page **pages, unsigned int page_shift);\n+\t\tstruct page **pages, unsigned int page_shift,\n+\t\tgfp_t gfp_mask);\n \n void vunmap_range_noflush(unsigned long start, unsigned long end);\n \n-void __vunmap_range_noflush(unsigned long start, unsigned long end);\n+void __vunmap_range_noflush(unsigned long start, unsigned long end,\n+\t\t\t    bool can_resched);\n \n #endif /* __MM_VMALLOC_H */\n"
}

2/1 2026/10/26 09:10 agent patch-evaluator
7m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "MMU",
    "SMP"
  ],
  "FocusSymbols": [
    "__vmap_pages_range_noflush",
    "vmap_pages_range_noflush",
    "vmap_page_range",
    "__vunmap_range_noflush",
    "vunmap_range_noflush",
    "kmsan_vmap_pages_range_noflush",
    "kmsan_vunmap_range_noflush",
    "kmsan_ioremap_page_range"
  ],
  "Reasoning": "The patch modifies core memory management (mm/vmalloc.c, mm/percpu-vm.c, and mm/kmsan) on reachable code paths for amd64. It adds new runtime assertions (WARN_ON_ONCE checking address alignment and bounds in vunmap_pte_range, vmap_pages_pte_range, and __vmap_pages_range_noflush), implements automatic teardown of partially mapped page tables on mapping failure in __vmap_pages_range_noflush and vmap_page_range, adjusts percpu error unwinding to avoid double unmapping, and modifies __vunmap_range_noflush to safely support atomic callers by conditionally disabling cond_resched(). These changes alter core VM invariants and error recovery paths, making this series high-value for fuzzing.",
  "WorthFuzzing": true
}

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 4b57052221731ef57556636f96cf8a2a9b97ac25
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Oct 26 09:10:02 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c
index 5c064485197a9..93029d85e6eaf 100644
--- a/arch/powerpc/kernel/isa-bridge.c
+++ b/arch/powerpc/kernel/isa-bridge.c
@@ -46,9 +46,8 @@ static void remap_isa_base(phys_addr_t pa, unsigned long size)
 	WARN_ON_ONCE(size & ~PAGE_MASK);
 
 	if (slab_is_available()) {
-		if (vmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
-				    pgprot_noncached(PAGE_KERNEL)))
-			vunmap_range(ISA_IO_BASE, ISA_IO_BASE + size);
+		vmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
+				pgprot_noncached(PAGE_KERNEL));
 	} else {
 		early_ioremap_range(ISA_IO_BASE, pa, size,
 				pgprot_noncached(PAGE_KERNEL));
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index e27342ef128b8..f74269636d5ec 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -139,10 +139,8 @@ void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size)
 
 	addr = (unsigned long)area->addr;
 	if (ioremap_page_range(addr, addr + size, paddr,
-			pgprot_noncached(PAGE_KERNEL))) {
-		vunmap_range(addr, addr + size);
+			pgprot_noncached(PAGE_KERNEL)))
 		return NULL;
-	}
 
 	return (void __iomem *)addr;
 }
diff --git a/include/linux/kmsan.h b/include/linux/kmsan.h
index 7da9fd506b390..0016c032d1e79 100644
--- a/include/linux/kmsan.h
+++ b/include/linux/kmsan.h
@@ -150,11 +150,14 @@ int __must_check kmsan_vmap_pages_range_noflush(unsigned long start,
  * kmsan_vunmap_kernel_range_noflush() - Notify KMSAN about a vunmap.
  * @start: start of vunmapped range.
  * @end:   end of vunmapped range.
+ * @can_resched: whether the unmap walk may call cond_resched(); pass
+ * false from atomic context.
  *
  * KMSAN unmaps the contiguous metadata ranges created by
  * kmsan_map_kernel_range_noflush().
  */
-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end);
+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,
+				bool can_resched);
 
 /**
  * kmsan_ioremap_page_range() - Notify KMSAN about a ioremap_page_range() call.
@@ -355,7 +358,8 @@ static inline int __must_check kmsan_vmap_pages_range_noflush(
 }
 
 static inline void kmsan_vunmap_range_noflush(unsigned long start,
-					      unsigned long end)
+					      unsigned long end,
+					      bool can_resched)
 {
 }
 
diff --git a/mm/kmsan/hooks.c b/mm/kmsan/hooks.c
index a706666db0973..0cd1edaa1120c 100644
--- a/mm/kmsan/hooks.c
+++ b/mm/kmsan/hooks.c
@@ -136,10 +136,16 @@ static unsigned long vmalloc_origin(unsigned long addr)
 						 KMSAN_META_ORIGIN);
 }
 
-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)
+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,
+				bool can_resched)
 {
-	__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end));
-	__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end));
+	if (!kmsan_enabled)
+		return;
+
+	__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end),
+			       can_resched);
+	__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end),
+			       can_resched);
 	flush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));
 	flush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));
 }
@@ -168,8 +174,9 @@ static void kmsan_iounmap_pages(unsigned long start, unsigned long end)
 		if (origin)
 			__free_pages(origin, KMSAN_IOREMAP_META_ORDER);
 	}
-	__vunmap_range_noflush(shadow_start, shadow_end);
-	__vunmap_range_noflush(origin_start, origin_end);
+	/* ioremap metadata mappings are always GFP_KERNEL, so resched is safe. */
+	__vunmap_range_noflush(shadow_start, shadow_end, true);
+	__vunmap_range_noflush(origin_start, origin_end, true);
 	flush_tlb_kernel_range(shadow_start, shadow_end);
 	flush_tlb_kernel_range(origin_start, origin_end);
 }
@@ -204,7 +211,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
 		mapped = __vmap_pages_range_noflush(
 			vmalloc_shadow(start + off),
 			vmalloc_shadow(start + off + PAGE_SIZE), prot, &shadow,
-			PAGE_SHIFT);
+			PAGE_SHIFT, gfp_mask);
 		if (mapped) {
 			err = mapped;
 			goto ret;
@@ -214,7 +221,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
 		mapped = __vmap_pages_range_noflush(
 			vmalloc_origin(start + off),
 			vmalloc_origin(start + off + PAGE_SIZE), prot, &origin,
-			PAGE_SHIFT);
+			PAGE_SHIFT, gfp_mask);
 		if (mapped) {
 			err = mapped;
 			goto ret;
diff --git a/mm/kmsan/shadow.c b/mm/kmsan/shadow.c
index 0c88d89bf0d68..452760a333e4f 100644
--- a/mm/kmsan/shadow.c
+++ b/mm/kmsan/shadow.c
@@ -247,7 +247,7 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
 	origin_end = vmalloc_meta((void *)end, KMSAN_META_ORIGIN);
 	kmsan_enter_runtime();
 	mapped = __vmap_pages_range_noflush(shadow_start, shadow_end, prot,
-					    s_pages, page_shift);
+					    s_pages, page_shift, gfp_mask);
 	kmsan_leave_runtime();
 	if (mapped) {
 		err = mapped;
@@ -255,9 +255,14 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
 	}
 	kmsan_enter_runtime();
 	mapped = __vmap_pages_range_noflush(origin_start, origin_end, prot,
-					    o_pages, page_shift);
+					    o_pages, page_shift, gfp_mask);
 	kmsan_leave_runtime();
 	if (mapped) {
+		/* Undo the shadow mapping set up above. */
+		kmsan_enter_runtime();
+		__vunmap_range_noflush(shadow_start, shadow_end,
+				       gfpflags_allow_blocking(gfp_mask));
+		kmsan_leave_runtime();
 		err = mapped;
 		goto ret;
 	}
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index 509d8901835cd..59d3a336a6bd1 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -252,10 +252,11 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages,
 	return 0;
 err:
 	for_each_possible_cpu(tcpu) {
-		__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
-				   page_end - page_start);
+		/* The failing CPU's partial mapping undoes itself */
 		if (tcpu == cpu)
 			break;
+		__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
+				   page_end - page_start);
 	}
 	pcpu_post_unmap_tlb_flush(chunk, page_start, page_end);
 	return err;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 44e0eb32708f1..2b68f7263d531 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -359,11 +359,17 @@ int vmap_page_range(unsigned long addr, unsigned long end,
 
 	err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
 				 ioremap_max_page_shift);
-	flush_cache_vmap(addr, end);
 	if (!err)
 		err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
 					       ioremap_max_page_shift);
-	return err;
+	if (err) {
+		/* Callers are ioremap and arch setup code, always sleepable. */
+		__vunmap_range_noflush(addr, end, true);
+		return err;
+	}
+
+	flush_cache_vmap(addr, end);
+	return 0;
 }
 
 int ioremap_page_range(unsigned long addr, unsigned long end,
@@ -393,6 +399,11 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 	pte_t ptent;
 	unsigned long size = PAGE_SIZE;
 
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
+		end = addr + PAGE_ALIGN_DOWN(end - addr);
+	if (end == addr)
+		return;
+
 	pte = pte_offset_kernel(pmd, addr);
 	lazy_mmu_mode_enable();
 
@@ -418,7 +429,7 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 }
 
 static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	pmd_t *pmd;
 	unsigned long next;
@@ -440,12 +451,14 @@ static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
 			continue;
 		vunmap_pte_range(pmd, addr, next, mask);
 
-		cond_resched();
+		/* The undo path may run in atomic context. */
+		if (can_resched)
+			cond_resched();
 	} while (pmd++, addr = next, addr != end);
 }
 
 static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	pud_t *pud;
 	unsigned long next;
@@ -465,12 +478,12 @@ static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
 		}
 		if (pud_none_or_clear_bad(pud))
 			continue;
-		vunmap_pmd_range(pud, addr, next, mask);
+		vunmap_pmd_range(pud, addr, next, mask, can_resched);
 	} while (pud++, addr = next, addr != end);
 }
 
 static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	p4d_t *p4d;
 	unsigned long next;
@@ -485,7 +498,7 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
 
 		if (p4d_none_or_clear_bad(p4d))
 			continue;
-		vunmap_pud_range(p4d, addr, next, mask);
+		vunmap_pud_range(p4d, addr, next, mask, can_resched);
 	} while (p4d++, addr = next, addr != end);
 }
 
@@ -501,7 +514,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
  *
  * This is an internal function only. Do not use outside mm/.
  */
-void __vunmap_range_noflush(unsigned long start, unsigned long end)
+void __vunmap_range_noflush(unsigned long start, unsigned long end,
+			    bool can_resched)
 {
 	unsigned long next;
 	pgd_t *pgd;
@@ -516,7 +530,7 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
 			mask |= PGTBL_PGD_MODIFIED;
 		if (pgd_none_or_clear_bad(pgd))
 			continue;
-		vunmap_p4d_range(pgd, addr, next, &mask);
+		vunmap_p4d_range(pgd, addr, next, &mask, can_resched);
 	} while (pgd++, addr = next, addr != end);
 
 	if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
@@ -525,8 +539,9 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
 
 void vunmap_range_noflush(unsigned long start, unsigned long end)
 {
-	kmsan_vunmap_range_noflush(start, end);
-	__vunmap_range_noflush(start, end);
+	/* The unmap paths are sleepable, keep the resched point. */
+	kmsan_vunmap_range_noflush(start, end, true);
+	__vunmap_range_noflush(start, end, true);
 }
 
 /**
@@ -552,6 +567,9 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
 	int err = 0;
 	pte_t *pte;
 
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
+		return -EINVAL;
+
 	/*
 	 * nr is a running index into the array which helps higher level
 	 * callers keep track of where we're up to.
@@ -595,16 +613,18 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 {
 	pmd_t *pmd;
 	unsigned long next;
+	int err = 0;
 
 	pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
 	if (!pmd)
 		return -ENOMEM;
 	do {
 		next = pmd_addr_end(addr, end);
-		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (pmd++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
@@ -613,16 +633,18 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
 {
 	pud_t *pud;
 	unsigned long next;
+	int err = 0;
 
 	pud = pud_alloc_track(&init_mm, p4d, addr, mask);
 	if (!pud)
 		return -ENOMEM;
 	do {
 		next = pud_addr_end(addr, end);
-		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (pud++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
@@ -631,16 +653,18 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 {
 	p4d_t *p4d;
 	unsigned long next;
+	int err = 0;
 
 	p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
 	if (!p4d)
 		return -ENOMEM;
 	do {
 		next = p4d_addr_end(addr, end);
-		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (p4d++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
@@ -677,33 +701,51 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
  * The caller is responsible for calling flush_cache_vmap() after this
  * function returns successfully and before the addresses are accessed.
  *
+ * On failure the mapping is torn down before returning, so a retry
+ * sees a clean range. When @gfp_mask cannot block, the teardown
+ * skips cond_resched(), it may run in atomic context.
+ *
  * This is an internal function only. Do not use outside mm/.
  */
 int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
-		pgprot_t prot, struct page **pages, unsigned int page_shift)
+		pgprot_t prot, struct page **pages, unsigned int page_shift,
+		gfp_t gfp_mask)
 {
 	unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
+	unsigned long start = addr;
+	int err = 0;
 
-	if (WARN_ON_ONCE(page_shift < PAGE_SHIFT))
+	if (WARN_ON_ONCE(addr >= end))
 		return -EINVAL;
 
-	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
-			page_shift == PAGE_SHIFT)
-		return vmap_small_pages_range_noflush(addr, end, prot, pages);
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(end)))
+		return -EINVAL;
 
-	for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
-		int err;
+	if (WARN_ON_ONCE(page_shift < PAGE_SHIFT))
+		return -EINVAL;
 
-		err = vmap_range_noflush(addr, addr + (1UL << page_shift),
+	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
+			page_shift == PAGE_SHIFT) {
+		err = vmap_small_pages_range_noflush(addr, end, prot, pages);
+	} else {
+		for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
+			err = vmap_range_noflush(addr,
+					addr + (1UL << page_shift),
 					page_to_phys(pages[i]), prot,
 					page_shift);
-		if (err)
-			return err;
+			if (err)
+				break;
 
-		addr += 1UL << page_shift;
+			addr += 1UL << page_shift;
+		}
 	}
 
-	return 0;
+	/* Undo the PTEs installed before the failure. */
+	if (err)
+		__vunmap_range_noflush(start, end,
+				       gfpflags_allow_blocking(gfp_mask));
+
+	return err;
 }
 
 int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
@@ -715,7 +757,18 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
 
 	if (ret)
 		return ret;
-	return __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
+
+	ret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,
+					 gfp_mask);
+	/*
+	 * The page tables undo themselves on failure. Tear down the
+	 * metadata that was fully set up before the mapping failed.
+	 */
+	if (ret)
+		kmsan_vunmap_range_noflush(addr, end,
+					   gfpflags_allow_blocking(gfp_mask));
+
+	return ret;
 }
 
 static int __vmap_pages_range(unsigned long addr, unsigned long end,
@@ -725,8 +778,11 @@ static int __vmap_pages_range(unsigned long addr, unsigned long end,
 	int err;
 
 	err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);
+	if (err)
+		return err;
+
 	flush_cache_vmap(addr, end);
-	return err;
+	return 0;
 }
 
 /**
diff --git a/mm/vmalloc.h b/mm/vmalloc.h
index d8372a4f65c4c..9ea28845f7133 100644
--- a/mm/vmalloc.h
+++ b/mm/vmalloc.h
@@ -40,10 +40,12 @@ void clear_vm_uninitialized_flag(struct vm_struct *vm);
 
 int __must_check __vmap_pages_range_noflush(unsigned long addr,
 		unsigned long end, pgprot_t prot,
-		struct page **pages, unsigned int page_shift);
+		struct page **pages, unsigned int page_shift,
+		gfp_t gfp_mask);
 
 void vunmap_range_noflush(unsigned long start, unsigned long end);
 
-void __vunmap_range_noflush(unsigned long start, unsigned long end);
+void __vunmap_range_noflush(unsigned long start, unsigned long end,
+			    bool can_resched);
 
 #endif /* __MM_VMALLOC_H */

3/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 9346
							output: 36
							thoughts: 1339
						

Thoughts:
**Patch Analysis and Fuzzing Prioritization**

Okay, let's break down this patch. My initial assessment is that it looks like a significant refactor within the memory management subsystem, which immediately raises a red flag in terms of fuzzing potential.

First, I need to understand what's actually being modified. The diff touches several files: `arch/powerpc/kernel/isa-bridge.c`, `arch/powerpc/kernel/pci_64.c`, `include/linux/kmsan.h`, `mm/kmsan/hooks.c`, `mm/kmsan/shadow.c`, `mm/percpu-vm.c`, `mm/vmalloc.c`, and `mm/vmalloc.h`. Most of the changes are centered around `mm/vmalloc.c` and KMSAN related code, which is concerning because `mm/vmalloc.c` is definitely a core memory management component. The PowerPC changes look like incidental cleanups around `vunmap_range` error handling.

The key modifications seem to be in error handling, teardown, and adding assertions. Specifically, there are new `WARN_ON_ONCE` checks being added, checking for page alignment in `vunmap_pte_range`, `vmap_pages_pte_range`, and several other places, along with checks that `addr < end` and page alignment in `__vmap_pages_range_noflush`. Error propagation is being improved as well. This indicates a focus on hardening, which means existing fuzzing coverage may be insufficient.

The introduction of `can_resched` in several `vunmap` functions and the passing of `gfp_mask` to functions like `__vmap_pages_range_noflush` and `vmap_pages_range_noflush` is interesting. It indicates changes in how memory allocation and deallocation interacts with scheduling, which always adds more complexity.

Now, considering the triage guidelines, this patch screams "WorthFuzzing=true." It modifies the core memory management subsystem. There are definitely logic modifications in the `mm/` directory. Refactoring is taking place, especially in error handling and teardown. And, crucially, we have the introduction of new runtime assertions (`WARN_ON_ONCE`). This ticks all the boxes.

Given that WorthFuzzing is TRUE, I need to focus the fuzzing. To figure out the focus, let's explore where the newly modified functions (`__vmap_pages_range_noflush`, `vmap_pages_range_noflush`, `vmap_page_range`, `vmap`, and `vmalloc`) are called. I'll need to use `grepper` or equivalent tools to identify the call sites and their context to understand the likely fuzzing targets. From there, I need to determine the right configurations to enable for this level of fuzzer coverage.



4/2 2026/10/26 09:10 tool grepper
0m Args:
{
  "Expression": "vmap_pages_range",
  "PathPrefixes": [
    "mm/",
    "include/linux/"
  ]
}

Results:
{
  "Output": "include/linux/kmsan.h=127=void kmsan_kfree_large(const void *ptr);\n--\ninclude/linux/kmsan.h-141- */\ninclude/linux/kmsan.h:142:int __must_check kmsan_vmap_pages_range_noflush(unsigned long start,\ninclude/linux/kmsan.h-143-\t\t\t\t\t\tunsigned long end,\n--\ninclude/linux/kmsan.h=349=static inline void kmsan_kfree_large(const void *ptr)\n--\ninclude/linux/kmsan.h-352-\ninclude/linux/kmsan.h:353:static inline int __must_check kmsan_vmap_pages_range_noflush(\ninclude/linux/kmsan.h-354-\tunsigned long start, unsigned long end, pgprot_t prot,\n--\ninclude/linux/vmalloc.h=230=extern int remap_vmalloc_range(struct vm_area_struct *vma, void *addr,\n--\ninclude/linux/vmalloc.h-232-\ninclude/linux/vmalloc.h:233:int vmap_pages_range(unsigned long addr, unsigned long end, pgprot_t prot,\ninclude/linux/vmalloc.h-234-\t\t     struct page **pages, unsigned int page_shift);\n--\nmm/alloc_tag.c=777=static int vm_module_tags_populate(void)\n--\nmm/alloc_tag.c-802-\t\tif (nr \u003c more_pages ||\nmm/alloc_tag.c:803:\t\t    vmap_pages_range(phys_end, phys_end + (nr \u003c\u003c PAGE_SHIFT), PAGE_KERNEL,\nmm/alloc_tag.c-804-\t\t\t\t     next_page, PAGE_SHIFT) \u003c 0) {\n--\nmm/kmsan/hooks.c=189=int kmsan_ioremap_page_range(unsigned long start, unsigned long end,\n--\nmm/kmsan/hooks.c-210-\t\t}\nmm/kmsan/hooks.c:211:\t\tmapped = __vmap_pages_range_noflush(\nmm/kmsan/hooks.c-212-\t\t\tvmalloc_shadow(start + off),\n--\nmm/kmsan/hooks.c-220-\t\tmapped_end = start + off + PAGE_SIZE;\nmm/kmsan/hooks.c:221:\t\tmapped = __vmap_pages_range_noflush(\nmm/kmsan/hooks.c-222-\t\t\tvmalloc_origin(start + off),\n--\nmm/kmsan/shadow.c=206=void kmsan_free_page(struct page *page, unsigned int order)\n--\nmm/kmsan/shadow.c-216-\nmm/kmsan/shadow.c:217:int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,\nmm/kmsan/shadow.c-218-\t\t\t\t   pgprot_t prot, struct page **pages,\n--\nmm/kmsan/shadow.c-248-\tkmsan_enter_runtime();\nmm/kmsan/shadow.c:249:\tmapped = __vmap_pages_range_noflush(shadow_start, shadow_end, prot,\nmm/kmsan/shadow.c-250-\t\t\t\t\t    s_pages, page_shift, gfp_mask);\n--\nmm/kmsan/shadow.c-256-\tkmsan_enter_runtime();\nmm/kmsan/shadow.c:257:\tmapped = __vmap_pages_range_noflush(origin_start, origin_end, prot,\nmm/kmsan/shadow.c-258-\t\t\t\t\t    o_pages, page_shift, gfp_mask);\n--\nmm/percpu-vm.c=201=static int __pcpu_map_pages(unsigned long addr, struct page **pages,\n--\nmm/percpu-vm.c-212-\tflags = memalloc_apply_gfp_scope(gfp);\nmm/percpu-vm.c:213:\tret = vmap_pages_range_noflush(addr, addr + (nr_pages \u003c\u003c PAGE_SHIFT),\nmm/percpu-vm.c-214-\t\t\t\t       PAGE_KERNEL, pages, PAGE_SHIFT, gfp);\n--\nmm/vmalloc.c=670=static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-697-/*\nmm/vmalloc.c:698: * vmap_pages_range_noflush is similar to vmap_pages_range, but does not\nmm/vmalloc.c-699- * flush caches.\n--\nmm/vmalloc.c-709- */\nmm/vmalloc.c:710:int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.c-711-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n--\nmm/vmalloc.c-750-\nmm/vmalloc.c:751:int vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.c-752-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n--\nmm/vmalloc.c-754-{\nmm/vmalloc.c:755:\tint ret = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,\nmm/vmalloc.c-756-\t\t\t\t\t\tpage_shift, gfp_mask);\n--\nmm/vmalloc.c-760-\nmm/vmalloc.c:761:\tret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,\nmm/vmalloc.c-762-\t\t\t\t\t gfp_mask);\n--\nmm/vmalloc.c-773-\nmm/vmalloc.c:774:static int __vmap_pages_range(unsigned long addr, unsigned long end,\nmm/vmalloc.c-775-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n--\nmm/vmalloc.c-779-\nmm/vmalloc.c:780:\terr = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);\nmm/vmalloc.c-781-\tif (err)\n--\nmm/vmalloc.c-788-/**\nmm/vmalloc.c:789: * vmap_pages_range - map pages to a kernel virtual address\nmm/vmalloc.c-790- * @addr: start of the VM area to map\n--\nmm/vmalloc.c-799- */\nmm/vmalloc.c:800:int vmap_pages_range(unsigned long addr, unsigned long end,\nmm/vmalloc.c-801-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift)\nmm/vmalloc.c-802-{\nmm/vmalloc.c:803:\treturn __vmap_pages_range(addr, end, prot, pages, page_shift, GFP_KERNEL);\nmm/vmalloc.c-804-}\n--\nmm/vmalloc.c=831=int vm_area_map_pages(struct vm_struct *area, unsigned long start,\n--\nmm/vmalloc.c-839-\nmm/vmalloc.c:840:\treturn vmap_pages_range(start, end, PAGE_KERNEL, pages, PAGE_SHIFT);\nmm/vmalloc.c-841-}\n--\nmm/vmalloc.c=3186=void *vm_map_ram(struct page **pages, unsigned int count, int node)\n--\nmm/vmalloc.c-3209-\nmm/vmalloc.c:3210:\tif (vmap_pages_range(addr, addr + size, PAGE_KERNEL,\nmm/vmalloc.c-3211-\t\t\t\tpages, PAGE_SHIFT) \u003c 0) {\n--\nmm/vmalloc.c=3665=void *vmap(struct page **pages, unsigned int count,\n--\nmm/vmalloc.c-3692-\taddr = (unsigned long)area-\u003eaddr;\nmm/vmalloc.c:3693:\tif (vmap_pages_range(addr, addr + size, pgprot_nx(prot),\nmm/vmalloc.c-3694-\t\t\t\tpages, PAGE_SHIFT) \u003c 0) {\n--\nmm/vmalloc.c=3774=vm_area_alloc_pages(gfp_t gfp, int nid,\n--\nmm/vmalloc.c-3789-\t * pages. Do not attempt allocating smaller than order chunks since\nmm/vmalloc.c:3790:\t * __vmap_pages_range() expects physically contiguous pages of exactly\nmm/vmalloc.c-3791-\t * order long chunks.\n--\nmm/vmalloc.c=3971=static void *__vmalloc_area_node(struct vm_struct *area, gfp_t gfp_mask,\n--\nmm/vmalloc.c-4048-\tdo {\nmm/vmalloc.c:4049:\t\tret = __vmap_pages_range(addr, addr + size, prot, area-\u003epages,\nmm/vmalloc.c-4050-\t\t\t\tpage_shift, nested_gfp);\n--\nmm/vmalloc.h=12=void __init vmalloc_init(void);\nmm/vmalloc.h:13:int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.h-14-\t\tpgprot_t prot, struct page **pages,\n--\nmm/vmalloc.h=20=static inline\nmm/vmalloc.h:21:int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.h-22-\t\tpgprot_t prot, struct page **pages,\n--\nmm/vmalloc.h=39=void clear_vm_uninitialized_flag(struct vm_struct *vm);\nmm/vmalloc.h-40-\nmm/vmalloc.h:41:int __must_check __vmap_pages_range_noflush(unsigned long addr,\nmm/vmalloc.h-42-\t\tunsigned long end, pgprot_t prot,\n"
}

5/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13955
							output: 20
							thoughts: 18
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Investigating `vmap_page_range` Usage**

Okay, so we're looking at a potential issue, and my first instinct is to trace the callers of `vmap_page_range`. Understanding *how* and *why* this function is being invoked is critical to pinpointing the root cause. This isn't just about identifying the direct calls; it's about seeing the *context* surrounding those calls. Are we dealing with a specific driver? Is this memory allocation related to I/O? Are there patterns based on kernel version, hardware, or workload that appear? Tracing the call stack will probably tell the story, but I need to ensure a holistic view. I’m thinking in terms of the bigger picture, in which this will help to fully understand the issue.



6/2 2026/10/26 09:10 tool grepper
0m Args:
{
  "Expression": "vmap_page_range"
}

Results:
{
  "Output": "arch/arm/mm/ioremap.c=111=int ioremap_page(unsigned long virt, unsigned long phys,\n--\narch/arm/mm/ioremap.c-113-{\narch/arm/mm/ioremap.c:114:\treturn vmap_page_range(virt, virt + PAGE_SIZE, phys,\narch/arm/mm/ioremap.c-115-\t\t\t       __pgprot(mtype-\u003eprot_pte));\n--\narch/arm/mm/ioremap.c=484=int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)\n--\narch/arm/mm/ioremap.c-493-\narch/arm/mm/ioremap.c:494:\treturn vmap_page_range(vaddr, vaddr + resource_size(res), phys_addr,\narch/arm/mm/ioremap.c-495-\t\t\t       __pgprot(get_mem_type(pci_ioremap_mem_type)-\u003eprot_pte));\n--\narch/loongarch/kernel/acpi.c=70=void acpi_add_early_pio(void)\n--\narch/loongarch/kernel/acpi.c-73-\t\tacpi_pio = true;\narch/loongarch/kernel/acpi.c:74:\t\tvmap_page_range(PIO_BASE, PIO_BASE + PIO_SIZE,\narch/loongarch/kernel/acpi.c-75-\t\t\t\tLOONGSON_LIO_BASE, pgprot_device(PAGE_KERNEL));\n--\narch/loongarch/kernel/setup.c=466=static int __init add_legacy_isa_io(struct fwnode_handle *fwnode,\n--\narch/loongarch/kernel/setup.c-495-\tvaddr = (unsigned long)(PCI_IOBASE + range-\u003eio_start);\narch/loongarch/kernel/setup.c:496:\tvmap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));\narch/loongarch/kernel/setup.c-497-\n--\narch/mips/loongson64/init.c=153=static int __init add_legacy_isa_io(struct fwnode_handle *fwnode, resource_size_t hw_start,\n--\narch/mips/loongson64/init.c-183-\narch/mips/loongson64/init.c:184:\tvmap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));\narch/mips/loongson64/init.c-185-\n--\narch/powerpc/kernel/isa-bridge.c=42=static void remap_isa_base(phys_addr_t pa, unsigned long size)\n--\narch/powerpc/kernel/isa-bridge.c-48-\tif (slab_is_available()) {\narch/powerpc/kernel/isa-bridge.c:49:\t\tvmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,\narch/powerpc/kernel/isa-bridge.c-50-\t\t\t\tpgprot_noncached(PAGE_KERNEL));\n--\ndrivers/pci/pci.c=4112=int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)\n--\ndrivers/pci/pci.c-4122-\ndrivers/pci/pci.c:4123:\treturn vmap_page_range(vaddr, vaddr + resource_size(res), phys_addr,\ndrivers/pci/pci.c-4124-\t\t\t       pgprot_device(PAGE_KERNEL));\n--\ninclude/linux/io.h=28=int ioremap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/io.h-29-\t\t       phys_addr_t phys_addr, pgprot_t prot);\ninclude/linux/io.h:30:int vmap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/io.h-31-\t\t    phys_addr_t phys_addr, pgprot_t prot);\n--\ninclude/linux/io.h=33=static inline int ioremap_page_range(unsigned long addr, unsigned long end,\n--\ninclude/linux/io.h-37-}\ninclude/linux/io.h:38:static inline int vmap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/io.h-39-\t\t\t\t  phys_addr_t phys_addr, pgprot_t prot)\n--\nkernel/trace/trace.c-52-#include \u003clinux/sort.h\u003e\nkernel/trace/trace.c:53:#include \u003clinux/io.h\u003e /* vmap_page_range() */\nkernel/trace/trace.c-54-#include \u003clinux/fs_context.h\u003e\n--\nkernel/trace/trace.c=8786=static u64 map_pages(unsigned long start, unsigned long size)\n--\nkernel/trace/trace.c-8798-\nkernel/trace/trace.c:8799:\tret = vmap_page_range(vmap_start, vmap_end,\nkernel/trace/trace.c-8800-\t\t\t      start, pgprot_nx(PAGE_KERNEL));\n--\nmm/vmalloc.c=321=static int vmap_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-354-\nmm/vmalloc.c:355:int vmap_page_range(unsigned long addr, unsigned long end,\nmm/vmalloc.c-356-\t\t    phys_addr_t phys_addr, pgprot_t prot)\n--\nmm/vmalloc.c=375=int ioremap_page_range(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-391-\t}\nmm/vmalloc.c:392:\treturn vmap_page_range(addr, end, phys_addr, prot);\nmm/vmalloc.c-393-}\n"
}

7/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 15731
							output: 34
							thoughts: 159
						

Thoughts:
**Analyzing the Patch Series**

Okay, first things first, I need to understand the context of this patch series. Since I don't have direct access to `git log` to examine the HEAD commit, I'll have to work with what's available. Hmm, it looks like I'm limited to `grepper`, `read-file`, `codesearch-dir-index`, and `set-results`. Alright, that's not ideal, but we can make this work.

It appears the author is listed as `syz-cluster <triage@syzkaller.com>`, and the subject is a generic "applied patch under review". So, the commit message itself isn't particularly insightful, just the generic message: `syz-cluster: applied patch under review`. This means I'll need to dig deeper into the code itself to understand the changes.

Given the generic nature of the commit message, my next step is clear: I'll focus on the modified code. Let's start by examining `mm/vmalloc.c`, specifically around lines 350-750. That should give me a good view of the core functions that were modified in detail. I'm hoping to get a clearer picture of the problem this patch is addressing and what changes it's introducing. Understanding this section of the code will be crucial to assessing the potential impact of these changes.



8/2 2026/10/26 09:10 tool read-file
0m Args:
{
  "File": "mm/vmalloc.c",
  "FirstLine": 355,
  "LineCount": 50
}

Results:
{
  "Contents": " 355:\tint vmap_page_range(unsigned long addr, unsigned long end,\n 356:\t\t\t    phys_addr_t phys_addr, pgprot_t prot)\n 357:\t{\n 358:\t\tint err;\n 359:\t\n 360:\t\terr = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),\n 361:\t\t\t\t\t ioremap_max_page_shift);\n 362:\t\tif (!err)\n 363:\t\t\terr = kmsan_ioremap_page_range(addr, end, phys_addr, prot,\n 364:\t\t\t\t\t\t       ioremap_max_page_shift);\n 365:\t\tif (err) {\n 366:\t\t\t/* Callers are ioremap and arch setup code, always sleepable. */\n 367:\t\t\t__vunmap_range_noflush(addr, end, true);\n 368:\t\t\treturn err;\n 369:\t\t}\n 370:\t\n 371:\t\tflush_cache_vmap(addr, end);\n 372:\t\treturn 0;\n 373:\t}\n 374:\t\n 375:\tint ioremap_page_range(unsigned long addr, unsigned long end,\n 376:\t\t\tphys_addr_t phys_addr, pgprot_t prot)\n 377:\t{\n 378:\t\tstruct vm_struct *area;\n 379:\t\n 380:\t\tarea = find_vm_area((void *)addr);\n 381:\t\tif (!area || !(area-\u003eflags \u0026 VM_IOREMAP)) {\n 382:\t\t\tWARN_ONCE(1, \"vm_area at addr %lx is not marked as VM_IOREMAP\\n\", addr);\n 383:\t\t\treturn -EINVAL;\n 384:\t\t}\n 385:\t\tif (addr != (unsigned long)area-\u003eaddr ||\n 386:\t\t    (void *)end != area-\u003eaddr + get_vm_area_size(area)) {\n 387:\t\t\tWARN_ONCE(1, \"ioremap request [%lx,%lx) doesn't match vm_area [%lx, %lx)\\n\",\n 388:\t\t\t\t  addr, end, (long)area-\u003eaddr,\n 389:\t\t\t\t  (long)area-\u003eaddr + get_vm_area_size(area));\n 390:\t\t\treturn -ERANGE;\n 391:\t\t}\n 392:\t\treturn vmap_page_range(addr, end, phys_addr, prot);\n 393:\t}\n 394:\t\n 395:\tstatic void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,\n 396:\t\t\t\t     pgtbl_mod_mask *mask)\n 397:\t{\n 398:\t\tpte_t *pte;\n 399:\t\tpte_t ptent;\n 400:\t\tunsigned long size = PAGE_SIZE;\n 401:\t\n 402:\t\tif (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))\n 403:\t\t\tend = addr + PAGE_ALIGN_DOWN(end - addr);\n 404:\t\tif (end == addr)\n"
}

9/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17063
							output: 34
							thoughts: 24
						

Thoughts:
Okay, here's my take on those lines of code in `mm/vmalloc.c`, from 550 to 750.

**Diving into `mm/vmalloc.c` – A Closer Look**

Alright, let's zoom in on `mm/vmalloc.c`, specifically lines 550 to 750. I know this area deals with the `vmalloc` system, which is critical for allocating virtually contiguous memory. From the start of this section, I expect to be seeing the guts of how non-contiguous physical pages get stitched together to appear contiguous to the kernel. I'm hoping to see the algorithms for page mapping and any complexities in accounting for the virtual address space.

I'm mentally preparing to scrutinize how they handle the potential for fragmentation, and how they manage allocating pages that might not be physically adjacent. I'll be particularly interested in whether they are doing any clever tricks, such as using a buddy allocator or a similar memory management scheme for the backing physical pages. I expect to be checking for any locking primitives that are used here, for concurrent access to the data structures involved in the address space mapping.

Given this is a core memory management function, I'll be looking for potential performance bottlenecks. Are there any critical sections where contention might become an issue? Is there any caching or prefetching going on to improve the speed of the allocation? I'll also be searching for any hints on how this allocation interfaces with the physical memory, and the hardware's page table structures. Finally, of course, I'll be paying close attention to any error handling and recovery mechanisms, such as what happens if the allocation fails, or if memory is exhausted. This should give me a good insight into the robustness of the implementation.



10/2 2026/10/26 09:10 tool read-file
0m Args:
{
  "File": "mm/vmalloc.c",
  "FirstLine": 550,
  "LineCount": 80
}

Results:
{
  "Contents": " 550:\t * @end: end of the VM area to unmap (non-inclusive)\n 551:\t *\n 552:\t * Clears any present PTEs in the virtual address range, flushes TLBs and\n 553:\t * caches. Any subsequent access to the address before it has been re-mapped\n 554:\t * is a kernel bug.\n 555:\t */\n 556:\tvoid vunmap_range(unsigned long addr, unsigned long end)\n 557:\t{\n 558:\t\tflush_cache_vunmap(addr, end);\n 559:\t\tvunmap_range_noflush(addr, end);\n 560:\t\tflush_tlb_kernel_range(addr, end);\n 561:\t}\n 562:\t\n 563:\tstatic int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,\n 564:\t\t\tunsigned long end, pgprot_t prot, struct page **pages, int *nr,\n 565:\t\t\tpgtbl_mod_mask *mask)\n 566:\t{\n 567:\t\tint err = 0;\n 568:\t\tpte_t *pte;\n 569:\t\n 570:\t\tif (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))\n 571:\t\t\treturn -EINVAL;\n 572:\t\n 573:\t\t/*\n 574:\t\t * nr is a running index into the array which helps higher level\n 575:\t\t * callers keep track of where we're up to.\n 576:\t\t */\n 577:\t\n 578:\t\tpte = pte_alloc_kernel_track(pmd, addr, mask);\n 579:\t\tif (!pte)\n 580:\t\t\treturn -ENOMEM;\n 581:\t\n 582:\t\tlazy_mmu_mode_enable();\n 583:\t\n 584:\t\tdo {\n 585:\t\t\tstruct page *page = pages[*nr];\n 586:\t\n 587:\t\t\tif (WARN_ON(!pte_none(ptep_get(pte)))) {\n 588:\t\t\t\terr = -EBUSY;\n 589:\t\t\t\tbreak;\n 590:\t\t\t}\n 591:\t\t\tif (WARN_ON(!page)) {\n 592:\t\t\t\terr = -ENOMEM;\n 593:\t\t\t\tbreak;\n 594:\t\t\t}\n 595:\t\t\tif (WARN_ON(!pfn_valid(page_to_pfn(page)))) {\n 596:\t\t\t\terr = -EINVAL;\n 597:\t\t\t\tbreak;\n 598:\t\t\t}\n 599:\t\n 600:\t\t\tset_pte_at(\u0026init_mm, addr, pte, mk_pte(page, prot));\n 601:\t\t\t(*nr)++;\n 602:\t\t} while (pte++, addr += PAGE_SIZE, addr != end);\n 603:\t\n 604:\t\tlazy_mmu_mode_disable();\n 605:\t\t*mask |= PGTBL_PTE_MODIFIED;\n 606:\t\n 607:\t\treturn err;\n 608:\t}\n 609:\t\n 610:\tstatic int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,\n 611:\t\t\tunsigned long end, pgprot_t prot, struct page **pages, int *nr,\n 612:\t\t\tpgtbl_mod_mask *mask)\n 613:\t{\n 614:\t\tpmd_t *pmd;\n 615:\t\tunsigned long next;\n 616:\t\tint err = 0;\n 617:\t\n 618:\t\tpmd = pmd_alloc_track(\u0026init_mm, pud, addr, mask);\n 619:\t\tif (!pmd)\n 620:\t\t\treturn -ENOMEM;\n 621:\t\tdo {\n 622:\t\t\tnext = pmd_addr_end(addr, end);\n 623:\t\t\terr = vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask);\n 624:\t\t\tif (err)\n 625:\t\t\t\tbreak;\n 626:\t\t} while (pmd++, addr = next, addr != end);\n 627:\t\treturn err;\n 628:\t}\n 629:\t\n"
}

11/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18652
							output: 34
							thoughts: 25
						

Thoughts:
Okay, let's dive into this. Here's my take on those lines in `mm/vmalloc.c`:

**Focusing on vmalloc.c - Lines 700-800**

Alright, my internal compiler is starting to whir. I need to get my head back in the game on this section of `mm/vmalloc.c`. This is where the magic (or the potential headaches) happen. My immediate goal is to pinpoint the exact functionality residing within lines 700 through 800. Specifically, I'm going to look for the core logic, or any key structures defined/used in this block of code that's related to the `vmalloc` implementation. What's the function doing there, is it allocating a specific chunk of memory, or something else. I'll need to figure out which part of the process this section is involved in, and what the dependencies are. I'll be looking for any clues that will help me understand its purpose and how it fits into the broader picture of virtual memory management within the kernel. Let's see if there are any obvious data structures being initialized or manipulated here as well. Let's see if I can find my answer.



12/2 2026/10/26 09:10 tool read-file
0m Args:
{
  "File": "mm/vmalloc.c",
  "FirstLine": 700,
  "LineCount": 80
}

Results:
{
  "Contents": " 700:\t *\n 701:\t * The caller is responsible for calling flush_cache_vmap() after this\n 702:\t * function returns successfully and before the addresses are accessed.\n 703:\t *\n 704:\t * On failure the mapping is torn down before returning, so a retry\n 705:\t * sees a clean range. When @gfp_mask cannot block, the teardown\n 706:\t * skips cond_resched(), it may run in atomic context.\n 707:\t *\n 708:\t * This is an internal function only. Do not use outside mm/.\n 709:\t */\n 710:\tint __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n 711:\t\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n 712:\t\t\tgfp_t gfp_mask)\n 713:\t{\n 714:\t\tunsigned int i, nr = (end - addr) \u003e\u003e PAGE_SHIFT;\n 715:\t\tunsigned long start = addr;\n 716:\t\tint err = 0;\n 717:\t\n 718:\t\tif (WARN_ON_ONCE(addr \u003e= end))\n 719:\t\t\treturn -EINVAL;\n 720:\t\n 721:\t\tif (WARN_ON_ONCE(!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(end)))\n 722:\t\t\treturn -EINVAL;\n 723:\t\n 724:\t\tif (WARN_ON_ONCE(page_shift \u003c PAGE_SHIFT))\n 725:\t\t\treturn -EINVAL;\n 726:\t\n 727:\t\tif (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||\n 728:\t\t\t\tpage_shift == PAGE_SHIFT) {\n 729:\t\t\terr = vmap_small_pages_range_noflush(addr, end, prot, pages);\n 730:\t\t} else {\n 731:\t\t\tfor (i = 0; i \u003c nr; i += 1U \u003c\u003c (page_shift - PAGE_SHIFT)) {\n 732:\t\t\t\terr = vmap_range_noflush(addr,\n 733:\t\t\t\t\t\taddr + (1UL \u003c\u003c page_shift),\n 734:\t\t\t\t\t\tpage_to_phys(pages[i]), prot,\n 735:\t\t\t\t\t\tpage_shift);\n 736:\t\t\t\tif (err)\n 737:\t\t\t\t\tbreak;\n 738:\t\n 739:\t\t\t\taddr += 1UL \u003c\u003c page_shift;\n 740:\t\t\t}\n 741:\t\t}\n 742:\t\n 743:\t\t/* Undo the PTEs installed before the failure. */\n 744:\t\tif (err)\n 745:\t\t\t__vunmap_range_noflush(start, end,\n 746:\t\t\t\t\t       gfpflags_allow_blocking(gfp_mask));\n 747:\t\n 748:\t\treturn err;\n 749:\t}\n 750:\t\n 751:\tint vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n 752:\t\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n 753:\t\t\tgfp_t gfp_mask)\n 754:\t{\n 755:\t\tint ret = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,\n 756:\t\t\t\t\t\t\tpage_shift, gfp_mask);\n 757:\t\n 758:\t\tif (ret)\n 759:\t\t\treturn ret;\n 760:\t\n 761:\t\tret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,\n 762:\t\t\t\t\t\t gfp_mask);\n 763:\t\t/*\n 764:\t\t * The page tables undo themselves on failure. Tear down the\n 765:\t\t * metadata that was fully set up before the mapping failed.\n 766:\t\t */\n 767:\t\tif (ret)\n 768:\t\t\tkmsan_vunmap_range_noflush(addr, end,\n 769:\t\t\t\t\t\t   gfpflags_allow_blocking(gfp_mask));\n 770:\t\n 771:\t\treturn ret;\n 772:\t}\n 773:\t\n 774:\tstatic int __vmap_pages_range(unsigned long addr, unsigned long end,\n 775:\t\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n 776:\t\t\tgfp_t gfp_mask)\n 777:\t{\n 778:\t\tint err;\n 779:\t\n"
}

13/2 2026/10/26 09:10 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 20191
							output: 38
							thoughts: 230
						

Thoughts:
**Tracing the Memory Mapping Hierarchy**

Okay, let's trace this out. I need to understand the connection between `__vmalloc` and `vmap` and the core functions involved in mapping memory. Right, starting with `__vmalloc_area_node`, I see that it ultimately calls `__vmap_pages_range`. Hold on... that's the key call!

So, `__vmalloc_area_node` *itself* uses `__vmap_pages_range`. And just to confirm, `vmap` also leads directly into `vmap_pages_range` which ultimately calls `__vmap_pages_range`, too. And let's not forget `vm_map_ram`, that also uses the same call path. I need to keep that in mind.

Ah, and I see `pcpu_map_pages` calls `vmap_pages_range_noflush`, which probably has an important difference, although it still points back to the underlying `__vmap_pages_range`. Finally, on the other end, when it comes to unmapping memory, `vunmap` and `vfree` call into either `vunmap_range` or `vunmap_range_noflush`, which eventually leads to `__vunmap_range_noflush`.

Now, I should shift my attention to `vunmap_range` and the functions that call it within `mm/vmalloc.c`. I need to fully understand how these unmapping operations relate to the mapping process I've just traced.



14/2 2026/10/26 09:11 tool grepper
0m Args:
{
  "Expression": "vunmap_range|__vunmap_range_noflush",
  "PathPrefixes": [
    "mm/"
  ]
}

Results:
{
  "Output": "mm/kmsan/hooks.c=133=static unsigned long vmalloc_origin(unsigned long addr)\n--\nmm/kmsan/hooks.c-138-\nmm/kmsan/hooks.c:139:void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,\nmm/kmsan/hooks.c-140-\t\t\t\tbool can_resched)\n--\nmm/kmsan/hooks.c-144-\nmm/kmsan/hooks.c:145:\t__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end),\nmm/kmsan/hooks.c-146-\t\t\t       can_resched);\nmm/kmsan/hooks.c:147:\t__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end),\nmm/kmsan/hooks.c-148-\t\t\t       can_resched);\n--\nmm/kmsan/hooks.c=155=static void kmsan_iounmap_pages(unsigned long start, unsigned long end)\n--\nmm/kmsan/hooks.c-177-\t/* ioremap metadata mappings are always GFP_KERNEL, so resched is safe. */\nmm/kmsan/hooks.c:178:\t__vunmap_range_noflush(shadow_start, shadow_end, true);\nmm/kmsan/hooks.c:179:\t__vunmap_range_noflush(origin_start, origin_end, true);\nmm/kmsan/hooks.c-180-\tflush_tlb_kernel_range(shadow_start, shadow_end);\n--\nmm/kmsan/shadow.c=217=int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,\n--\nmm/kmsan/shadow.c-262-\t\tkmsan_enter_runtime();\nmm/kmsan/shadow.c:263:\t\t__vunmap_range_noflush(shadow_start, shadow_end,\nmm/kmsan/shadow.c-264-\t\t\t\t       gfpflags_allow_blocking(gfp_mask));\n--\nmm/percpu-vm.c=143=static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)\nmm/percpu-vm.c-144-{\nmm/percpu-vm.c:145:\tvunmap_range_noflush(addr, addr + (nr_pages \u003c\u003c PAGE_SHIFT));\nmm/percpu-vm.c-146-}\n--\nmm/vmalloc.c=355=int vmap_page_range(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-366-\t\t/* Callers are ioremap and arch setup code, always sleepable. */\nmm/vmalloc.c:367:\t\t__vunmap_range_noflush(addr, end, true);\nmm/vmalloc.c-368-\t\treturn err;\n--\nmm/vmalloc.c=485=static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-505-/*\nmm/vmalloc.c:506: * vunmap_range_noflush is similar to vunmap_range, but does not\nmm/vmalloc.c-507- * flush caches or TLBs.\n--\nmm/vmalloc.c-516- */\nmm/vmalloc.c:517:void __vunmap_range_noflush(unsigned long start, unsigned long end,\nmm/vmalloc.c-518-\t\t\t    bool can_resched)\n--\nmm/vmalloc.c-539-\nmm/vmalloc.c:540:void vunmap_range_noflush(unsigned long start, unsigned long end)\nmm/vmalloc.c-541-{\nmm/vmalloc.c-542-\t/* The unmap paths are sleepable, keep the resched point. */\nmm/vmalloc.c:543:\tkmsan_vunmap_range_noflush(start, end, true);\nmm/vmalloc.c:544:\t__vunmap_range_noflush(start, end, true);\nmm/vmalloc.c-545-}\n--\nmm/vmalloc.c-547-/**\nmm/vmalloc.c:548: * vunmap_range - unmap kernel virtual addresses\nmm/vmalloc.c-549- * @addr: start of the VM area to unmap\n--\nmm/vmalloc.c-555- */\nmm/vmalloc.c:556:void vunmap_range(unsigned long addr, unsigned long end)\nmm/vmalloc.c-557-{\nmm/vmalloc.c-558-\tflush_cache_vunmap(addr, end);\nmm/vmalloc.c:559:\tvunmap_range_noflush(addr, end);\nmm/vmalloc.c-560-\tflush_tlb_kernel_range(addr, end);\n--\nmm/vmalloc.c=710=int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-744-\tif (err)\nmm/vmalloc.c:745:\t\t__vunmap_range_noflush(start, end,\nmm/vmalloc.c-746-\t\t\t\t       gfpflags_allow_blocking(gfp_mask));\n--\nmm/vmalloc.c=751=int vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-767-\tif (ret)\nmm/vmalloc.c:768:\t\tkmsan_vunmap_range_noflush(addr, end,\nmm/vmalloc.c-769-\t\t\t\t\t   gfpflags_allow_blocking(gfp_mask));\n--\nmm/vmalloc.c=849=void vm_area_unmap_pages(struct vm_struct *area, unsigned long start,\n--\nmm/vmalloc.c-854-\nmm/vmalloc.c:855:\tvunmap_range(start, end);\nmm/vmalloc.c-856-}\n--\nmm/vmalloc.c=2571=static void free_unmap_vmap_area(struct vmap_area *va)\n--\nmm/vmalloc.c-2573-\tflush_cache_vunmap(va-\u003eva_start, va-\u003eva_end);\nmm/vmalloc.c:2574:\tvunmap_range_noflush(va-\u003eva_start, va-\u003eva_end);\nmm/vmalloc.c-2575-\tif (debug_pagealloc_enabled_static())\n--\nmm/vmalloc.c=3025=static void vb_free(unsigned long addr, unsigned long size)\n--\nmm/vmalloc.c-3046-\nmm/vmalloc.c:3047:\tvunmap_range_noflush(addr, addr + size);\nmm/vmalloc.c-3048-\n--\nmm/vmalloc.c=3530=void vfree_atomic(const void *addr)\n--\nmm/vmalloc.c-3554- * Freed vm-\u003epages[] entries are set to NULL.\nmm/vmalloc.c:3555: * Caller is responsible for unmapping (vunmap_range) and KASAN\nmm/vmalloc.c-3556- * poisoning before calling this.\n--\nmm/vmalloc.c=4454=void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,\n--\nmm/vmalloc.c-4540-\nmm/vmalloc.c:4541:\t\t\tvunmap_range(addr + (new_nr_pages \u003c\u003c PAGE_SHIFT),\nmm/vmalloc.c-4542-\t\t\t\t     addr + (old_nr_pages \u003c\u003c PAGE_SHIFT));\n--\nmm/vmalloc.h=21=int __must_check vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.h-27-\nmm/vmalloc.h:28:static inline void vunmap_range_noflush(unsigned long start, unsigned long end)\nmm/vmalloc.h-29-{\n--\nmm/vmalloc.h=41=int __must_check __vmap_pages_range_noflush(unsigned long addr,\n--\nmm/vmalloc.h-45-\nmm/vmalloc.h:46:void vunmap_range_noflush(unsigned long start, unsigned long end);\nmm/vmalloc.h-47-\nmm/vmalloc.h:48:void __vunmap_range_noflush(unsigned long start, unsigned long end,\nmm/vmalloc.h-49-\t\t\t    bool can_resched);\n"
}

15/2 2026/10/26 09:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23026
							output: 38
							thoughts: 2316
						

Thoughts:
**Analyzing the Patch's Impact on Kernel Memory Management**

Okay, let's break this down. My initial thought is to understand the overall function of this patch. It seems we're looking at changes related to page mapping and unmapping within the kernel's virtual memory space, specifically when using `vmalloc`-related functions. Functions like `vmap_pages_range_noflush`, `__vmap_pages_range_noflush`, and `vmap_page_range` appear to be the focus. The core issue seems to be that if a mapping fails mid-process, the previous implementation didn't fully clean up after itself, leaving potentially inconsistent page table entries (PTEs) behind. This patch introduces a crucial step: on failure, the code now explicitly tears down the partially installed PTEs, preventing memory corruption or leaks.

Looking deeper, the patch modifies `__vmap_pages_range_noflush` to include this cleanup. It also updates `vmap_pages_range_noflush` to call this cleanup function on errors. Furthermore, `vmap_page_range` now includes similar error handling and cleanup logic. The addition of the `can_resched` argument to `__vunmap_range_noflush` and its helper functions hints at handling both atomic and non-atomic contexts, ensuring that `cond_resched()` isn't called from atomic contexts to prevent deadlocks. The patch also includes various `WARN_ON_ONCE` checks to validate input parameters, like page alignment and valid address ranges, which indicate an attempt to enhance the robustness of these functions. Additionally, the patch includes more checks and immediate breaks in the error paths from lower level functions in `vmap_pages_pmd_range`, etc., to make error handling more explicit.

Now, I need to understand the scope of these changes. Are these code paths frequently traversed? AMD64 certainly uses vmalloc extensively, so the functions touched are relevant. The next question is, what triggers these error paths? Are they common? I'm thinking about the guidelines for fuzzing, specifically avoiding focusing on ubiquitous lifecycle functions. If these functions are called very commonly, that could dilute the fuzzing effort.

I need to list all the modified functions in this patch:

*   In `arch/powerpc/...`: PowerPC specific - irrelevant to my primary focus.
*   In `mm/kmsan/hooks.c`: `kmsan_vunmap_range_noflush`, static `kmsan_iounmap_pages`, `kmsan_ioremap_page_range`.
*   In `mm/kmsan/shadow.c`: `kmsan_vmap_pages_range_noflush`.
*   In `mm/percpu-vm.c`: `pcpu_map_pages` (static, potentially important).
*   In `mm/vmalloc.c`: `vmap_page_range`, static `vunmap_pte_range`, static `vunmap_pmd_range`, static `vunmap_pud_range`, static `vunmap_p4d_range`, non-static `__vunmap_range_noflush`, `vunmap_range_noflush`, static `vmap_pages_pte_range`, static `vmap_pages_pmd_range`, static `vmap_pages_pud_range`, static `vmap_pages_p4d_range`, non-static `__vmap_pages_range_noflush`, `vmap_pages_range_noflush`, static `__vmap_pages_range`.

Hmm, this is a lot, and I'm starting to get concerned. The presence of functions like `vmalloc`, `vfree`, `vmap`, and `vunmap_range` raises a red flag. Are they *too* ubiquitous? The guidelines specifically say to avoid those types of functions. I need to be careful not to introduce too much noise into the fuzzing process.

Let's trace `vfree` and `vunmap_range_noflush`. It appears that `free_unmap_vmap_area` calls `vunmap_range_noflush`, and therefore `__vunmap_range_noflush` in the `vfree` function. And `vfree` is called by every `vfree` or deferred vfree calls in `vfree_atomic` and `unmap`. This is problematic, if `vfree` is ubiquitous! Similarly, `vmalloc` calls `__vmalloc_node_range`, which calls `__vmalloc_area_node`, which calls `__vmap_pages_range`, which calls `vmap_pages_range_noflush`, which then calls `__vmap_


16/2 2026/10/26 09:11 tool grepper
0m Args:
{
  "Expression": "vmap_page_range",
  "PathPrefixes": [
    "arch/x86/",
    "drivers/"
  ]
}

Results:
{
  "Output": "drivers/pci/pci.c=4112=int pci_remap_iospace(const struct resource *res, phys_addr_t phys_addr)\n--\ndrivers/pci/pci.c-4122-\ndrivers/pci/pci.c:4123:\treturn vmap_page_range(vaddr, vaddr + resource_size(res), phys_addr,\ndrivers/pci/pci.c-4124-\t\t\t       pgprot_device(PAGE_KERNEL));\n"
}

17/2 2026/10/26 09:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26544
							output: 20
							thoughts: 71
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Tracing Down `ioremap_page_range` Usage**

Okay, so `ioremap_page_range` is definitely using `vmap_page_range`, that's clear from the direct function call. And, interestingly, that `kernel/trace/trace.c` file also leverages `vmap_page_range` within its `map_pages` function. Hmm, given my understanding of the virtual memory mapping scheme, that's not entirely surprising, but it's good to have confirmed it. The trace use case is one where vmap is particularly handy. Now, I really need to drill down and see *where* `ioremap_page_range` itself is being called within the kernel. I need to get a picture of what modules or drivers depend on it. That's the key to understanding the context of these virtual mappings and how they're being used. Time to go hunting for call sites.



18/2 2026/10/26 09:11 tool grepper
0m Args:
{
  "Expression": "ioremap_page_range"
}

Results:
{
  "Output": "arch/alpha/kernel/proto.h=199=__alpha_remap_area_pages(unsigned long address, unsigned long phys_addr,\n--\narch/alpha/kernel/proto.h-205-\t\t\t| _PAGE_KWE | flags);\narch/alpha/kernel/proto.h:206:\treturn ioremap_page_range(address, address + size, phys_addr, prot);\narch/alpha/kernel/proto.h-207-}\n--\narch/arm/mm/ioremap.c=274=static void __iomem * __arm_ioremap_pfn_caller(unsigned long pfn,\n--\narch/arm/mm/ioremap.c-339-#endif\narch/arm/mm/ioremap.c:340:\t\terr = ioremap_page_range(addr, addr + size, paddr,\narch/arm/mm/ioremap.c-341-\t\t\t\t\t __pgprot(type-\u003eprot_pte));\n--\narch/loongarch/include/asm/kfence.h=17=static inline bool arch_kfence_init_pool(void)\n--\narch/loongarch/include/asm/kfence.h-30-\t__kfence_pool = (char *)area-\u003eaddr;\narch/loongarch/include/asm/kfence.h:31:\terr = ioremap_page_range((unsigned long)__kfence_pool,\narch/loongarch/include/asm/kfence.h-32-\t\t\t\t (unsigned long)__kfence_pool + KFENCE_POOL_SIZE,\n--\narch/mips/mm/ioremap.c=46=void __iomem *ioremap_prot(phys_addr_t phys_addr, unsigned long size,\n--\narch/mips/mm/ioremap.c-107-\tflags |= _PAGE_GLOBAL | _PAGE_PRESENT | __READABLE | __WRITEABLE;\narch/mips/mm/ioremap.c:108:\tif (ioremap_page_range(vaddr, vaddr + size, phys_addr,\narch/mips/mm/ioremap.c-109-\t\t\t__pgprot(flags))) {\n--\narch/powerpc/kernel/pci_64.c=120=void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size)\n--\narch/powerpc/kernel/pci_64.c-140-\taddr = (unsigned long)area-\u003eaddr;\narch/powerpc/kernel/pci_64.c:141:\tif (ioremap_page_range(addr, addr + size, paddr,\narch/powerpc/kernel/pci_64.c-142-\t\t\tpgprot_noncached(PAGE_KERNEL)))\n--\narch/sh/kernel/cpu/sh4/sq.c=101=static int __sq_remap(struct sq_mapping *map, pgprot_t prot)\n--\narch/sh/kernel/cpu/sh4/sq.c-112-\narch/sh/kernel/cpu/sh4/sq.c:113:\tif (ioremap_page_range((unsigned long)vma-\u003eaddr,\narch/sh/kernel/cpu/sh4/sq.c-114-\t\t\t       (unsigned long)vma-\u003eaddr + map-\u003esize,\n--\narch/x86/mm/ioremap.c=184=__ioremap_caller(resource_size_t phys_addr, unsigned long size,\n--\narch/x86/mm/ioremap.c-300-\narch/x86/mm/ioremap.c:301:\tif (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot))\narch/x86/mm/ioremap.c-302-\t\tgoto err_free_area;\n--\ninclude/linux/io.h=24=void __iowrite64_copy(void __iomem *to, const void *from, size_t count);\n--\ninclude/linux/io.h-27-#ifdef CONFIG_MMU\ninclude/linux/io.h:28:int ioremap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/io.h-29-\t\t       phys_addr_t phys_addr, pgprot_t prot);\ninclude/linux/io.h=30=int vmap_page_range(unsigned long addr, unsigned long end,\n--\ninclude/linux/io.h-32-#else\ninclude/linux/io.h:33:static inline int ioremap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/io.h-34-\t\t\t\t     phys_addr_t phys_addr, pgprot_t prot)\n--\ninclude/linux/kmsan.h=159=void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,\n--\ninclude/linux/kmsan.h-162-/**\ninclude/linux/kmsan.h:163: * kmsan_ioremap_page_range() - Notify KMSAN about a ioremap_page_range() call.\ninclude/linux/kmsan.h-164- * @addr:\trange start.\n--\ninclude/linux/kmsan.h-166- * @phys_addr:\tphysical range start.\ninclude/linux/kmsan.h:167: * @prot:\tpage protection flags used for ioremap_page_range().\ninclude/linux/kmsan.h-168- * @page_shift:\tpage_shift argument passed to vmap_range_noflush().\n--\ninclude/linux/kmsan.h-173- */\ninclude/linux/kmsan.h:174:int __must_check kmsan_ioremap_page_range(unsigned long addr, unsigned long end,\ninclude/linux/kmsan.h-175-\t\t\t\t\t  phys_addr_t phys_addr, pgprot_t prot,\n--\ninclude/linux/kmsan.h=360=static inline void kmsan_vunmap_range_noflush(unsigned long start,\n--\ninclude/linux/kmsan.h-365-\ninclude/linux/kmsan.h:366:static inline int __must_check kmsan_ioremap_page_range(unsigned long start,\ninclude/linux/kmsan.h-367-\t\t\t\t\t\t\tunsigned long end,\n--\nmm/ioremap.c=15=void __iomem *generic_ioremap_prot(phys_addr_t phys_addr, size_t size,\n--\nmm/ioremap.c-42-\nmm/ioremap.c:43:\tif (ioremap_page_range(vaddr, vaddr + size, phys_addr, prot)) {\nmm/ioremap.c-44-\t\tfree_vm_area(area);\n--\nmm/kmsan/hooks.c=155=static void kmsan_iounmap_pages(unsigned long start, unsigned long end)\n--\nmm/kmsan/hooks.c-188- */\nmm/kmsan/hooks.c:189:int kmsan_ioremap_page_range(unsigned long start, unsigned long end,\nmm/kmsan/hooks.c-190-\t\t\t     phys_addr_t phys_addr, pgprot_t prot,\n--\nmm/vmalloc.c=355=int vmap_page_range(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-362-\tif (!err)\nmm/vmalloc.c:363:\t\terr = kmsan_ioremap_page_range(addr, end, phys_addr, prot,\nmm/vmalloc.c-364-\t\t\t\t\t       ioremap_max_page_shift);\n--\nmm/vmalloc.c-374-\nmm/vmalloc.c:375:int ioremap_page_range(unsigned long addr, unsigned long end,\nmm/vmalloc.c-376-\t\tphys_addr_t phys_addr, pgprot_t prot)\n"
}

19/2 2026/10/26 09:11 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28829
							output: 29
							thoughts: 171
						

Thoughts:
Here's my analysis of the provided information, as though it were my train of thought:

**Dissecting the Memory Mapping Path**

Okay, so I'm tracing the memory mapping process on x86, trying to understand how this all works at a granular level. The first function I'm seeing is `__ioremap_caller`, and it's calling `ioremap_page_range`, which then goes to `vmap_page_range`. Fine, standard stuff. Now, I need to know where `vmap_pages_range_noflush` gets its data from, since that's where the rubber meets the road. 

Let's see… It looks like `pcpu_map_pages` (which is itself called via `__pcpu_map_pages`) and `__vmap_pages_range` are responsible for calling `vmap_pages_range_noflush`

And what's upstream from `__vmap_pages_range`? `vmap_pages_range` is a huge clue. Seems to be the core, with callers like `vm_map_ram`, `vmap`, `vm_area_map_pages`, and `vm_module_tags_populate`. Oh, and `__vmalloc_area_node` is also in the mix! This is starting to build a clearer picture of the different methods of mapping.

Alright, I want to drill down further. Let's dig into `pcpu_map_pages` now. I'll need to check the `mm/percpu-vm.c` file. How exactly is that one called? Understanding its call chain will give me an even more complete picture of this crucial process.



20/2 2026/10/26 09:12 tool grepper
0m Args:
{
  "Expression": "pcpu_map_pages",
  "PathPrefixes": [
    "mm/"
  ]
}

Results:
{
  "Output": "mm/percpu-vm.c=62=static void pcpu_free_pages(struct pcpu_chunk *chunk,\n--\nmm/percpu-vm.c-87- * The allocation is for @chunk.  Percpu core doesn't care about the\nmm/percpu-vm.c:88: * content of @pages and will pass it verbatim to pcpu_map_pages().\nmm/percpu-vm.c-89- */\n--\nmm/percpu-vm.c=193=static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,\n--\nmm/percpu-vm.c-200-\nmm/percpu-vm.c:201:static int __pcpu_map_pages(unsigned long addr, struct page **pages,\nmm/percpu-vm.c-202-\t\t\t    int nr_pages, gfp_t gfp)\n--\nmm/percpu-vm.c-220-/**\nmm/percpu-vm.c:221: * pcpu_map_pages - map pages into a pcpu_chunk\nmm/percpu-vm.c-222- * @chunk: chunk of interest\n--\nmm/percpu-vm.c-234- */\nmm/percpu-vm.c:235:static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages,\nmm/percpu-vm.c-236-\t\t\t  int page_start, int page_end, gfp_t gfp)\n--\nmm/percpu-vm.c-241-\tfor_each_possible_cpu(cpu) {\nmm/percpu-vm.c:242:\t\terr = __pcpu_map_pages(pcpu_chunk_addr(chunk, cpu, page_start),\nmm/percpu-vm.c-243-\t\t\t\t       \u0026pages[pcpu_page_idx(cpu, page_start)],\n--\nmm/percpu-vm.c=298=static int pcpu_populate_chunk(struct pcpu_chunk *chunk,\n--\nmm/percpu-vm.c-309-\nmm/percpu-vm.c:310:\tif (pcpu_map_pages(chunk, pages, page_start, page_end, gfp)) {\nmm/percpu-vm.c-311-\t\tpcpu_free_pages(chunk, pages, page_start, page_end);\n--\nmm/percpu.c=3198=int __init pcpu_page_first_chunk(size_t reserved_size, pcpu_fc_cpu_to_node_fn_t cpu_to_nd_fn)\n--\nmm/percpu.c-3261-\t\t/* pte already populated, the following shouldn't fail */\nmm/percpu.c:3262:\t\trc = __pcpu_map_pages(unit_addr, \u0026pages[unit * unit_pages],\nmm/percpu.c-3263-\t\t\t\t      unit_pages, GFP_KERNEL);\n"
}

21/2 2026/10/26 09:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30153
							output: 29
							thoughts: 325
						

Thoughts:
**Analyzing `pcpu_map_pages` and its Callers**

Okay, let's dive into this `pcpu_map_pages` function within `mm/percpu-vm.c`. My initial thought is to understand its core operation. I see it iterates through all possible CPUs, attempting to map pages for each one using `__pcpu_map_pages`. The loop handles each CPU and then in case of an error it unmaps the previously mapped pages of other CPUs. The structure using `pcpu_chunk_addr` and `pcpu_page_idx`, suggest a per-CPU data arrangement, which is common in VM systems. The use of `gfp_t` indicates memory allocation flags, and the `err` variable handles any mapping errors. The post-unmap TLB flush after error handling is also interesting.

Now, how does this `pcpu_populate_chunk` come into play? I need to understand the larger context. My next move is to identify the callers of `pcpu_populate_chunk`. Tracing its usage will give me the bigger picture of how this per-CPU memory mapping is initiated and used within the kernel's memory management. Understanding the call stack should provide essential context and enable deeper analysis.



22/2 2026/10/26 09:12 tool grepper
0m Args:
{
  "Expression": "pcpu_populate_chunk",
  "PathPrefixes": [
    "mm/"
  ]
}

Results:
{
  "Output": "mm/percpu-km.c=35=static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,\n--\nmm/percpu-km.c-40-\nmm/percpu-km.c:41:static int pcpu_populate_chunk(struct pcpu_chunk *chunk,\nmm/percpu-km.c-42-\t\t\t       int page_start, int page_end, gfp_t gfp)\n--\nmm/percpu-vm.c=277=static void pcpu_post_map_flush(struct pcpu_chunk *chunk,\n--\nmm/percpu-vm.c-285-/**\nmm/percpu-vm.c:286: * pcpu_populate_chunk - populate and map an area of a pcpu_chunk\nmm/percpu-vm.c-287- * @chunk: chunk of interest\n--\nmm/percpu-vm.c-297- */\nmm/percpu-vm.c:298:static int pcpu_populate_chunk(struct pcpu_chunk *chunk,\nmm/percpu-vm.c-299-\t\t\t       int page_start, int page_end, gfp_t gfp)\n--\nmm/percpu.c=1535=static void pcpu_chunk_depopulated(struct pcpu_chunk *chunk,\n--\nmm/percpu.c-1556- *\nmm/percpu.c:1557: * pcpu_populate_chunk\t\t- populate the specified range of a chunk\nmm/percpu.c-1558- * pcpu_depopulate_chunk\t- depopulate the specified range of a chunk\n--\nmm/percpu.c-1564- */\nmm/percpu.c:1565:static int pcpu_populate_chunk(struct pcpu_chunk *chunk,\nmm/percpu.c-1566-\t\t\t       int page_start, int page_end, gfp_t gfp);\n--\nmm/percpu.c=1734=void __percpu *pcpu_alloc_noprof(size_t size, size_t align, bool reserved,\n--\nmm/percpu.c-1882-\nmm/percpu.c:1883:\t\t\tret = pcpu_populate_chunk(chunk, rs, re, pcpu_gfp);\nmm/percpu.c-1884-\n--\nmm/percpu.c=2012=static void pcpu_balance_populated(void)\n--\nmm/percpu.c-2061-\t\t\tspin_unlock_irq(\u0026pcpu_lock);\nmm/percpu.c:2062:\t\t\tret = pcpu_populate_chunk(chunk, rs, rs + nr, gfp);\nmm/percpu.c-2063-\t\t\tcond_resched();\n"
}

23/2 2026/10/26 09:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31486
							output: 26
							thoughts: 4039
						

Thoughts:
**Focusing on vmalloc Cleanup and KMSAN Integration**

Alright, after reviewing this patch series, my initial assessment is that it's a valuable cleanup effort within the virtual memory management system. It's targeted at ensuring that page tables are correctly cleaned up, particularly in error scenarios within `vmalloc`, and integrates well with KMSAN's metadata management. Specifically, I'm noting the changes that ensure that `vmap_page_range`, `__vmap_pages_range_noflush`, and the KMSAN variants of these functions properly unmap partially allocated pages on failure.  This is a significant improvement, preventing potential memory leaks or corruption that could arise from partially mapped regions.

The key aspects of this patch that pique my interest are the changes to error handling within `vmap_page_range`, `__vmap_pages_range_noflush`, and their KMSAN counterparts (`kmsan_vunmap_range_noflush` and `kmsan_vmap_pages_range_noflush`). These functions are core to how vmalloc allocates and maps physical pages. The addition of `can_resched` to `__vunmap_range_noflush` and `kmsan_vunmap_range_noflush` is crucial for safely handling unmapping in both sleepable and atomic contexts, preventing potential deadlocks. Furthermore, the introduction of `WARN_ON_ONCE` checks to ensure alignment is a welcome addition, tightening the invariants on these key functions. The modifications to `pcpu_map_pages` to rely on the self-cleanup of `__vmap_pages_range_noflush` show a thoughtful integration of these changes.

Given the depth of these changes and the areas they touch, I need to carefully select the focus symbols for fuzzing. To avoid diluting the fuzzer's focus, I'm avoiding any functions that are generic or ubiquitous in the kernel's life cycle. While `__vunmap_range_noflush` is called when `vfree` is called, and `vmalloc` is not ubiquitous as `kmalloc` is, the core functions modified here are specific to `vmalloc`, which is still targeted by this patch series. My plan is to target the functions that directly implement or are altered by the core logic:

*   `__vmap_pages_range_noflush`
*   `vmap_pages_range_noflush`
*   `vmap_page_range`
*   `__vunmap_range_noflush`
*   `kmsan_vmap_pages_range_noflush`
*   `kmsan_vunmap_range_noflush`
*   `kmsan_ioremap_page_range`



24/2 2026/10/26 09:12 tool grepper
0m Args:
{
  "Expression": "kmsan",
  "PathPrefixes": [
    "mm/Makefile"
  ]
}

Results:
{
  "Output": "mm/Makefile=94=obj-$(CONFIG_KFENCE) += kfence/\nmm/Makefile:95:obj-$(CONFIG_KMSAN)\t+= kmsan/\nmm/Makefile-96-obj-$(CONFIG_FAILSLAB) += failslab.o\n"
}

25/2 2026/10/26 09:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36213
							output: 31
							thoughts: 33
						

Thoughts:
Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm speaking to a fellow expert:

**KMSAN Module Inclusion – A Quick Check**

Right, let's get straight to it. I'm seeing this `obj-$(CONFIG_KMSAN) += kmsan/` line, which immediately tells me we're trying to conditionally include the KMSAN module based on the `CONFIG_KMSAN` kernel configuration option. My immediate thought is, before diving further, I need to verify its dependencies in the Kconfig file. I need to be absolutely sure that `CONFIG_KMSAN` is properly defined and doesn't conflict with any other critical settings. It's the standard first step, isn't it? Just want to confirm that we're covering all the necessary bases. This confirms there is more work to be done.



26/2 2026/10/26 09:12 tool grepper
0m Args:
{
  "Expression": "config KMSAN",
  "PathPrefixes": [
    "mm/",
    "lib/"
  ]
}

Results:
{
  "Output": "lib/Kconfig.kmsan=5=config HAVE_KMSAN_COMPILER\n--\nlib/Kconfig.kmsan-7-\nlib/Kconfig.kmsan:8:config KMSAN\nlib/Kconfig.kmsan-9-\tbool \"KMSAN: detector of uninitialized values use\"\n--\nlib/Kconfig.kmsan=26=if KMSAN\nlib/Kconfig.kmsan-27-\nlib/Kconfig.kmsan:28:config KMSAN_CHECK_PARAM_RETVAL\nlib/Kconfig.kmsan-29-\tbool \"Check for uninitialized values passed to and returned from functions\"\n--\nlib/Kconfig.kmsan-41-\nlib/Kconfig.kmsan:42:config KMSAN_KUNIT_TEST\nlib/Kconfig.kmsan-43-\ttristate \"KMSAN integration test suite\" if !KUNIT_ALL_TESTS\n"
}

27/2 2026/10/26 09:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36692
							output: 33
							thoughts: 13
						

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

**Examining the KMSAN Configuration**

Alright, let's dive right into `lib/Kconfig.kmsan`. I'm going to quickly parse this Kconfig file, because I already have a pretty solid grasp on the KMSAN landscape. My focus will be on understanding any recent changes, or perhaps any subtle interactions within the configuration options themselves. I want to see if there's anything new or noteworthy regarding KMSAN's integration, any new knobs that have been added. I'm going to be looking for dependencies, default values, and implications for build configurations. Specifically, I'll be trying to identify:

*   **Any novel KMSAN features or options:** Are there any recent additions related to memory region tracking, reporting, or performance optimization?
*   **Dependency analysis:** How does KMSAN interact with other kernel subsystems? Any newly introduced compile-time dependencies or conflicts that I need to be aware of?
*   **Target Architectures:** Is there any architecture specific configuration I should be keeping an eye on?
*   **Default settings and their impact:** Are there any changes to default behaviors that could influence the overall testing coverage and/or performance of KMSAN?
*   **Build-time impacts:** Anything that will increase compilation time or add other complexities to the build process?

Essentially, I'm aiming for a quick scan to make sure my existing knowledge is up-to-date, that nothing jumps out as immediately problematic, and to identify any interesting areas for further investigation or future use. Let's see what's in there.



28/2 2026/10/26 09:12 tool read-file
0m Args:
{
  "File": "lib/Kconfig.kmsan",
  "FirstLine": 1,
  "LineCount": 40
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\tconfig HAVE_ARCH_KMSAN\n   3:\t\tbool\n   4:\t\n   5:\tconfig HAVE_KMSAN_COMPILER\n   6:\t\tdef_bool $(cc-option,-fsanitize=kernel-memory)\n   7:\t\n   8:\tconfig KMSAN\n   9:\t\tbool \"KMSAN: detector of uninitialized values use\"\n  10:\t\tdepends on HAVE_ARCH_KMSAN \u0026\u0026 HAVE_KMSAN_COMPILER\n  11:\t\tdepends on DEBUG_KERNEL \u0026\u0026 !KASAN \u0026\u0026 !KCSAN\n  12:\t\tdepends on !PREEMPT_RT\n  13:\t\tselect STACKDEPOT\n  14:\t\tselect STACKDEPOT_ALWAYS_INIT\n  15:\t\thelp\n  16:\t\t  KernelMemorySanitizer (KMSAN) is a dynamic detector of uses of\n  17:\t\t  uninitialized values in the kernel. It is based on compiler\n  18:\t\t  instrumentation provided by Clang and thus requires Clang to build.\n  19:\t\n  20:\t\t  An important note is that KMSAN is not intended for production use,\n  21:\t\t  because it drastically increases kernel memory footprint and slows\n  22:\t\t  the whole system down.\n  23:\t\n  24:\t\t  See \u003cfile:Documentation/dev-tools/kmsan.rst\u003e for more details.\n  25:\t\n  26:\tif KMSAN\n  27:\t\n  28:\tconfig KMSAN_CHECK_PARAM_RETVAL\n  29:\t\tbool \"Check for uninitialized values passed to and returned from functions\"\n  30:\t\tdefault y\n  31:\t\thelp\n  32:\t\t  If the compiler supports -fsanitize-memory-param-retval, KMSAN will\n  33:\t\t  eagerly check every function parameter passed by value and every\n  34:\t\t  function return value.\n  35:\t\n  36:\t\t  Disabling KMSAN_CHECK_PARAM_RETVAL will result in tracking shadow for\n  37:\t\t  function parameters and return values across function borders. This\n  38:\t\t  is a more relaxed mode, but it generates more instrumentation code and\n  39:\t\t  may potentially report errors in corner cases when non-instrumented\n  40:\t\t  functions call instrumented ones.\n"
}

29/2 2026/10/26 09:12 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37670
							output: 37
							thoughts: 109
						

Thoughts:
**KMSAN Feature Focus**

Okay, so I'm diving into this code, specifically looking at changes related to `KMSAN`. It seems like modifications are concentrated in `mm/kmsan/hooks.c` and `mm/kmsan/shadow.c`. But hold on, the architecture's huge vmalloc support, `CONFIG_HAVE_ARCH_HUGE_VMALLOC`, that's got to be relevant here. I better investigate how that plays into things. I see it referenced in `mm/vmalloc.c`, specifically this line: `if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) || page_shift == PAGE_SHIFT)`. Right, so the logic depends on `HAVE_ARCH_HUGE_VMALLOC`. I need to understand what this configuration option means in this context. Let me make sure I know what `HAVE_ARCH_HUGE_VMALLOC` covers.



30/2 2026/10/26 09:13 tool grepper
0m Args:
{
  "Expression": "HAVE_ARCH_HUGE_VMALLOC",
  "PathPrefixes": [
    "arch/x86/"
  ]
}

Results:
{
  "Output": "arch/x86/Kconfig=57=config X86\n--\narch/x86/Kconfig-193-\tselect HAVE_ARCH_HUGE_VMAP\t\tif X86_64 || X86_PAE\narch/x86/Kconfig:194:\tselect HAVE_ARCH_HUGE_VMALLOC\t\tif X86_64\narch/x86/Kconfig-195-\tselect HAVE_ARCH_JUMP_LABEL\n"
}

31/2 2026/10/26 09:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38148
							output: 28
							thoughts: 60
						

Thoughts:
**Investigating the Compilation of `percpu-vm.c` in the x86_64 Kernel**

Okay, so I'm digging into how the kernel handles huge vmalloc on x86_64, and this `HAVE_ARCH_HUGE_VMALLOC` definition caught my eye. It's automatically selected when `X86_64` is defined, which makes sense. My question is, how does this interact with `percpu-vm.c`? Where does that fit in the build process? I vaguely remember it being conditional, related to certain memory management features.

Let's not jump to conclusions, I'll check `mm/Makefile` and see what the build rules tell me. It should have some clues as to when `percpu-vm.c` is compiled. Specifically, I'm looking for any dependencies or conditional logic around its inclusion. This should clear things up.



32/2 2026/10/26 09:13 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "percpu-vm",
  "PathPrefixes": [
    "mm/Makefile"
  ]
}

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

33/2 2026/10/26 09:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38448
							output: 20
							thoughts: 20
						

Thoughts:
Okay, here's my summarized thought process, assuming I'm an expert in kernel internals and memory management:

**Hunting Down `percpu-vm.o`**

Alright, I need to figure out where the `percpu-vm.o` object file resides within the kernel source tree. My immediate instinct is to check within the `mm/` directory, since "vm" suggests it's related to virtual memory, and `percpu` screams memory management on a per-CPU basis. However, I can't be certain, so I need to expand my search. It could be lurking elsewhere, possibly in a related subsystem. I'll need to use a find or similar tool to perform a comprehensive search within the kernel source code. I need to track where it is.



34/2 2026/10/26 09:13 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "percpu-vm.o"
}

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

35/2 2026/10/26 09:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38667
							output: 20
							thoughts: 16
						

Thoughts:
Okay, here's my attempt at summarizing that thought process, tailored for an expert audience:

**Tracing the Usage of `percpu-vm.c`**

Alright, so I'm looking to understand the scope and impact of `percpu-vm.c`. The first step, naturally, is to pinpoint where it's actually *used* within the larger kernel framework. I need to quickly identify the call sites and related files. That means I need to start with a robust search for references to that specific file name. Let's get to it. I want to see how broadly this module is interacting with other parts of the system.



36/2 2026/10/26 09:13 tool grepper
0m Args:
{
  "Expression": "percpu-vm\\.c"
}

Results:
{
  "Output": "mm/percpu-vm.c-2-/*\nmm/percpu-vm.c:3: * mm/percpu-vm.c - vmalloc area based chunk allocation\nmm/percpu-vm.c-4- *\n--\nmm/percpu.c=1574=static int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai);\n--\nmm/percpu.c-1578-#else\nmm/percpu.c:1579:#include \"percpu-vm.c\"\nmm/percpu.c-1580-#endif\n"
}

37/2 2026/10/26 09:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38995
							output: 35
							thoughts: 35
						

Thoughts:
Here's my take on the thought process:

**Diving into Per-CPU VM Details**

Okay, so I'm thinking, "percpu-vm.c" is where I need to start. Specifically, I'm trying to figure out a piece of code that should be relevant. My gut tells me that line 1578 in `mm/percpu.c` is going to be helpful. I know how the per-CPU VM system functions, and I suspect that region will be a good entry point. Let me pull that up and see if my hunch pays off.



38/2 2026/10/26 09:13 tool read-file
0m Args:
{
  "File": "mm/percpu.c",
  "FirstLine": 1570,
  "LineCount": 20
}

Results:
{
  "Contents": "1570:\t\t\t\t\t      int page_start, int page_end);\n1571:\tstatic struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp);\n1572:\tstatic void pcpu_destroy_chunk(struct pcpu_chunk *chunk);\n1573:\tstatic struct page *pcpu_addr_to_page(void *addr);\n1574:\tstatic int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai);\n1575:\t\n1576:\t#ifdef CONFIG_NEED_PER_CPU_KM\n1577:\t#include \"percpu-km.c\"\n1578:\t#else\n1579:\t#include \"percpu-vm.c\"\n1580:\t#endif\n1581:\t\n1582:\t/**\n1583:\t * pcpu_chunk_addr_search - determine chunk containing specified address\n1584:\t * @addr: address for which the chunk needs to be determined.\n1585:\t *\n1586:\t * This is an internal function that handles all but static allocations.\n1587:\t * Static percpu address values should never be passed into the allocator.\n1588:\t *\n1589:\t * RETURNS:\n"
}

39/2 2026/10/26 09:13 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39531
							output: 21
							thoughts: 56
						

Thoughts:
Here's my analysis of the situation:

**Investigating the Role of `percpu-vm.c`**

Okay, so I'm looking at this `percpu-vm.c` file and immediately my mind jumps to the configuration.  I recall that this file is *only* included if `CONFIG_NEED_PER_CPU_KM` is *not* defined.  Hmm, interesting! And, if I'm remembering my kernel config defaults correctly, that's the *standard* setup for systems with SMP (Symmetric Multiprocessing) and MMU (Memory Management Unit) capabilities, like the ubiquitous x86_64 architecture.  So, this file is probably very relevant to the systems that make up the vast majority of our deployments.

My next step is clear: I need to explicitly check the definition of `CONFIG_NEED_PER_CPU_KM`.  Understanding its state is key to understanding the role that `percpu-vm.c` plays in this system. This will tell me whether this file is being used in the systems I'm concerned with or not. I'll dig into the configuration files to clarify this and understand its implication.



40/2 2026/10/26 09:14 tool grepper
0m Args:
{
  "Expression": "NEED_PER_CPU_KM"
}

Results:
{
  "Output": "lib/test_vmalloc.c=316=pcpu_alloc_test(void)\n--\nlib/test_vmalloc.c-318-\tint rv = 0;\nlib/test_vmalloc.c:319:#ifndef CONFIG_NEED_PER_CPU_KM\nlib/test_vmalloc.c-320-\tvoid __percpu **pcpu;\n--\nmm/Kconfig=1032=config ARCH_MODULE_NEEDS_WEAK_PER_CPU\n--\nmm/Kconfig-1037-#\nmm/Kconfig:1038:config NEED_PER_CPU_KM\nmm/Kconfig-1039-\tdepends on !SMP || !MMU\n--\nmm/Kconfig=1157=config DEFERRED_STRUCT_PAGE_INIT\n--\nmm/Kconfig-1159-\tdepends on SPARSEMEM\nmm/Kconfig:1160:\tdepends on !NEED_PER_CPU_KM\nmm/Kconfig-1161-\tdepends on 64BIT\n--\nmm/percpu-km.c-12- *\nmm/percpu-km.c:13: * - define CONFIG_NEED_PER_CPU_KM from the arch Kconfig.\nmm/percpu-km.c-14- *\n--\nmm/percpu.c=1574=static int __init pcpu_verify_alloc_info(const struct pcpu_alloc_info *ai);\nmm/percpu.c-1575-\nmm/percpu.c:1576:#ifdef CONFIG_NEED_PER_CPU_KM\nmm/percpu.c-1577-#include \"percpu-km.c\"\n"
}

41/2 2026/10/26 09:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 40281
							output: 27
							thoughts: 1248
						

Thoughts:
**Analyzing the CONFIG Dependencies for the Patch**

Okay, so I'm looking at this patch and trying to figure out the right `EnableConfigs`.  `mm/percpu-vm.c` and `mm/vmalloc.c` are straightforward; they're compiled on standard x86_64 systems with `CONFIG_MMU` and `CONFIG_SMP`, which are pretty much always on. `mm/vmalloc.c` specifically needs `CONFIG_MMU`. Then there's the `KMSAN` stuff, which is controlled by `CONFIG_KMSAN`.

Now, the question is, should `CONFIG_KMSAN` be *in* `EnableConfigs`?  Let's re-read the instructions... right, we need to identify the specific `CONFIG_` options needed to *reach* the modified code.  That means inspecting Kconfig files, #ifdef guards, checking "depends on" lines, and stripping the `CONFIG_` prefix.

The core modified code is in two parts: `mm/vmalloc.c` and `mm/percpu-vm.c`, which are compiled under `CONFIG_MMU`, and the KMSAN code in `mm/kmsan/hooks.c` and `mm/kmsan/shadow.c`, which, obviously, requires `CONFIG_KMSAN`. The KMSAN code runs whenever vmap/ioremap operations occur *if* `CONFIG_KMSAN` is enabled.  But, wait, KMSAN is mutually exclusive with KASAN (because of `DEBUG_KERNEL && !KASAN && !KCSAN`), and Syzkaller often runs with either KASAN or KMSAN.

So, can we just include `KMSAN` in `EnableConfigs`? Or do we need `KMSAN`?  If `KMSAN` is enabled, obviously, we *need* `KMSAN`, but what about `CONFIG_MMU`?  `CONFIG_MMU` is standard, though.  What if someone *doesn't* enable KMSAN? Does `mm/vmalloc.c` and `mm/percpu-vm.c` still compile and run?  Yes, because `mm/vmalloc.c` includes `<linux/kmsan.h>` which provides static inlines when `!CONFIG_KMSAN`, so they are still part of the build.

So, the changes to `mm/kmsan/hooks.c` and `mm/kmsan/shadow.c` are not compiled or reached if KMSAN isn't enabled. But is KMSAN required to get to the changes in `mm/vmalloc.c`? No! So, what configs *are* needed, then? Can we list `KMSAN` in `EnableConfigs`?

If Syzkaller builds a kernel *with* `KMSAN`, it can't enable `KASAN`. Syzkaller has separate KMSAN instances. So what exactly do we want to accomplish with `EnableConfigs`? `EnableConfigs` in syz-cluster are configs that the kernel config *must* have in order to enable the target code. If `EnableConfigs` *includes* `KMSAN`, a kernel without `KMSAN` wouldn't fuzz the KMSAN part, but with `KMSAN` it *does* compile the KMSAN hooks.

But, if `EnableConfigs` contains `KMSAN` and the syz-cluster sets `CONFIG_KMSAN=y`, what if the compiler is GCC and `CONFIG_KMSAN` depends on `HAVE_KMSAN_COMPILER` (Clang with -fsanitize=kernel-memory)?

Let's look at the patch itself... does it *require* `CONFIG_KMSAN`, or is the core patch in `mm/vmalloc.c`? The patch touches `arch/powerpc/kernel/isa-bridge.c`, `arch/powerpc/kernel/pci_64.c`, `include/linux/kmsan.h`, `mm/kmsan/hooks.c`, `mm/kmsan/shadow.c`, `mm/percpu-vm.c`, `mm/vmalloc.c`, and `mm/vmalloc.h`.

The *core* change is in `mm/vmalloc.c`. `__vmap_pages_range_noflush` now unmaps partially installed PTEs on error and skips `cond_resched()` if atomic.  `vmap_pages_pte_range` and `__vmap_pages_range_noflush` and `vunmap_pte_range` have new `WARN_ON_ONCE` checks.  `mm/percpu-vm.


42/2 2026/10/26 09:14 tool grepper
0m Args:
{
  "Expression": "vmalloc\\.o",
  "PathPrefixes": [
    "mm/"
  ]
}

Results:
{
  "Output": "mm/Makefile=37=mmu-$(CONFIG_MMU)\t:= highmem.o memory.o mincore.o \\\n--\nmm/Makefile-39-\t\t\t   msync.o page_vma_mapped.o pagewalk.o \\\nmm/Makefile:40:\t\t\t   pgtable-generic.o rmap.o vmalloc.o vma.o vma_exec.o\nmm/Makefile-41-\n"
}

43/2 2026/10/26 09:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42695
							output: 27
							thoughts: 74
						

Thoughts:
**Investigating vmalloc and CONFIG_MMU**

Okay, so I'm looking at this build system fragment, `mmu-$(CONFIG_MMU) := ... vmalloc.o ...`. The key takeaway here is that `vmalloc.o` is conditionally built based on `CONFIG_MMU`. On amd64 (x86_64), `CONFIG_MMU` is *always* defined as `y`. That means `vmalloc.o` is always included, which seems reasonable for that architecture.

But now I'm curious. Is there any specific testing framework related to `vmalloc` that's contingent on a particular configuration or test module? I'm thinking, are there tests specifically targeted at verifying the functionality of `vmalloc` given that it's always included on amd64? This feels like a good place to look for those. Let's take a look at `lib/test_vmalloc.c`. That seems like the logical place to start. I'm going to quickly check that file for any interesting logic or setup related to `vmalloc`. It's possible there are some subtleties I might be overlooking, or it could give me a better understanding of the overall testing coverage.



44/2 2026/10/26 09:14 tool grepper
0m Args:
{
  "Expression": "test_vmalloc",
  "PathPrefixes": [
    "lib/"
  ]
}

Results:
{
  "Output": "lib/Kconfig.debug=2596=config TEST_VMALLOC\n--\nlib/Kconfig.debug-2600-\thelp\nlib/Kconfig.debug:2601:\t  This builds the \"test_vmalloc\" module that should be used for\nlib/Kconfig.debug-2602-\t  stress and performance analysis. So, any new change for vmalloc\n--\nlib/Makefile=81=obj-$(CONFIG_TEST_LKM) += test_module.o\nlib/Makefile:82:obj-$(CONFIG_TEST_VMALLOC) += test_vmalloc.o\nlib/Makefile-83-obj-$(CONFIG_TEST_WORKQUEUE) += test_workqueue.o\n"
}

45/2 2026/10/26 09:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43238
							output: 35
							thoughts: 67
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the vmalloc Debugging Landscape**

Okay, so I need to understand this `vmalloc` code better. Let's start with a foundational check. My instincts tell me to look at the `lib/Kconfig.debug` file. Ah, yes! Here we are: "config TEST_VMALLOC... This builds the 'test_vmalloc' module that should be used for stress and performance analysis. So, any new change for vmalloc..." That confirms my initial hunch – anything related to debugging or stress testing `vmalloc` should be declared in this module. Makes sense. Now, let me pinpoint the relevant section in `lib/Kconfig.debug`. I recall this being around line 2596. Time to dig in and see what's being configured, and how it relates to the problem at hand. It will likely highlight related configuration options, and possibly dependencies I need to be aware of.



46/2 2026/10/26 09:14 tool read-file
0m Args:
{
  "File": "lib/Kconfig.debug",
  "FirstLine": 2595,
  "LineCount": 25
}

Results:
{
  "Contents": "2595:\t\n2596:\tconfig TEST_VMALLOC\n2597:\t\ttristate \"Test module for stress/performance analysis of vmalloc allocator\"\n2598:\t\tdefault n\n2599:\t\tdepends on MMU\n2600:\t\thelp\n2601:\t\t  This builds the \"test_vmalloc\" module that should be used for\n2602:\t\t  stress and performance analysis. So, any new change for vmalloc\n2603:\t\t  subsystem can be evaluated from performance and stability point\n2604:\t\t  of view.\n2605:\t\n2606:\t\t  If unsure, say N.\n2607:\t\n2608:\tconfig TEST_WORKQUEUE\n2609:\t\ttristate \"Test module for stress/performance analysis of workqueue\"\n2610:\t\tdepends on m\n2611:\t\tdefault n\n2612:\t\thelp\n2613:\t\t  This builds the \"test_workqueue\" module for benchmarking\n2614:\t\t  workqueue throughput under contention. Useful for evaluating\n2615:\t\t  affinity scope changes (e.g., cache_shard vs cache).\n2616:\t\n2617:\t\t  The test drives sysfs to switch affinity scopes, so it must be\n2618:\t\t  loaded after userspace has mounted sysfs; building it in (=y)\n2619:\t\t  would run module_init before /sys is available.\n"
}

47/2 2026/10/26 09:14 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43927
							output: 36
							thoughts: 2654
						

Thoughts:
**Analyzing Kernel Configuration Requirements for Fuzzing**

Okay, here's the breakdown of my thought process. First, my immediate reaction is to assess whether `TEST_VMALLOC` is actually fuzzed by syzkaller. I know syzkaller typically focuses on syscalls and interfaces within the running kernel, not test modules directly (unless, of course, the test module is loaded and usable). So, I'm trying to get a picture of what code is even touched here.

Next, I need to understand the compilation environment. What specific kernel configurations are *required* to compile the modified code? The prompt emphasizes careful inspection of Kconfig files and `#ifdef` guards, without making assumptions. I need to make sure I am following instructions and not making assumptions here. The documentation stresses the importance of checking "depends on" lines in Kconfig files, which is critical to identify non-standard parent subsystem configs.

Alright, the changes target `mm/vmalloc.c`, `mm/percpu-vm.c`, `mm/kmsan/hooks.c`, and `mm/kmsan/shadow.c`.  I note that `mm/vmalloc.c` compiles if `CONFIG_MMU` is enabled, and `mm/percpu-vm.c` needs `CONFIG_MMU` and `!CONFIG_NEED_PER_CPU_KM`. The KMSAN files, naturally, are tied to `CONFIG_KMSAN`.

Now, the critical question: What happens if `CONFIG_KMSAN` is *not* enabled? I recognize that `include/linux/kmsan.h` provides empty inline stubs when `CONFIG_KMSAN` is off. So, the kernel compiles fine without it, but is it the *right* compilation for reaching the "interesting" code? That might depend on whether or not the intent of the fuzz is to exercise the new KMSAN features.

If I set `EnableConfigs: ["KMSAN"]`, what happens? KMSAN conflicts with KASAN (`depends on !KASAN`). So I can't accidentally break the existing KASAN fuzzing. I have to make the decision on which tool I want to use.

Let's re-examine if KMSAN is really needed. No, `mm/vmalloc.c` and `percpu-vm.c` are core code that *is* enabled by default on amd64 (under `MMU`).

My next move is to check the prompt again: I need to identify *specific* configs, not make assumptions, and exclude the 'CONFIG_' prefix.

Okay, is `MMU` a standard config on amd64? Yes, it is. Both `CONFIG_MMU` and `CONFIG_64BIT` are standard. I can return either `MMU` or `KMSAN`.

Let's check if the previous reviews returned `KMSAN` when KMSAN code is modified.

I need to make sure that the KMSAN code in `mm/kmsan/hooks.c` and `mm/kmsan/shadow.c` is only compiled when `CONFIG_KMSAN` is enabled. Without `CONFIG_KMSAN`, these files are *not* even compiled. I need to be exact here.

What if I list `["MMU", "KMSAN"]` as `EnableConfigs`? Syzkaller supports KMSAN, but I have to make sure there are no conflicts. If syzkaller enables `CONFIG_KMSAN=y`, then KMSAN *is* enabled, and I'm good. Syzkaller has its own KMSAN instances.

I need to make sure that KMSAN does not conflict with KASAN in the build config.

Now I need to remember: is `KMSAN` *required* to compile the non-KMSAN related files (`mm/vmalloc.c`, etc.)? No!

The instructions say to identify what is required:
*   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").

Is `MMU` required? Yes, because `mm/vmalloc.c` requires `CONFIG_MMU`. KMSAN requires `CONFIG_KMSAN`.

So, what should `EnableConfigs` contain? `MMU`? `KMSAN`? Both?

I need to be conservative. The code has changes to core code (`mm/vmalloc.c`, etc) and `mm/kmsan/` that requires a specific config.

Let's think. If I'm using


48/2 2026/10/26 09:15 tool grepper
0m Args:
{
  "Expression": "vmap_pages_range_noflush",
  "PathPrefixes": [
    "mm/vmalloc.c"
  ]
}

Results:
{
  "Output": "mm/vmalloc.c=670=static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-697-/*\nmm/vmalloc.c:698: * vmap_pages_range_noflush is similar to vmap_pages_range, but does not\nmm/vmalloc.c-699- * flush caches.\n--\nmm/vmalloc.c-709- */\nmm/vmalloc.c:710:int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.c-711-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n--\nmm/vmalloc.c-750-\nmm/vmalloc.c:751:int vmap_pages_range_noflush(unsigned long addr, unsigned long end,\nmm/vmalloc.c-752-\t\tpgprot_t prot, struct page **pages, unsigned int page_shift,\n--\nmm/vmalloc.c-754-{\nmm/vmalloc.c:755:\tint ret = kmsan_vmap_pages_range_noflush(addr, end, prot, pages,\nmm/vmalloc.c-756-\t\t\t\t\t\tpage_shift, gfp_mask);\n--\nmm/vmalloc.c-760-\nmm/vmalloc.c:761:\tret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,\nmm/vmalloc.c-762-\t\t\t\t\t gfp_mask);\n--\nmm/vmalloc.c=774=static int __vmap_pages_range(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-779-\nmm/vmalloc.c:780:\terr = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);\nmm/vmalloc.c-781-\tif (err)\n"
}

49/2 2026/10/26 09:15 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 48154
							output: 30
							thoughts: 5182
						

Thoughts:
**Focusing on vmalloc: A Deep Dive into Patch Analysis**

Alright, let's break this down. We're looking at a patch from syz-cluster, likely targeting `mm/vmalloc.c`. My initial thought was to check `git log` and maybe look for callers of `vmap_pages_range_noflush`.  We're dealing with incoming code, so let's carefully dissect the patch's intent, specifically within the context of vmalloc and unmapping operations.

The patch's core modification appears to revolve around how `__vmap_pages_range_noflush` handles errors and atomic contexts. The addition of `gfp_mask` to several functions, including `__vmap_pages_range_noflush`,  `__vunmap_range_noflush`, and the associated KMSAN functions, is the key change. This allows for conditional `cond_resched` calls, enabling atomic contexts to work correctly. The error handling ensures clean-up via the `__vunmap_range_noflush` function on failure.

Now, let's zero in on the `FocusSymbols`.  We need to avoid generic functions. Functions that implement the actual changes are the ones that must be targeted. We're looking at functions that directly implement the logic being added or altered, or direct API entrypoints for the subsystem feature under review.  Let's consider the following functions in `.c` files, and if they're relevant:
1.  `__vmap_pages_range_noflush`:  Definitely a prime candidate. This is where the core logic change is, specifically with the `gfp_mask` parameter and rollback on error. It's not a generic function; it's specific to vmalloc's page mapping process.
2.  `vmap_pages_range_noflush`: It calls KMSAN and `__vmap_pages_range_noflush`, so it also directly involved in the fix.
3.  `__vunmap_range_noflush`:  This is *the* unmapping helper that was modified to handle both atomic and sleepable contexts. This is critical as it's the core unmapping function that now incorporates the new `can_resched` parameter.
4.  `vmap_page_range`: Since it calls the now-modified `__vmap_pages_range_noflush`, we should include this function.
5.  `vunmap_range_noflush`: calls `kmsan_vunmap_range_noflush` and `__vunmap_range_noflush`.
6.  `kmsan_vmap_pages_range_noflush`: KMSAN implementation.
7.  `kmsan_vunmap_range_noflush`: KMSAN implementation.
8.  `kmsan_ioremap_page_range`: KMSAN implementation.

These are all the relevant functions, and non-ubiquitous.

Now, regarding the `EnableConfigs`, we must consider if `MMU` and/or `KMSAN` are needed. `MMU` is required to build vmalloc.  Since the core code being modified is in `mm/vmalloc.c`, we should definitely include `"MMU"` in `EnableConfigs`. The code won't compile without it.

What about `KMSAN`? While the patch does touch KMSAN to ensure compatibility with the updated vmalloc functions, it's *not* essential to test the *core* change. The patch's primary concern is within `mm/vmalloc.c`. Providing `KMSAN` to syzkaller might cause build issues, or, it might be superfluous, if syzkaller is not running a KMSAN build.  Therefore, to avoid unnecessary complications, and to target the core vmalloc change, let's leave KMSAN out.



50/2 2026/10/26 09:15 tool grepper
0m Args:
{
  "Expression": "config MMU",
  "PathPrefixes": [
    "arch/x86/"
  ]
}

Results:
{
  "Output": "arch/x86/Kconfig=353=config STACKTRACE_SUPPORT\n--\narch/x86/Kconfig-355-\narch/x86/Kconfig:356:config MMU\narch/x86/Kconfig-357-\tdef_bool y\n"
}

51/2 2026/10/26 09:15 llm patch-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54283
							output: 308
							thoughts: 4893
						

Thoughts:
**Analysis of Patch Fuzzing Strategy**

Okay, here's the breakdown. This patch definitely warrants fuzzing; the changes touch core memory management (`mm/vmalloc.c`, `mm/percpu-vm.c`) and include new runtime assertions, as well as modifications to the error handling paths.  This means the patch meets all the criteria for `WorthFuzzing = true`. I need to ensure the fuzzer is focused on these changes.

Now, concerning which symbols to target: I need to be careful to avoid ubiquitous functions that would dilute fuzzing efforts. However, after careful consideration, I've identified several key functions impacted by the patch that *should* be listed:

*   `__vmap_pages_range_noflush`
*   `vmap_pages_range_noflush`
*   `vmap_page_range`
*   `__vunmap_range_noflush`
*   `vunmap_range_noflush`
*   `kmsan_vmap_pages_range_noflush`
*   `kmsan_vunmap_range_noflush`
*   `kmsan_ioremap_page_range`

I included the KMSAN-specific functions because even if KMSAN isn't enabled, they're likely present as inline functions and listing them won't hurt. And if KMSAN *is* enabled, they are distinct symbols related to patch modifications, so they become even more valuable in the fuzzing process.

Now, the `EnableConfigs` list.  I've concluded that `["MMU", "SMP"]` is the right choice. `MMU` is required for `mm/vmalloc.c`, and by enabling `SMP`, I ensure that the `percpu-vm.c` code is utilized, rather than the `percpu-km.c` code, thus more accurately reflecting the changes in this patch. I'm *not* including `KMSAN` in `EnableConfigs`.  Requiring `KMSAN` would complicate the build process on systems that don't already have it (like KASAN instances), and it's not strictly necessary to fuzz the core memory management changes.  Therefore, it's safer to omit it. The patch can be effectively fuzzed with just the MMU and SMP configurations enabled.



52/2 2026/10/26 09:17 tool set-results
0m Args:
{
  "EnableConfigs": [
    "MMU",
    "SMP"
  ],
  "FocusSymbols": [
    "__vmap_pages_range_noflush",
    "vmap_pages_range_noflush",
    "vmap_page_range",
    "__vunmap_range_noflush",
    "vunmap_range_noflush",
    "kmsan_vmap_pages_range_noflush",
    "kmsan_vunmap_range_noflush",
    "kmsan_ioremap_page_range"
  ],
  "Reasoning": "The patch modifies core memory management (mm/vmalloc.c, mm/percpu-vm.c, and mm/kmsan) on reachable code paths for amd64. It adds new runtime assertions (WARN_ON_ONCE checking address alignment and bounds in vunmap_pte_range, vmap_pages_pte_range, and __vmap_pages_range_noflush), implements automatic teardown of partially mapped page tables on mapping failure in __vmap_pages_range_noflush and vmap_page_range, adjusts percpu error unwinding to avoid double unmapping, and modifies __vunmap_range_noflush to safely support atomic callers by conditionally disabling cond_resched(). These changes alter core VM invariants and error recovery paths, making this series high-value for fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "MMU",
    "SMP"
  ],
  "FocusSymbols": [
    "__vmap_pages_range_noflush",
    "vmap_pages_range_noflush",
    "vmap_page_range",
    "__vunmap_range_noflush",
    "vunmap_range_noflush",
    "kmsan_vmap_pages_range_noflush",
    "kmsan_vunmap_range_noflush",
    "kmsan_ioremap_page_range"
  ],
  "Reasoning": "The patch modifies core memory management (mm/vmalloc.c, mm/percpu-vm.c, and mm/kmsan) on reachable code paths for amd64. It adds new runtime assertions (WARN_ON_ONCE checking address alignment and bounds in vunmap_pte_range, vmap_pages_pte_range, and __vmap_pages_range_noflush), implements automatic teardown of partially mapped page tables on mapping failure in __vmap_pages_range_noflush and vmap_page_range, adjusts percpu error unwinding to avoid double unmapping, and modifies __vunmap_range_noflush to safely support atomic callers by conditionally disabling cond_resched(). These changes alter core VM invariants and error recovery paths, making this series high-value for fuzzing.",
  "WorthFuzzing": true
}

53/1 2026/10/26 09:17 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

54/2 2026/10/26 09:17 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch refactors kernel virtual memory mapping and unmapping functions (vmap, ioremap, vunmap, and percpu-vm) so that partially mapped page table ranges automatically unwind themselves on failure. It also introduces a `can_resched` flag to avoid calling `cond_resched()` in atomic contexts and updates the KMSAN shadow/origin mapping hooks to reflect these signature changes and unwind shadow mappings on error.\n\nKMSAN is specifically designed to detect uninitialized memory reads and kernel-to-user memory info-leaks. The changes in this patch:\n1. Do not copy any data to user space (no copy_to_user, netlink skb, ioctls, etc.).\n2. Do not introduce any variables or structures that could be read while uninitialized; all new and modified local variables (such as err, start, can_resched) are explicitly initialized.\n3. Primarily affect page table manipulation, error unwinding, and context scheduling (sleeping vs atomic).\n\nAny potential bugs resulting from these changes (such as sleeping in atomic context, improper TLB flushing, or invalid page table references) are covered by standard kernel detectors such as LOCKDEP, CONFIG_DEBUG_VM, and KASAN. Therefore, dedicated KMSAN fuzzing is not justified.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


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

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 4b57052221731ef57556636f96cf8a2a9b97ac25
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Oct 26 09:10:02 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/powerpc/kernel/isa-bridge.c b/arch/powerpc/kernel/isa-bridge.c
index 5c064485197a9..93029d85e6eaf 100644
--- a/arch/powerpc/kernel/isa-bridge.c
+++ b/arch/powerpc/kernel/isa-bridge.c
@@ -46,9 +46,8 @@ static void remap_isa_base(phys_addr_t pa, unsigned long size)
 	WARN_ON_ONCE(size & ~PAGE_MASK);
 
 	if (slab_is_available()) {
-		if (vmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
-				    pgprot_noncached(PAGE_KERNEL)))
-			vunmap_range(ISA_IO_BASE, ISA_IO_BASE + size);
+		vmap_page_range(ISA_IO_BASE, ISA_IO_BASE + size, pa,
+				pgprot_noncached(PAGE_KERNEL));
 	} else {
 		early_ioremap_range(ISA_IO_BASE, pa, size,
 				pgprot_noncached(PAGE_KERNEL));
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index e27342ef128b8..f74269636d5ec 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -139,10 +139,8 @@ void __iomem *ioremap_phb(phys_addr_t paddr, unsigned long size)
 
 	addr = (unsigned long)area->addr;
 	if (ioremap_page_range(addr, addr + size, paddr,
-			pgprot_noncached(PAGE_KERNEL))) {
-		vunmap_range(addr, addr + size);
+			pgprot_noncached(PAGE_KERNEL)))
 		return NULL;
-	}
 
 	return (void __iomem *)addr;
 }
diff --git a/include/linux/kmsan.h b/include/linux/kmsan.h
index 7da9fd506b390..0016c032d1e79 100644
--- a/include/linux/kmsan.h
+++ b/include/linux/kmsan.h
@@ -150,11 +150,14 @@ int __must_check kmsan_vmap_pages_range_noflush(unsigned long start,
  * kmsan_vunmap_kernel_range_noflush() - Notify KMSAN about a vunmap.
  * @start: start of vunmapped range.
  * @end:   end of vunmapped range.
+ * @can_resched: whether the unmap walk may call cond_resched(); pass
+ * false from atomic context.
  *
  * KMSAN unmaps the contiguous metadata ranges created by
  * kmsan_map_kernel_range_noflush().
  */
-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end);
+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,
+				bool can_resched);
 
 /**
  * kmsan_ioremap_page_range() - Notify KMSAN about a ioremap_page_range() call.
@@ -355,7 +358,8 @@ static inline int __must_check kmsan_vmap_pages_range_noflush(
 }
 
 static inline void kmsan_vunmap_range_noflush(unsigned long start,
-					      unsigned long end)
+					      unsigned long end,
+					      bool can_resched)
 {
 }
 
diff --git a/mm/kmsan/hooks.c b/mm/kmsan/hooks.c
index a706666db0973..0cd1edaa1120c 100644
--- a/mm/kmsan/hooks.c
+++ b/mm/kmsan/hooks.c
@@ -136,10 +136,16 @@ static unsigned long vmalloc_origin(unsigned long addr)
 						 KMSAN_META_ORIGIN);
 }
 
-void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end)
+void kmsan_vunmap_range_noflush(unsigned long start, unsigned long end,
+				bool can_resched)
 {
-	__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end));
-	__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end));
+	if (!kmsan_enabled)
+		return;
+
+	__vunmap_range_noflush(vmalloc_shadow(start), vmalloc_shadow(end),
+			       can_resched);
+	__vunmap_range_noflush(vmalloc_origin(start), vmalloc_origin(end),
+			       can_resched);
 	flush_cache_vmap(vmalloc_shadow(start), vmalloc_shadow(end));
 	flush_cache_vmap(vmalloc_origin(start), vmalloc_origin(end));
 }
@@ -168,8 +174,9 @@ static void kmsan_iounmap_pages(unsigned long start, unsigned long end)
 		if (origin)
 			__free_pages(origin, KMSAN_IOREMAP_META_ORDER);
 	}
-	__vunmap_range_noflush(shadow_start, shadow_end);
-	__vunmap_range_noflush(origin_start, origin_end);
+	/* ioremap metadata mappings are always GFP_KERNEL, so resched is safe. */
+	__vunmap_range_noflush(shadow_start, shadow_end, true);
+	__vunmap_range_noflush(origin_start, origin_end, true);
 	flush_tlb_kernel_range(shadow_start, shadow_end);
 	flush_tlb_kernel_range(origin_start, origin_end);
 }
@@ -204,7 +211,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
 		mapped = __vmap_pages_range_noflush(
 			vmalloc_shadow(start + off),
 			vmalloc_shadow(start + off + PAGE_SIZE), prot, &shadow,
-			PAGE_SHIFT);
+			PAGE_SHIFT, gfp_mask);
 		if (mapped) {
 			err = mapped;
 			goto ret;
@@ -214,7 +221,7 @@ int kmsan_ioremap_page_range(unsigned long start, unsigned long end,
 		mapped = __vmap_pages_range_noflush(
 			vmalloc_origin(start + off),
 			vmalloc_origin(start + off + PAGE_SIZE), prot, &origin,
-			PAGE_SHIFT);
+			PAGE_SHIFT, gfp_mask);
 		if (mapped) {
 			err = mapped;
 			goto ret;
diff --git a/mm/kmsan/shadow.c b/mm/kmsan/shadow.c
index 0c88d89bf0d68..452760a333e4f 100644
--- a/mm/kmsan/shadow.c
+++ b/mm/kmsan/shadow.c
@@ -247,7 +247,7 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
 	origin_end = vmalloc_meta((void *)end, KMSAN_META_ORIGIN);
 	kmsan_enter_runtime();
 	mapped = __vmap_pages_range_noflush(shadow_start, shadow_end, prot,
-					    s_pages, page_shift);
+					    s_pages, page_shift, gfp_mask);
 	kmsan_leave_runtime();
 	if (mapped) {
 		err = mapped;
@@ -255,9 +255,14 @@ int kmsan_vmap_pages_range_noflush(unsigned long start, unsigned long end,
 	}
 	kmsan_enter_runtime();
 	mapped = __vmap_pages_range_noflush(origin_start, origin_end, prot,
-					    o_pages, page_shift);
+					    o_pages, page_shift, gfp_mask);
 	kmsan_leave_runtime();
 	if (mapped) {
+		/* Undo the shadow mapping set up above. */
+		kmsan_enter_runtime();
+		__vunmap_range_noflush(shadow_start, shadow_end,
+				       gfpflags_allow_blocking(gfp_mask));
+		kmsan_leave_runtime();
 		err = mapped;
 		goto ret;
 	}
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index 509d8901835cd..59d3a336a6bd1 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -252,10 +252,11 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages,
 	return 0;
 err:
 	for_each_possible_cpu(tcpu) {
-		__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
-				   page_end - page_start);
+		/* The failing CPU's partial mapping undoes itself */
 		if (tcpu == cpu)
 			break;
+		__pcpu_unmap_pages(pcpu_chunk_addr(chunk, tcpu, page_start),
+				   page_end - page_start);
 	}
 	pcpu_post_unmap_tlb_flush(chunk, page_start, page_end);
 	return err;
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 44e0eb32708f1..2b68f7263d531 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -359,11 +359,17 @@ int vmap_page_range(unsigned long addr, unsigned long end,
 
 	err = vmap_range_noflush(addr, end, phys_addr, pgprot_nx(prot),
 				 ioremap_max_page_shift);
-	flush_cache_vmap(addr, end);
 	if (!err)
 		err = kmsan_ioremap_page_range(addr, end, phys_addr, prot,
 					       ioremap_max_page_shift);
-	return err;
+	if (err) {
+		/* Callers are ioremap and arch setup code, always sleepable. */
+		__vunmap_range_noflush(addr, end, true);
+		return err;
+	}
+
+	flush_cache_vmap(addr, end);
+	return 0;
 }
 
 int ioremap_page_range(unsigned long addr, unsigned long end,
@@ -393,6 +399,11 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 	pte_t ptent;
 	unsigned long size = PAGE_SIZE;
 
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
+		end = addr + PAGE_ALIGN_DOWN(end - addr);
+	if (end == addr)
+		return;
+
 	pte = pte_offset_kernel(pmd, addr);
 	lazy_mmu_mode_enable();
 
@@ -418,7 +429,7 @@ static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,
 }
 
 static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	pmd_t *pmd;
 	unsigned long next;
@@ -440,12 +451,14 @@ static void vunmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
 			continue;
 		vunmap_pte_range(pmd, addr, next, mask);
 
-		cond_resched();
+		/* The undo path may run in atomic context. */
+		if (can_resched)
+			cond_resched();
 	} while (pmd++, addr = next, addr != end);
 }
 
 static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	pud_t *pud;
 	unsigned long next;
@@ -465,12 +478,12 @@ static void vunmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
 		}
 		if (pud_none_or_clear_bad(pud))
 			continue;
-		vunmap_pmd_range(pud, addr, next, mask);
+		vunmap_pmd_range(pud, addr, next, mask, can_resched);
 	} while (pud++, addr = next, addr != end);
 }
 
 static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
-			     pgtbl_mod_mask *mask)
+			     pgtbl_mod_mask *mask, bool can_resched)
 {
 	p4d_t *p4d;
 	unsigned long next;
@@ -485,7 +498,7 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
 
 		if (p4d_none_or_clear_bad(p4d))
 			continue;
-		vunmap_pud_range(p4d, addr, next, mask);
+		vunmap_pud_range(p4d, addr, next, mask, can_resched);
 	} while (p4d++, addr = next, addr != end);
 }
 
@@ -501,7 +514,8 @@ static void vunmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
  *
  * This is an internal function only. Do not use outside mm/.
  */
-void __vunmap_range_noflush(unsigned long start, unsigned long end)
+void __vunmap_range_noflush(unsigned long start, unsigned long end,
+			    bool can_resched)
 {
 	unsigned long next;
 	pgd_t *pgd;
@@ -516,7 +530,7 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
 			mask |= PGTBL_PGD_MODIFIED;
 		if (pgd_none_or_clear_bad(pgd))
 			continue;
-		vunmap_p4d_range(pgd, addr, next, &mask);
+		vunmap_p4d_range(pgd, addr, next, &mask, can_resched);
 	} while (pgd++, addr = next, addr != end);
 
 	if (mask & ARCH_PAGE_TABLE_SYNC_MASK)
@@ -525,8 +539,9 @@ void __vunmap_range_noflush(unsigned long start, unsigned long end)
 
 void vunmap_range_noflush(unsigned long start, unsigned long end)
 {
-	kmsan_vunmap_range_noflush(start, end);
-	__vunmap_range_noflush(start, end);
+	/* The unmap paths are sleepable, keep the resched point. */
+	kmsan_vunmap_range_noflush(start, end, true);
+	__vunmap_range_noflush(start, end, true);
 }
 
 /**
@@ -552,6 +567,9 @@ static int vmap_pages_pte_range(pmd_t *pmd, unsigned long addr,
 	int err = 0;
 	pte_t *pte;
 
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(end - addr)))
+		return -EINVAL;
+
 	/*
 	 * nr is a running index into the array which helps higher level
 	 * callers keep track of where we're up to.
@@ -595,16 +613,18 @@ static int vmap_pages_pmd_range(pud_t *pud, unsigned long addr,
 {
 	pmd_t *pmd;
 	unsigned long next;
+	int err = 0;
 
 	pmd = pmd_alloc_track(&init_mm, pud, addr, mask);
 	if (!pmd)
 		return -ENOMEM;
 	do {
 		next = pmd_addr_end(addr, end);
-		if (vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pte_range(pmd, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (pmd++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
@@ -613,16 +633,18 @@ static int vmap_pages_pud_range(p4d_t *p4d, unsigned long addr,
 {
 	pud_t *pud;
 	unsigned long next;
+	int err = 0;
 
 	pud = pud_alloc_track(&init_mm, p4d, addr, mask);
 	if (!pud)
 		return -ENOMEM;
 	do {
 		next = pud_addr_end(addr, end);
-		if (vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pmd_range(pud, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (pud++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
@@ -631,16 +653,18 @@ static int vmap_pages_p4d_range(pgd_t *pgd, unsigned long addr,
 {
 	p4d_t *p4d;
 	unsigned long next;
+	int err = 0;
 
 	p4d = p4d_alloc_track(&init_mm, pgd, addr, mask);
 	if (!p4d)
 		return -ENOMEM;
 	do {
 		next = p4d_addr_end(addr, end);
-		if (vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask))
-			return -ENOMEM;
+		err = vmap_pages_pud_range(p4d, addr, next, prot, pages, nr, mask);
+		if (err)
+			break;
 	} while (p4d++, addr = next, addr != end);
-	return 0;
+	return err;
 }
 
 static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
@@ -677,33 +701,51 @@ static int vmap_small_pages_range_noflush(unsigned long addr, unsigned long end,
  * The caller is responsible for calling flush_cache_vmap() after this
  * function returns successfully and before the addresses are accessed.
  *
+ * On failure the mapping is torn down before returning, so a retry
+ * sees a clean range. When @gfp_mask cannot block, the teardown
+ * skips cond_resched(), it may run in atomic context.
+ *
  * This is an internal function only. Do not use outside mm/.
  */
 int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,
-		pgprot_t prot, struct page **pages, unsigned int page_shift)
+		pgprot_t prot, struct page **pages, unsigned int page_shift,
+		gfp_t gfp_mask)
 {
 	unsigned int i, nr = (end - addr) >> PAGE_SHIFT;
+	unsigned long start = addr;
+	int err = 0;
 
-	if (WARN_ON_ONCE(page_shift < PAGE_SHIFT))
+	if (WARN_ON_ONCE(addr >= end))
 		return -EINVAL;
 
-	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
-			page_shift == PAGE_SHIFT)
-		return vmap_small_pages_range_noflush(addr, end, prot, pages);
+	if (WARN_ON_ONCE(!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(end)))
+		return -EINVAL;
 
-	for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
-		int err;
+	if (WARN_ON_ONCE(page_shift < PAGE_SHIFT))
+		return -EINVAL;
 
-		err = vmap_range_noflush(addr, addr + (1UL << page_shift),
+	if (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||
+			page_shift == PAGE_SHIFT) {
+		err = vmap_small_pages_range_noflush(addr, end, prot, pages);
+	} else {
+		for (i = 0; i < nr; i += 1U << (page_shift - PAGE_SHIFT)) {
+			err = vmap_range_noflush(addr,
+					addr + (1UL << page_shift),
 					page_to_phys(pages[i]), prot,
 					page_shift);
-		if (err)
-			return err;
+			if (err)
+				break;
 
-		addr += 1UL << page_shift;
+			addr += 1UL << page_shift;
+		}
 	}
 
-	return 0;
+	/* Undo the PTEs installed before the failure. */
+	if (err)
+		__vunmap_range_noflush(start, end,
+				       gfpflags_allow_blocking(gfp_mask));
+
+	return err;
 }
 
 int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
@@ -715,7 +757,18 @@ int vmap_pages_range_noflush(unsigned long addr, unsigned long end,
 
 	if (ret)
 		return ret;
-	return __vmap_pages_range_noflush(addr, end, prot, pages, page_shift);
+
+	ret = __vmap_pages_range_noflush(addr, end, prot, pages, page_shift,
+					 gfp_mask);
+	/*
+	 * The page tables undo themselves on failure. Tear down the
+	 * metadata that was fully set up before the mapping failed.
+	 */
+	if (ret)
+		kmsan_vunmap_range_noflush(addr, end,
+					   gfpflags_allow_blocking(gfp_mask));
+
+	return ret;
 }
 
 static int __vmap_pages_range(unsigned long addr, unsigned long end,
@@ -725,8 +778,11 @@ static int __vmap_pages_range(unsigned long addr, unsigned long end,
 	int err;
 
 	err = vmap_pages_range_noflush(addr, end, prot, pages, page_shift, gfp_mask);
+	if (err)
+		return err;
+
 	flush_cache_vmap(addr, end);
-	return err;
+	return 0;
 }
 
 /**
diff --git a/mm/vmalloc.h b/mm/vmalloc.h
index d8372a4f65c4c..9ea28845f7133 100644
--- a/mm/vmalloc.h
+++ b/mm/vmalloc.h
@@ -40,10 +40,12 @@ void clear_vm_uninitialized_flag(struct vm_struct *vm);
 
 int __must_check __vmap_pages_range_noflush(unsigned long addr,
 		unsigned long end, pgprot_t prot,
-		struct page **pages, unsigned int page_shift);
+		struct page **pages, unsigned int page_shift,
+		gfp_t gfp_mask);
 
 void vunmap_range_noflush(unsigned long start, unsigned long end);
 
-void __vunmap_range_noflush(unsigned long start, unsigned long end);
+void __vunmap_range_noflush(unsigned long start, unsigned long end,
+			    bool can_resched);
 
 #endif /* __MM_VMALLOC_H */

55/3 2026/10/26 09:17 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 8558
							output: 295
							thoughts: 3805
						

Thoughts:
**Analysis of the Patch Series and KMSAN Applicability**

Okay, let's break this down. My initial assessment suggests this patch series is primarily focused on improving the error handling and cleanup mechanisms within the `vmalloc`, `ioremap`, and related page table manipulation functions. Specifically, it seems to be ensuring that partially mapped ranges are correctly unwound if an error occurs during mapping and adding a `can_resched` flag to prevent calling `cond_resched()` in atomic contexts. It also updates the KMSAN shadow/origin mapping functions to coordinate with changes. I notice that this involves refactoring several functions like `__vmap_pages_range_noflush` and their callers.

The crucial question is whether these changes introduce or expose risks related to uninitialized memory, which is the domain of KMSAN. My understanding of KMSAN is that it's designed to detect reads of uninitialized memory (on the stack, heap, or in page allocations) and potential kernel-to-user memory leaks. We must evaluate this patch series according to the following criteria: whether it introduces:

1.  Kernel structures sent to userspace with uninitialized fields.
2.  Conditional logic based on potentially uninitialized variables.
3.  Allocation of complex data structures with uninitialized fields read later.
4.  Modifications to bounds checks or logic that allow access to uninitialized bytes.

Based on my analysis, this patch series doesn't seem to trigger any of the criteria that would warrant a dedicated KMSAN fuzzing session. The patch is primarily about improving error handling, particularly by making the `vmap`/`ioremap`/`vunmap` paths self-unwinding. No new data structures are introduced or modified that are sent to userspace. All variables and parameters involved, such as `can_resched`, `err`, `start`, are properly initialized. The changes affect page table manipulations, specifically with KMSAN's shadow/origin mapping, which are internal kernel mechanisms. The patch ensures that shadow mappings are cleaned up when the origin mapping fails.

Potential regressions from this patch (such as locking or concurrency issues) are likely to be found using standard bug detectors like KASAN or LOCKDEP, or may be addressed with DEBUG_VM checks. This patch's primary impact seems to be to improve error handling in memory mapping/unmapping, but it doesn't appear to be introducing the *specific* kind of risk that KMSAN targets. The focus is mainly on error handling, and the changes do not introduce or expose uninitialized memory risks.

Therefore, running a dedicated KMSAN session isn't warranted in this instance.



56/3 2026/10/26 09:17 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch refactors kernel virtual memory mapping and unmapping functions (vmap, ioremap, vunmap, and percpu-vm) so that partially mapped page table ranges automatically unwind themselves on failure. It also introduces a `can_resched` flag to avoid calling `cond_resched()` in atomic contexts and updates the KMSAN shadow/origin mapping hooks to reflect these signature changes and unwind shadow mappings on error.\n\nKMSAN is specifically designed to detect uninitialized memory reads and kernel-to-user memory info-leaks. The changes in this patch:\n1. Do not copy any data to user space (no copy_to_user, netlink skb, ioctls, etc.).\n2. Do not introduce any variables or structures that could be read while uninitialized; all new and modified local variables (such as err, start, can_resched) are explicitly initialized.\n3. Primarily affect page table manipulation, error unwinding, and context scheduling (sleeping vs atomic).\n\nAny potential bugs resulting from these changes (such as sleeping in atomic context, improper TLB flushing, or invalid page table references) are covered by standard kernel detectors such as LOCKDEP, CONFIG_DEBUG_VM, and KASAN. Therefore, dedicated KMSAN fuzzing is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch refactors kernel virtual memory mapping and unmapping functions (vmap, ioremap, vunmap, and percpu-vm) so that partially mapped page table ranges automatically unwind themselves on failure. It also introduces a `can_resched` flag to avoid calling `cond_resched()` in atomic contexts and updates the KMSAN shadow/origin mapping hooks to reflect these signature changes and unwind shadow mappings on error.\n\nKMSAN is specifically designed to detect uninitialized memory reads and kernel-to-user memory info-leaks. The changes in this patch:\n1. Do not copy any data to user space (no copy_to_user, netlink skb, ioctls, etc.).\n2. Do not introduce any variables or structures that could be read while uninitialized; all new and modified local variables (such as err, start, can_resched) are explicitly initialized.\n3. Primarily affect page table manipulation, error unwinding, and context scheduling (sleeping vs atomic).\n\nAny potential bugs resulting from these changes (such as sleeping in atomic context, improper TLB flushing, or invalid page table references) are covered by standard kernel detectors such as LOCKDEP, CONFIG_DEBUG_VM, and KASAN. Therefore, dedicated KMSAN fuzzing is not justified.",
  "NeedsKMSAN": false
}

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)