| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/11 06:37 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"PTDUMP_DEBUGFS",
"DEBUG_WX"
],
"FocusSymbols": [
"vmap_try_huge_pmd",
"vmap_try_huge_pud",
"vmap_try_huge_p4d",
"walk_page_range_debug",
"pmd_free_pte_page",
"pud_free_pmd_page"
],
"Reasoning": "The patch modifies the locking logic for freeing page tables during huge vmalloc mappings, replacing an arm64-specific static key with a generic mmap_read_lock_try in core vmalloc code. It also adds an mmap write lock assertion in walk_page_range_debug. This is functional core kernel and architecture code that should be fuzzed to ensure no deadlocks or use-after-free issues occur during concurrent vmalloc and ptdump operations.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/11 06:37 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 79eed1851feff772655b1ad4c8d419f939e00ff8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Jul 11 06:37:39 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h\nindex 5b374a6ab34a4e..50a195eda8ed22 100644\n--- a/arch/arm64/include/asm/ptdump.h\n+++ b/arch/arm64/include/asm/ptdump.h\n@@ -7,8 +7,6 @@\n \n #include \u003clinux/ptdump.h\u003e\n \n-DECLARE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);\n-\n #ifdef CONFIG_PTDUMP\n \n #include \u003clinux/mm_types.h\u003e\ndiff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c\nindex f2be501468ce5a..f723bcf68174fb 100644\n--- a/arch/arm64/mm/mmu.c\n+++ b/arch/arm64/mm/mmu.c\n@@ -49,8 +49,6 @@\n #define NO_CONT_MAPPINGS\tBIT(1)\n #define NO_EXEC_MAPPINGS\tBIT(2)\t/* assumes FEAT_HPDS is not used */\n \n-DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);\n-\n u64 kimage_voffset __ro_after_init;\n EXPORT_SYMBOL(kimage_voffset);\n \n@@ -1857,8 +1855,7 @@ int pmd_clear_huge(pmd_t *pmdp)\n \treturn 1;\n }\n \n-static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,\n-\t\t\t bool acquire_mmap_lock)\n+int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)\n {\n \tpte_t *table;\n \tpmd_t pmd;\n@@ -1870,25 +1867,13 @@ static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,\n \t\treturn 1;\n \t}\n \n-\t/* See comment in pud_free_pmd_page for static key logic */\n \ttable = pte_offset_kernel(pmdp, addr);\n \tpmd_clear(pmdp);\n \t__flush_tlb_kernel_pgtable(addr);\n-\tif (static_branch_unlikely(\u0026arm64_ptdump_lock_key) \u0026\u0026 acquire_mmap_lock) {\n-\t\tmmap_read_lock(\u0026init_mm);\n-\t\tmmap_read_unlock(\u0026init_mm);\n-\t}\n-\n \tpte_free_kernel(NULL, table);\n \treturn 1;\n }\n \n-int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)\n-{\n-\t/* If ptdump is walking the pagetables, acquire init_mm.mmap_lock */\n-\treturn __pmd_free_pte_page(pmdp, addr, /* acquire_mmap_lock = */ true);\n-}\n-\n int pud_free_pmd_page(pud_t *pudp, unsigned long addr)\n {\n \tpmd_t *table;\n@@ -1904,36 +1889,16 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)\n \t}\n \n \ttable = pmd_offset(pudp, addr);\n-\n-\t/*\n-\t * Our objective is to prevent ptdump from reading a PMD table which has\n-\t * been freed. In this race, if pud_free_pmd_page observes the key on\n-\t * (which got flipped by ptdump) then the mmap lock sequence here will,\n-\t * as a result of the mmap write lock/unlock sequence in ptdump, give\n-\t * us the correct synchronization. If not, this means that ptdump has\n-\t * yet not started walking the pagetables - the sequence of barriers\n-\t * issued by __flush_tlb_kernel_pgtable() guarantees that ptdump will\n-\t * observe an empty PUD.\n-\t */\n-\tpud_clear(pudp);\n-\t__flush_tlb_kernel_pgtable(addr);\n-\tif (static_branch_unlikely(\u0026arm64_ptdump_lock_key)) {\n-\t\tmmap_read_lock(\u0026init_mm);\n-\t\tmmap_read_unlock(\u0026init_mm);\n-\t}\n-\n \tpmdp = table;\n \tnext = addr;\n \tend = addr + PUD_SIZE;\n \tdo {\n \t\tif (pmd_present(pmdp_get(pmdp)))\n-\t\t\t/*\n-\t\t\t * PMD has been isolated, so ptdump won't see it. No\n-\t\t\t * need to acquire init_mm.mmap_lock.\n-\t\t\t */\n-\t\t\t__pmd_free_pte_page(pmdp, next, /* acquire_mmap_lock = */ false);\n+\t\t\tpmd_free_pte_page(pmdp, next);\n \t} while (pmdp++, next += PMD_SIZE, next != end);\n \n+\tpud_clear(pudp);\n+\t__flush_tlb_kernel_pgtable(addr);\n \tpmd_free(NULL, table);\n \treturn 1;\n }\ndiff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c\nindex 1c20144700d7d7..5a76c59b5ada55 100644\n--- a/arch/arm64/mm/ptdump.c\n+++ b/arch/arm64/mm/ptdump.c\n@@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_state *pt_st)\n \tnote_page(pt_st, 0, -1, pte_val(pte_zero));\n }\n \n-static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)\n-{\n-\tstatic_branch_inc(\u0026arm64_ptdump_lock_key);\n-\tptdump_walk_pgd(st, mm, NULL);\n-\tstatic_branch_dec(\u0026arm64_ptdump_lock_key);\n-}\n-\n void ptdump_walk(struct seq_file *s, struct ptdump_info *info)\n {\n \tunsigned long end = ~0UL;\n@@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)\n \t\t}\n \t};\n \n-\tarm64_ptdump_walk_pgd(\u0026st.ptdump, info-\u003emm);\n+\tptdump_walk_pgd(\u0026st.ptdump, info-\u003emm, NULL);\n }\n \n static void __init ptdump_initialize(void)\n@@ -360,7 +353,7 @@ bool ptdump_check_wx(void)\n \t\t}\n \t};\n \n-\tarm64_ptdump_walk_pgd(\u0026st.ptdump, \u0026init_mm);\n+\tptdump_walk_pgd(\u0026st.ptdump, \u0026init_mm, NULL);\n \n \tif (st.wx_pages || st.uxn_pages) {\n \t\tpr_warn(\"Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\\n\",\ndiff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h\nindex 04b8f61ece5d8e..6b5c2390cc301f 100644\n--- a/include/linux/mmap_lock.h\n+++ b/include/linux/mmap_lock.h\n@@ -621,6 +621,7 @@ static inline void mmap_read_unlock(struct mm_struct *mm)\n \n DEFINE_GUARD(mmap_read_lock, struct mm_struct *,\n \t mmap_read_lock(_T), mmap_read_unlock(_T))\n+DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))\n \n static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)\n {\ndiff --git a/mm/pagewalk.c b/mm/pagewalk.c\nindex 3ae2586ff45b44..bbcfd68d0907df 100644\n--- a/mm/pagewalk.c\n+++ b/mm/pagewalk.c\n@@ -678,6 +678,8 @@ int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end\n * will also not lock the PTEs for the pte_entry() callback.\n *\n * This is for debugging purposes ONLY.\n+ *\n+ * The mmap write lock must be held.\n */\n int walk_page_range_debug(struct mm_struct *mm, unsigned long start,\n \t\t\t unsigned long end, const struct mm_walk_ops *ops,\n@@ -691,6 +693,16 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,\n \t\t.no_vma\t\t= true\n \t};\n \n+\t/*\n+\t * When walking userland page tables, an mmap write lock must be held to\n+\t * account for munmap() downgrading to an mmap read lock when tearing\n+\t * down page tables.\n+\t *\n+\t * When walking kernel page tables, an mmap write lock must also be held\n+\t * to account for page table freeing on vmap huge page mapping.\n+\t */\n+\tmmap_assert_write_locked(mm);\n+\n \t/* For convenience, we allow traversal of kernel mappings. */\n \tif (mm == \u0026init_mm)\n \t\treturn walk_kernel_page_table_range(start, end, ops,\n@@ -700,16 +712,6 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,\n \tif (!check_ops_safe(ops))\n \t\treturn -EINVAL;\n \n-\t/*\n-\t * The mmap lock protects the page walker from changes to the page\n-\t * tables during the walk. However a read lock is insufficient to\n-\t * protect those areas which don't have a VMA as munmap() detaches\n-\t * the VMAs before downgrading to a read lock and actually tearing\n-\t * down PTEs/page tables. In which case, the mmap write lock should\n-\t * be held.\n-\t */\n-\tmmap_assert_write_locked(mm);\n-\n \treturn walk_pgd_range(start, end, \u0026walk);\n }\n \ndiff --git a/mm/vmalloc.c b/mm/vmalloc.c\nindex 1afca3568b9b65..537ff7b3c41270 100644\n--- a/mm/vmalloc.c\n+++ b/mm/vmalloc.c\n@@ -43,6 +43,7 @@\n #include \u003casm/tlbflush.h\u003e\n #include \u003casm/shmparam.h\u003e\n #include \u003clinux/page_owner.h\u003e\n+#include \u003clinux/cleanup.h\u003e\n \n #define CREATE_TRACE_POINTS\n #include \u003ctrace/events/vmalloc.h\u003e\n@@ -158,10 +159,22 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,\n \tif (!IS_ALIGNED(phys_addr, PMD_SIZE))\n \t\treturn 0;\n \n-\tif (pmd_present(*pmd) \u0026\u0026 !pmd_free_pte_page(pmd, addr))\n-\t\treturn 0;\n+\tif (!pmd_present(*pmd))\n+\t\treturn pmd_set_huge(pmd, phys_addr, prot);\n \n-\treturn pmd_set_huge(pmd, phys_addr, prot);\n+\t/*\n+\t * Kernel page table walkers either walk ranges they own exclusively\n+\t * using the mmap lock for mutual exclusion, or hold the mmap write lock\n+\t * on init_mm (ptdump being the motivating case).\n+\t *\n+\t * Therefore, acquire the mmap read lock to prevent use-after-free when\n+\t * freeing page tables.\n+\t */\n+\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\n+\t\tif (!pmd_free_pte_page(pmd, addr))\n+\t\t\treturn 0;\n+\t\treturn pmd_set_huge(pmd, phys_addr, prot);\n+\t}\n }\n \n static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,\n@@ -210,10 +223,15 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end,\n \tif (!IS_ALIGNED(phys_addr, PUD_SIZE))\n \t\treturn 0;\n \n-\tif (pud_present(*pud) \u0026\u0026 !pud_free_pmd_page(pud, addr))\n-\t\treturn 0;\n+\tif (!pud_present(*pud))\n+\t\treturn pud_set_huge(pud, phys_addr, prot);\n \n-\treturn pud_set_huge(pud, phys_addr, prot);\n+\t/* See comment in vmap_try_huge_pmd(). */\n+\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\n+\t\tif (!pud_free_pmd_page(pud, addr))\n+\t\t\treturn 0;\n+\t\treturn pud_set_huge(pud, phys_addr, prot);\n+\t}\n }\n \n static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,\n@@ -262,10 +280,15 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end,\n \tif (!IS_ALIGNED(phys_addr, P4D_SIZE))\n \t\treturn 0;\n \n-\tif (p4d_present(*p4d) \u0026\u0026 !p4d_free_pud_page(p4d, addr))\n-\t\treturn 0;\n+\tif (!p4d_present(*p4d))\n+\t\treturn p4d_set_huge(p4d, phys_addr, prot);\n \n-\treturn p4d_set_huge(p4d, phys_addr, prot);\n+\t/* See comment in vmap_try_huge_pmd(). */\n+\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\n+\t\tif (!p4d_free_pud_page(p4d, addr))\n+\t\t\treturn 0;\n+\t\treturn p4d_set_huge(p4d, phys_addr, prot);\n+\t}\n }\n \n static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,\n"
} |
| 2/1 | 2026/07/11 06:37 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"PTDUMP_DEBUGFS",
"DEBUG_WX"
],
"FocusSymbols": [
"vmap_try_huge_pmd",
"vmap_try_huge_pud",
"vmap_try_huge_p4d",
"walk_page_range_debug",
"pmd_free_pte_page",
"pud_free_pmd_page"
],
"Reasoning": "The patch modifies the locking logic for freeing page tables during huge vmalloc mappings, replacing an arm64-specific static key with a generic mmap_read_lock_try in core vmalloc code. It also adds an mmap write lock assertion in walk_page_range_debug. This is functional core kernel and architecture code that should be fuzzed to ensure no deadlocks or use-after-free issues occur during concurrent vmalloc and ptdump operations.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 79eed1851feff772655b1ad4c8d419f939e00ff8
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Jul 11 06:37:39 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
index 5b374a6ab34a4e..50a195eda8ed22 100644
--- a/arch/arm64/include/asm/ptdump.h
+++ b/arch/arm64/include/asm/ptdump.h
@@ -7,8 +7,6 @@
#include <linux/ptdump.h>
-DECLARE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
-
#ifdef CONFIG_PTDUMP
#include <linux/mm_types.h>
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index f2be501468ce5a..f723bcf68174fb 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -49,8 +49,6 @@
#define NO_CONT_MAPPINGS BIT(1)
#define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */
-DEFINE_STATIC_KEY_FALSE(arm64_ptdump_lock_key);
-
u64 kimage_voffset __ro_after_init;
EXPORT_SYMBOL(kimage_voffset);
@@ -1857,8 +1855,7 @@ int pmd_clear_huge(pmd_t *pmdp)
return 1;
}
-static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
- bool acquire_mmap_lock)
+int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
{
pte_t *table;
pmd_t pmd;
@@ -1870,25 +1867,13 @@ static int __pmd_free_pte_page(pmd_t *pmdp, unsigned long addr,
return 1;
}
- /* See comment in pud_free_pmd_page for static key logic */
table = pte_offset_kernel(pmdp, addr);
pmd_clear(pmdp);
__flush_tlb_kernel_pgtable(addr);
- if (static_branch_unlikely(&arm64_ptdump_lock_key) && acquire_mmap_lock) {
- mmap_read_lock(&init_mm);
- mmap_read_unlock(&init_mm);
- }
-
pte_free_kernel(NULL, table);
return 1;
}
-int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)
-{
- /* If ptdump is walking the pagetables, acquire init_mm.mmap_lock */
- return __pmd_free_pte_page(pmdp, addr, /* acquire_mmap_lock = */ true);
-}
-
int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
{
pmd_t *table;
@@ -1904,36 +1889,16 @@ int pud_free_pmd_page(pud_t *pudp, unsigned long addr)
}
table = pmd_offset(pudp, addr);
-
- /*
- * Our objective is to prevent ptdump from reading a PMD table which has
- * been freed. In this race, if pud_free_pmd_page observes the key on
- * (which got flipped by ptdump) then the mmap lock sequence here will,
- * as a result of the mmap write lock/unlock sequence in ptdump, give
- * us the correct synchronization. If not, this means that ptdump has
- * yet not started walking the pagetables - the sequence of barriers
- * issued by __flush_tlb_kernel_pgtable() guarantees that ptdump will
- * observe an empty PUD.
- */
- pud_clear(pudp);
- __flush_tlb_kernel_pgtable(addr);
- if (static_branch_unlikely(&arm64_ptdump_lock_key)) {
- mmap_read_lock(&init_mm);
- mmap_read_unlock(&init_mm);
- }
-
pmdp = table;
next = addr;
end = addr + PUD_SIZE;
do {
if (pmd_present(pmdp_get(pmdp)))
- /*
- * PMD has been isolated, so ptdump won't see it. No
- * need to acquire init_mm.mmap_lock.
- */
- __pmd_free_pte_page(pmdp, next, /* acquire_mmap_lock = */ false);
+ pmd_free_pte_page(pmdp, next);
} while (pmdp++, next += PMD_SIZE, next != end);
+ pud_clear(pudp);
+ __flush_tlb_kernel_pgtable(addr);
pmd_free(NULL, table);
return 1;
}
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index 1c20144700d7d7..5a76c59b5ada55 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -283,13 +283,6 @@ void note_page_flush(struct ptdump_state *pt_st)
note_page(pt_st, 0, -1, pte_val(pte_zero));
}
-static void arm64_ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm)
-{
- static_branch_inc(&arm64_ptdump_lock_key);
- ptdump_walk_pgd(st, mm, NULL);
- static_branch_dec(&arm64_ptdump_lock_key);
-}
-
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
{
unsigned long end = ~0UL;
@@ -318,7 +311,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
}
};
- arm64_ptdump_walk_pgd(&st.ptdump, info->mm);
+ ptdump_walk_pgd(&st.ptdump, info->mm, NULL);
}
static void __init ptdump_initialize(void)
@@ -360,7 +353,7 @@ bool ptdump_check_wx(void)
}
};
- arm64_ptdump_walk_pgd(&st.ptdump, &init_mm);
+ ptdump_walk_pgd(&st.ptdump, &init_mm, NULL);
if (st.wx_pages || st.uxn_pages) {
pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found, %lu non-UXN pages found\n",
diff --git a/include/linux/mmap_lock.h b/include/linux/mmap_lock.h
index 04b8f61ece5d8e..6b5c2390cc301f 100644
--- a/include/linux/mmap_lock.h
+++ b/include/linux/mmap_lock.h
@@ -621,6 +621,7 @@ static inline void mmap_read_unlock(struct mm_struct *mm)
DEFINE_GUARD(mmap_read_lock, struct mm_struct *,
mmap_read_lock(_T), mmap_read_unlock(_T))
+DEFINE_GUARD_COND(mmap_read_lock, _try, mmap_read_trylock(_T))
static inline void mmap_read_unlock_non_owner(struct mm_struct *mm)
{
diff --git a/mm/pagewalk.c b/mm/pagewalk.c
index 3ae2586ff45b44..bbcfd68d0907df 100644
--- a/mm/pagewalk.c
+++ b/mm/pagewalk.c
@@ -678,6 +678,8 @@ int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end
* will also not lock the PTEs for the pte_entry() callback.
*
* This is for debugging purposes ONLY.
+ *
+ * The mmap write lock must be held.
*/
int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
unsigned long end, const struct mm_walk_ops *ops,
@@ -691,6 +693,16 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
.no_vma = true
};
+ /*
+ * When walking userland page tables, an mmap write lock must be held to
+ * account for munmap() downgrading to an mmap read lock when tearing
+ * down page tables.
+ *
+ * When walking kernel page tables, an mmap write lock must also be held
+ * to account for page table freeing on vmap huge page mapping.
+ */
+ mmap_assert_write_locked(mm);
+
/* For convenience, we allow traversal of kernel mappings. */
if (mm == &init_mm)
return walk_kernel_page_table_range(start, end, ops,
@@ -700,16 +712,6 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
if (!check_ops_safe(ops))
return -EINVAL;
- /*
- * The mmap lock protects the page walker from changes to the page
- * tables during the walk. However a read lock is insufficient to
- * protect those areas which don't have a VMA as munmap() detaches
- * the VMAs before downgrading to a read lock and actually tearing
- * down PTEs/page tables. In which case, the mmap write lock should
- * be held.
- */
- mmap_assert_write_locked(mm);
-
return walk_pgd_range(start, end, &walk);
}
diff --git a/mm/vmalloc.c b/mm/vmalloc.c
index 1afca3568b9b65..537ff7b3c41270 100644
--- a/mm/vmalloc.c
+++ b/mm/vmalloc.c
@@ -43,6 +43,7 @@
#include <asm/tlbflush.h>
#include <asm/shmparam.h>
#include <linux/page_owner.h>
+#include <linux/cleanup.h>
#define CREATE_TRACE_POINTS
#include <trace/events/vmalloc.h>
@@ -158,10 +159,22 @@ static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, PMD_SIZE))
return 0;
- if (pmd_present(*pmd) && !pmd_free_pte_page(pmd, addr))
- return 0;
+ if (!pmd_present(*pmd))
+ return pmd_set_huge(pmd, phys_addr, prot);
- return pmd_set_huge(pmd, phys_addr, prot);
+ /*
+ * Kernel page table walkers either walk ranges they own exclusively
+ * using the mmap lock for mutual exclusion, or hold the mmap write lock
+ * on init_mm (ptdump being the motivating case).
+ *
+ * Therefore, acquire the mmap read lock to prevent use-after-free when
+ * freeing page tables.
+ */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!pmd_free_pte_page(pmd, addr))
+ return 0;
+ return pmd_set_huge(pmd, phys_addr, prot);
+ }
}
static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,
@@ -210,10 +223,15 @@ static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, PUD_SIZE))
return 0;
- if (pud_present(*pud) && !pud_free_pmd_page(pud, addr))
- return 0;
+ if (!pud_present(*pud))
+ return pud_set_huge(pud, phys_addr, prot);
- return pud_set_huge(pud, phys_addr, prot);
+ /* See comment in vmap_try_huge_pmd(). */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!pud_free_pmd_page(pud, addr))
+ return 0;
+ return pud_set_huge(pud, phys_addr, prot);
+ }
}
static int vmap_pud_range(p4d_t *p4d, unsigned long addr, unsigned long end,
@@ -262,10 +280,15 @@ static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end,
if (!IS_ALIGNED(phys_addr, P4D_SIZE))
return 0;
- if (p4d_present(*p4d) && !p4d_free_pud_page(p4d, addr))
- return 0;
+ if (!p4d_present(*p4d))
+ return p4d_set_huge(p4d, phys_addr, prot);
- return p4d_set_huge(p4d, phys_addr, prot);
+ /* See comment in vmap_try_huge_pmd(). */
+ scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm) {
+ if (!p4d_free_pud_page(p4d, addr))
+ return 0;
+ return p4d_set_huge(p4d, phys_addr, prot);
+ }
}
static int vmap_p4d_range(pgd_t *pgd, unsigned long addr, unsigned long end,
|
| 3/2 | 2026/07/11 06:37 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 5500 output: 23 thoughts: 398 Thoughts: **Analyzing a Patch: Huge Pages and vmalloc** Okay, so I'm looking at this patch and my initial thought is, "Ah, it's messing with page table dumping and `vmalloc` code, specifically around huge pages and locking." The patch looks like it's taking out the `arm64_ptdump_lock_key` static key logic from the `arm64` architecture's `mmu.c` and `ptdump.c` files. That's interesting, I wonder why they're doing that. Instead, it's introducing `mmap_read_lock_try` calls within `vmap_try_huge_pmd`, `vmap_try_huge_pud`, and `vmap_try_huge_p4d` functions in `mm/vmalloc.c`. And it also touches `walk_page_range_debug` in `mm/pagewalk.c`, making it assert `mmap_assert_write_locked(mm)` for kernel page tables. My immediate question is, "Is this functional and, more importantly, is it actually *reachable*?" Let's see... `vmap_try_huge_pmd` is called from `vmap_pmd_range`, which is part of the `vmalloc` path. Okay, that confirms that `vmalloc` itself is definitely reachable. This patch seems to be altering how page tables are freed when we're creating those huge mappings inside `vmalloc`. It's using `scoped_cond_guard(mmap_read_lock_try, return 0, &init_mm)` to get the `mmap` read lock. If the lock acquisition fails, it returns 0, which likely signals a fallback to non-huge mapping, or perhaps a failure altogether. Now I'm thinking, "Wait a second, this is touching `mm/vmalloc.c`... that's core kernel code. This is definitely something I need to look at carefully." I'm going to dig into `vmap_try_huge_pmd` in `mm/vmalloc.c` now to see what's what. Okay, so huge page vmalloc *is* reachable – that confirms it. Now, let's see if there are any specific kernel configurations involved, anything that this depends on. Perhaps something like `CONFIG_HAVE_ARCH_HUGE_VMALLOC`? I should probably grep for `vmap_try_huge_pmd` just to make sure I don't miss anything relevant in the broader context. |
| 4/2 | 2026/07/11 06:37 | tool | grepper |
0mArgs:{
"Expression": "vmap_try_huge_pmd"
}Results: {
"Output": "mm/vmalloc.c=95=static int vmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-142-\nmm/vmalloc.c:143:static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,\nmm/vmalloc.c-144-\t\t\tphys_addr_t phys_addr, pgprot_t prot,\n--\nmm/vmalloc.c=180=static int vmap_pmd_range(pud_t *pud, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-193-\nmm/vmalloc.c:194:\t\tif (vmap_try_huge_pmd(pmd, addr, next, phys_addr, prot,\nmm/vmalloc.c-195-\t\t\t\t\tmax_page_shift)) {\n--\nmm/vmalloc.c=207=static int vmap_try_huge_pud(pud_t *pud, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-228-\nmm/vmalloc.c:229:\t/* See comment in vmap_try_huge_pmd(). */\nmm/vmalloc.c-230-\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\n--\nmm/vmalloc.c=264=static int vmap_try_huge_p4d(p4d_t *p4d, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-285-\nmm/vmalloc.c:286:\t/* See comment in vmap_try_huge_pmd(). */\nmm/vmalloc.c-287-\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\n"
} |
| 5/2 | 2026/07/11 06:37 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 6963 output: 24 thoughts: 22 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Checking `HAVE_ARCH_HUGE_VMALLOC` Configuration** Okay, so I'm looking at this code and I need to make sure we're handling the vmalloc region correctly, particularly in relation to huge pages. My initial thought is to confirm we're using the right configuration macro. Let's see... is `HAVE_ARCH_HUGE_VMALLOC` the appropriate flag to determine if the architecture supports and utilizes huge vmalloc regions? It's crucial to get this right. We need this so that the kernel can allocate large contiguous virtual memory regions for certain operations. If it's not set correctly, we might be using suboptimal allocations, or worse, causing stability issues. I need to dig into the relevant architecture code and Kconfig files to confirm this is the definitive check and to ensure there aren't any other factors influencing the behavior of vmalloc and huge pages. Let's start by tracing the usages of this macro and how it affects the vmalloc implementation in different parts of the code. This will clarify if it's the gatekeeper, or if there are other, possibly platform-specific, considerations to account for. |
| 6/2 | 2026/07/11 06:37 | tool | grepper |
0mArgs:{
"Expression": "HAVE_ARCH_HUGE_VMALLOC"
}Results: {
"Output": "arch/Kconfig=1092=config HAVE_ARCH_HUGE_VMAP\n--\narch/Kconfig-1099-#\narch/Kconfig:1100:config HAVE_ARCH_HUGE_VMALLOC\narch/Kconfig-1101-\tdepends on HAVE_ARCH_HUGE_VMAP\n--\narch/arm64/Kconfig=2=config ARM64\n--\narch/arm64/Kconfig-153-\tselect HAVE_ARCH_COMPILER_H\narch/arm64/Kconfig:154:\tselect HAVE_ARCH_HUGE_VMALLOC\narch/arm64/Kconfig-155-\tselect HAVE_ARCH_HUGE_VMAP\n--\narch/powerpc/Kconfig=118=config PPC\n--\narch/powerpc/Kconfig-218-\tselect HAVE_ARCH_AUDITSYSCALL\narch/powerpc/Kconfig:219:\tselect HAVE_ARCH_HUGE_VMALLOC\t\tif HAVE_ARCH_HUGE_VMAP\narch/powerpc/Kconfig-220-\tselect HAVE_ARCH_HUGE_VMAP\t\tif PPC_RADIX_MMU || PPC_8xx\n--\narch/riscv/Kconfig=13=config RISCV\n--\narch/riscv/Kconfig-131-\tselect HAVE_ARCH_BITREVERSE if RISCV_ISA_ZBKB \u0026\u0026 BITREVERSE\narch/riscv/Kconfig:132:\tselect HAVE_ARCH_HUGE_VMALLOC if HAVE_ARCH_HUGE_VMAP\narch/riscv/Kconfig-133-\tselect HAVE_ARCH_HUGE_VMAP if MMU \u0026\u0026 64BIT\n--\narch/x86/Kconfig=58=config X86\n--\narch/x86/Kconfig-192-\tselect HAVE_ARCH_HUGE_VMAP\t\tif X86_64 || X86_PAE\narch/x86/Kconfig:193:\tselect HAVE_ARCH_HUGE_VMALLOC\t\tif X86_64\narch/x86/Kconfig-194-\tselect HAVE_ARCH_JUMP_LABEL\n--\ninclude/linux/vmalloc.h=19=struct iov_iter;\t\t/* in uio.h */\n--\ninclude/linux/vmalloc.h-31-#define VM_MAP_PUT_PAGES\t0x00000200\t/* put pages and free array in vfree */\ninclude/linux/vmalloc.h:32:#define VM_ALLOW_HUGE_VMAP\t0x00000400 /* Allow for huge pages on archs with HAVE_ARCH_HUGE_VMALLOC */\ninclude/linux/vmalloc.h-33-\n--\ninclude/linux/vmalloc.h=52=struct vm_struct {\n--\ninclude/linux/vmalloc.h-61-\tstruct page\t\t**pages;\ninclude/linux/vmalloc.h:62:#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC\ninclude/linux/vmalloc.h-63-\tunsigned int\t\tpage_order;\n--\ninclude/linux/vmalloc.h=258=static inline bool is_vm_area_hugepages(const void *addr)\n--\ninclude/linux/vmalloc.h-266-\t */\ninclude/linux/vmalloc.h:267:#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC\ninclude/linux/vmalloc.h-268-\tstruct vm_struct *area = find_vm_area(addr);\n--\nmm/vmalloc.c=64=static const unsigned int ioremap_max_page_shift = PAGE_SHIFT;\n--\nmm/vmalloc.c-66-\nmm/vmalloc.c:67:#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC\nmm/vmalloc.c-68-static bool __ro_after_init vmap_allow_huge = true;\n--\nmm/vmalloc.c=75=early_param(\"nohugevmalloc\", set_nohugevmalloc);\nmm/vmalloc.c:76:#else /* CONFIG_HAVE_ARCH_HUGE_VMALLOC */\nmm/vmalloc.c-77-static const bool vmap_allow_huge = false;\nmm/vmalloc.c:78:#endif\t/* CONFIG_HAVE_ARCH_HUGE_VMALLOC */\nmm/vmalloc.c-79-\n--\nmm/vmalloc.c=682=int __vmap_pages_range_noflush(unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-688-\nmm/vmalloc.c:689:\tif (!IS_ENABLED(CONFIG_HAVE_ARCH_HUGE_VMALLOC) ||\nmm/vmalloc.c-690-\t\t\tpage_shift == PAGE_SHIFT)\n--\nmm/vmalloc.c=3128=static inline unsigned int vm_area_page_order(struct vm_struct *vm)\nmm/vmalloc.c-3129-{\nmm/vmalloc.c:3130:#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC\nmm/vmalloc.c-3131-\treturn vm-\u003epage_order;\n--\nmm/vmalloc.c=3142=static inline void set_vm_area_page_order(struct vm_struct *vm, unsigned int order)\nmm/vmalloc.c-3143-{\nmm/vmalloc.c:3144:#ifdef CONFIG_HAVE_ARCH_HUGE_VMALLOC\nmm/vmalloc.c-3145-\tvm-\u003epage_order = order;\n"
} |
| 7/2 | 2026/07/11 06:37 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8653 output: 17 thoughts: 418 Thoughts: **Analyzing a Kernel Patch: A Lock-Down on Huge vmalloc Page Table Races** Okay, let's break down this patch. My immediate focus is on the changes to `mm/vmalloc.c`, specifically those alterations to `vmap_try_huge_pmd`, `vmap_try_huge_pud`, and `vmap_try_huge_p4d`. These are called by the range-based functions, meaning they're central to enabling huge vmalloc mappings – so, we're talking performance optimizations here. I see they're also touching `arch/arm64/mm/mmu.c` and `arch/arm64/mm/ptdump.c`, which always warrants close attention, since ARM64 is a key target. Specifically, changes to `pmd_free_pte_page`, `pud_free_pmd_page` et cetera, hint at modifications to memory management routines – standard territory. Also `ptdump_walk` and `ptdump_check_wx`, that's an interesting combination with the vmalloc changes. Ah, here's the kicker: the patch *removes* `arm64_ptdump_lock_key` and adds `mmap_read_lock_try` within `vmap_try_huge_pmd`. This is a *functional* change, not just a cleanup. It's addressing a race condition! I'm betting that the race is between freeing vmalloc huge page tables and `ptdump` walking those tables. This locks it down. Now, the question becomes, is this patch fuzzable? Absolutely. It touches core kernel locking primitives in vmalloc and page table walking, so this is critical. A fuzzing campaign here is definitely a good idea, particularly given it impacts a variety of ARM64 configurations. As for configurations, well `CONFIG_PTDUMP_CORE` or `CONFIG_PTDUMP_DEBUGFS` are the obvious ones for `ptdump`. Since `CONFIG_HAVE_ARCH_HUGE_VMALLOC` is architecture-selected, no need to worry there, but `CONFIG_DEBUG_WX` or `CONFIG_PTDUMP_DEBUGFS` may be useful. Time to check `CONFIG_PTDUMP` in `arch/arm64/Kconfig` for related config dependencies or any relevant details I might have missed. |
| 8/2 | 2026/07/11 06:38 | tool | grepper |
0mArgs:{
"Expression": "PTDUMP"
}Results: {
"Output": "Documentation/arch/arm64/ptdump.rst=23=configurations and mount debugfs::\nDocumentation/arch/arm64/ptdump.rst-24-\nDocumentation/arch/arm64/ptdump.rst:25: CONFIG_PTDUMP_DEBUGFS=y\nDocumentation/arch/arm64/ptdump.rst-26-\n--\narch/arm/Kconfig.debug-2-\narch/arm/Kconfig.debug:3:config ARM_PTDUMP_CORE\narch/arm/Kconfig.debug-4-\tdef_bool n\narch/arm/Kconfig.debug-5-\narch/arm/Kconfig.debug:6:config ARM_PTDUMP_DEBUGFS\narch/arm/Kconfig.debug-7-\tbool \"Export kernel pagetable layout to userspace via debugfs\"\n--\narch/arm/Kconfig.debug-9-\tdepends on MMU\narch/arm/Kconfig.debug:10:\tselect ARM_PTDUMP_CORE\narch/arm/Kconfig.debug-11-\tselect DEBUG_FS\n--\narch/arm/Kconfig.debug=20=config ARM_DEBUG_WX\n--\narch/arm/Kconfig.debug-22-\tdepends on MMU\narch/arm/Kconfig.debug:23:\tselect ARM_PTDUMP_CORE\narch/arm/Kconfig.debug-24-\thelp\n--\narch/arm/include/asm/ptdump.h-2-/* Copyright (C) 2014 ARM Ltd. */\narch/arm/include/asm/ptdump.h:3:#ifndef __ASM_PTDUMP_H\narch/arm/include/asm/ptdump.h:4:#define __ASM_PTDUMP_H\narch/arm/include/asm/ptdump.h-5-\narch/arm/include/asm/ptdump.h:6:#ifdef CONFIG_ARM_PTDUMP_CORE\narch/arm/include/asm/ptdump.h-7-\n--\narch/arm/include/asm/ptdump.h=22=void ptdump_walk_pgd(struct seq_file *s, struct ptdump_info *info);\narch/arm/include/asm/ptdump.h:23:#ifdef CONFIG_ARM_PTDUMP_DEBUGFS\narch/arm/include/asm/ptdump.h-24-#define EFI_RUNTIME_MAP_END\tSZ_1G\n--\narch/arm/include/asm/ptdump.h=27=static inline void ptdump_debugfs_register(struct ptdump_info *info,\narch/arm/include/asm/ptdump.h-28-\t\t\t\t\t const char *name) { }\narch/arm/include/asm/ptdump.h:29:#endif /* CONFIG_ARM_PTDUMP_DEBUGFS */\narch/arm/include/asm/ptdump.h-30-\narch/arm/include/asm/ptdump.h=31=void ptdump_check_wx(void);\narch/arm/include/asm/ptdump.h-32-\narch/arm/include/asm/ptdump.h:33:#endif /* CONFIG_ARM_PTDUMP_CORE */\narch/arm/include/asm/ptdump.h-34-\n--\narch/arm/include/asm/ptdump.h-40-\narch/arm/include/asm/ptdump.h:41:#endif /* __ASM_PTDUMP_H */\n--\narch/arm/mm/Makefile=15=endif\narch/arm/mm/Makefile-16-\narch/arm/mm/Makefile:17:obj-$(CONFIG_ARM_PTDUMP_CORE)\t+= dump.o\narch/arm/mm/Makefile:18:obj-$(CONFIG_ARM_PTDUMP_DEBUGFS)\t+= ptdump_debugfs.o\narch/arm/mm/Makefile-19-obj-$(CONFIG_MODULES)\t\t+= proc-syms.o\n--\narch/arm64/Kconfig=2=config ARM64\n--\narch/arm64/Kconfig-44-\tselect ARCH_HAS_PREEMPT_LAZY\narch/arm64/Kconfig:45:\tselect ARCH_HAS_PTDUMP\narch/arm64/Kconfig-46-\tselect ARCH_HAS_PTE_SPECIAL\n--\narch/arm64/include/asm/kvm_host.h=153=struct kvm_s2_mmu {\n--\narch/arm64/include/asm/kvm_host.h-219-\narch/arm64/include/asm/kvm_host.h:220:#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS\narch/arm64/include/asm/kvm_host.h-221-\tstruct dentry *shadow_pt_debugfs_dentry;\n--\narch/arm64/include/asm/kvm_host.h=309=struct kvm_arch {\n--\narch/arm64/include/asm/kvm_host.h-422-\narch/arm64/include/asm/kvm_host.h:423:#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS\narch/arm64/include/asm/kvm_host.h-424-\t/* Nested virtualization info */\n--\narch/arm64/include/asm/kvm_mmu.h=387=static inline bool kvm_supports_cacheable_pfnmap(void)\n--\narch/arm64/include/asm/kvm_mmu.h-392-\narch/arm64/include/asm/kvm_mmu.h:393:#ifdef CONFIG_PTDUMP_STAGE2_DEBUGFS\narch/arm64/include/asm/kvm_mmu.h-394-void kvm_s2_ptdump_create_debugfs(struct kvm *kvm);\n--\narch/arm64/include/asm/kvm_mmu.h=400=static inline void kvm_nested_s2_ptdump_remove_debugfs(struct kvm_s2_mmu *mmu) {}\narch/arm64/include/asm/kvm_mmu.h:401:#endif /* CONFIG_PTDUMP_STAGE2_DEBUGFS */\narch/arm64/include/asm/kvm_mmu.h-402-\n--\narch/arm64/include/asm/ptdump.h-4- */\narch/arm64/include/asm/ptdump.h:5:#ifndef __ASM_PTDUMP_H\narch/arm64/include/asm/ptdump.h:6:#define __ASM_PTDUMP_H\narch/arm64/include/asm/ptdump.h-7-\n--\narch/arm64/include/asm/ptdump.h-9-\narch/arm64/include/asm/ptdump.h:10:#ifdef CONFIG_PTDUMP\narch/arm64/include/asm/ptdump.h-11-\n--\narch/arm64/include/asm/ptdump.h=68=void note_page_flush(struct ptdump_state *st);\narch/arm64/include/asm/ptdump.h:69:#ifdef CONFIG_PTDUMP_DEBUGFS\narch/arm64/include/asm/ptdump.h-70-#define EFI_RUNTIME_MAP_END\tDEFAULT_MAP_WINDOW_64\n--\narch/arm64/include/asm/ptdump.h=73=static inline void ptdump_debugfs_register(struct ptdump_info *info,\narch/arm64/include/asm/ptdump.h-74-\t\t\t\t\t const char *name) { }\narch/arm64/include/asm/ptdump.h:75:#endif /* CONFIG_PTDUMP_DEBUGFS */\narch/arm64/include/asm/ptdump.h-76-#else\n--\narch/arm64/include/asm/ptdump.h=84=static inline void note_page_flush(struct ptdump_state *st) { }\narch/arm64/include/asm/ptdump.h:85:#endif /* CONFIG_PTDUMP */\narch/arm64/include/asm/ptdump.h-86-\narch/arm64/include/asm/ptdump.h:87:#endif /* __ASM_PTDUMP_H */\n--\narch/arm64/kvm/Kconfig=45=if KVM\narch/arm64/kvm/Kconfig-46-\narch/arm64/kvm/Kconfig:47:config PTDUMP_STAGE2_DEBUGFS\narch/arm64/kvm/Kconfig-48-\tbool \"Present the stage-2 pagetables to debugfs\"\n--\narch/arm64/kvm/Kconfig-50-\tdepends on DEBUG_FS\narch/arm64/kvm/Kconfig:51:\tdepends on ARCH_HAS_PTDUMP\narch/arm64/kvm/Kconfig:52:\tselect PTDUMP\narch/arm64/kvm/Kconfig-53-\tdefault n\n--\narch/arm64/kvm/Makefile=30=kvm-$(CONFIG_ARM64_PTR_AUTH) += pauth.o\narch/arm64/kvm/Makefile:31:kvm-$(CONFIG_PTDUMP_STAGE2_DEBUGFS) += ptdump.o\narch/arm64/kvm/Makefile-32-\n--\narch/arm64/mm/Makefile=7=obj-$(CONFIG_HUGETLB_PAGE)\t+= hugetlbpage.o\narch/arm64/mm/Makefile:8:obj-$(CONFIG_PTDUMP)\t\t+= ptdump.o\narch/arm64/mm/Makefile:9:obj-$(CONFIG_PTDUMP_DEBUGFS)\t+= ptdump_debugfs.o\narch/arm64/mm/Makefile-10-obj-$(CONFIG_TRANS_TABLE)\t+= trans_pgd.o\n--\narch/powerpc/Kconfig=118=config PPC\n--\narch/powerpc/Kconfig-149-\tselect ARCH_HAS_PREEMPT_LAZY\narch/powerpc/Kconfig:150:\tselect ARCH_HAS_PTDUMP\narch/powerpc/Kconfig-151-\tselect ARCH_HAS_PTE_SPECIAL\n--\narch/powerpc/configs/mpc885_ads_defconfig=79=CONFIG_PPC_EARLY_DEBUG=y\narch/powerpc/configs/mpc885_ads_defconfig:80:CONFIG_PTDUMP_DEBUGFS=y\n--\narch/powerpc/mm/Makefile=17=obj-$(CONFIG_PPC_COPRO_BASE)\t+= copro_fault.o\narch/powerpc/mm/Makefile:18:obj-$(CONFIG_PTDUMP)\t\t+= ptdump/\narch/powerpc/mm/Makefile-19-obj-$(CONFIG_KASAN)\t\t+= kasan/\n--\narch/powerpc/mm/ptdump/Makefile=9=obj-$(CONFIG_PPC_BOOK3S_64)\t+= book3s64.o\narch/powerpc/mm/ptdump/Makefile-10-\narch/powerpc/mm/ptdump/Makefile:11:ifdef CONFIG_PTDUMP_DEBUGFS\narch/powerpc/mm/ptdump/Makefile-12-obj-$(CONFIG_PPC_BOOK3S_32)\t+= bats.o segment_regs.o\n--\narch/powerpc/mm/ptdump/ptdump.c=406=static int __init ptdump_init(void)\n--\narch/powerpc/mm/ptdump/ptdump.c-419-\narch/powerpc/mm/ptdump/ptdump.c:420:\tif (IS_ENABLED(CONFIG_PTDUMP_DEBUGFS))\narch/powerpc/mm/ptdump/ptdump.c-421-\t\tdebugfs_create_file(\"kernel_page_tables\", 0400, NULL, NULL, \u0026ptdump_fops);\n--\narch/riscv/Kconfig=13=config RISCV\n--\narch/riscv/Kconfig-46-\tselect ARCH_HAS_PREPARE_SYNC_CORE_CMD\narch/riscv/Kconfig:47:\tselect ARCH_HAS_PTDUMP if MMU\narch/riscv/Kconfig-48-\tselect ARCH_HAS_PTE_SPECIAL\n--\narch/riscv/mm/Makefile=21=obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o\narch/riscv/mm/Makefile:22:obj-$(CONFIG_PTDUMP) += ptdump.o\narch/riscv/mm/Makefile-23-obj-$(CONFIG_KASAN) += kasan_init.o\n--\narch/s390/Kconfig=75=config S390\n--\narch/s390/Kconfig-103-\tselect ARCH_HAS_PREEMPT_LAZY\narch/s390/Kconfig:104:\tselect ARCH_HAS_PTDUMP\narch/s390/Kconfig-105-\tselect ARCH_HAS_PTE_SPECIAL\n--\narch/s390/configs/debug_defconfig=842=CONFIG_DEBUG_WX=y\narch/s390/configs/debug_defconfig:843:CONFIG_PTDUMP_DEBUGFS=y\narch/s390/configs/debug_defconfig-844-CONFIG_DEBUG_OBJECTS=y\n--\narch/s390/configs/defconfig=821=CONFIG_DEBUG_WX=y\narch/s390/configs/defconfig:822:CONFIG_PTDUMP_DEBUGFS=y\narch/s390/configs/defconfig-823-CONFIG_DEBUG_MEMORY_INIT=y\n--\narch/s390/mm/Makefile=11=obj-$(CONFIG_HUGETLB_PAGE)\t+= hugetlbpage.o\narch/s390/mm/Makefile:12:obj-$(CONFIG_PTDUMP)\t\t+= dump_pagetables.o\narch/s390/mm/Makefile-13-obj-$(CONFIG_PFAULT)\t\t+= pfault.o\n--\narch/s390/mm/dump_pagetables.c=182=bool ptdump_check_wx(void)\n--\narch/s390/mm/dump_pagetables.c-224-\narch/s390/mm/dump_pagetables.c:225:#ifdef CONFIG_PTDUMP_DEBUGFS\narch/s390/mm/dump_pagetables.c-226-static int ptdump_show(struct seq_file *m, void *v)\n--\narch/s390/mm/dump_pagetables.c=255=DEFINE_SHOW_ATTRIBUTE(ptdump);\narch/s390/mm/dump_pagetables.c:256:#endif /* CONFIG_PTDUMP_DEBUGFS */\narch/s390/mm/dump_pagetables.c-257-\n--\narch/s390/mm/dump_pagetables.c=315=static int pt_dump_init(void)\n--\narch/s390/mm/dump_pagetables.c-355-\tsort(\u0026markers[1], markers_cnt - 1, sizeof(*markers), ptdump_cmp, NULL);\narch/s390/mm/dump_pagetables.c:356:#ifdef CONFIG_PTDUMP_DEBUGFS\narch/s390/mm/dump_pagetables.c-357-\tdebugfs_create_file(\"kernel_page_tables\", 0400, NULL, NULL, \u0026ptdump_fops);\narch/s390/mm/dump_pagetables.c:358:#endif /* CONFIG_PTDUMP_DEBUGFS */\narch/s390/mm/dump_pagetables.c-359-\treturn 0;\n--\narch/x86/Kconfig=58=config X86\n--\narch/x86/Kconfig-98-\tselect ARCH_HAS_PREEMPT_LAZY\narch/x86/Kconfig:99:\tselect ARCH_HAS_PTDUMP\narch/x86/Kconfig-100-\tselect ARCH_HAS_PTE_SPECIAL\n--\narch/x86/Kconfig.debug=59=config EFI_PGT_DUMP\n--\narch/x86/Kconfig.debug-61-\tdepends on EFI\narch/x86/Kconfig.debug:62:\tselect PTDUMP\narch/x86/Kconfig.debug-63-\thelp\n--\narch/x86/mm/Makefile=36=obj-$(CONFIG_HUGETLB_PAGE)\t+= hugetlbpage.o\narch/x86/mm/Makefile:37:obj-$(CONFIG_PTDUMP)\t\t+= dump_pagetables.o\narch/x86/mm/Makefile:38:obj-$(CONFIG_PTDUMP_DEBUGFS)\t+= debug_pagetables.o\narch/x86/mm/Makefile-39-\n--\ndrivers/firmware/efi/arm-runtime.c-27-\ndrivers/firmware/efi/arm-runtime.c:28:#if defined(CONFIG_PTDUMP_DEBUGFS) || defined(CONFIG_ARM_PTDUMP_DEBUGFS)\ndrivers/firmware/efi/arm-runtime.c-29-#include \u003casm/ptdump.h\u003e\n--\ninclude/linux/ptdump.h-2-\ninclude/linux/ptdump.h:3:#ifndef _LINUX_PTDUMP_H\ninclude/linux/ptdump.h:4:#define _LINUX_PTDUMP_H\ninclude/linux/ptdump.h-5-\n--\ninclude/linux/ptdump.h=34=static inline void debug_checkwx(void)\n--\ninclude/linux/ptdump.h-39-\ninclude/linux/ptdump.h:40:#endif /* _LINUX_PTDUMP_H */\n--\nkernel/configs/debug.config=78=CONFIG_KASAN_VMALLOC=y\nkernel/configs/debug.config:79:CONFIG_PTDUMP_DEBUGFS=y\nkernel/configs/debug.config-80-CONFIG_SCHED_STACK_END_CHECK=y\n--\nmm/Kconfig.debug=186=config DEBUG_WX\n--\nmm/Kconfig.debug-188-\tdepends on ARCH_HAS_DEBUG_WX\nmm/Kconfig.debug:189:\tdepends on ARCH_HAS_PTDUMP\nmm/Kconfig.debug-190-\tdepends on MMU\nmm/Kconfig.debug:191:\tselect PTDUMP\nmm/Kconfig.debug-192-\thelp\n--\nmm/Kconfig.debug-215-\nmm/Kconfig.debug:216:config ARCH_HAS_PTDUMP\nmm/Kconfig.debug-217-\tbool\nmm/Kconfig.debug-218-\nmm/Kconfig.debug:219:config PTDUMP\nmm/Kconfig.debug-220-\tbool\nmm/Kconfig.debug-221-\nmm/Kconfig.debug:222:config PTDUMP_DEBUGFS\nmm/Kconfig.debug-223-\tbool \"Export kernel pagetable layout to userspace via debugfs\"\n--\nmm/Kconfig.debug-225-\tdepends on DEBUG_FS\nmm/Kconfig.debug:226:\tdepends on ARCH_HAS_PTDUMP\nmm/Kconfig.debug:227:\tselect PTDUMP\nmm/Kconfig.debug-228-\thelp\n--\nmm/Makefile=141=obj-$(CONFIG_MAPPING_DIRTY_HELPERS) += mapping_dirty_helpers.o\nmm/Makefile:142:obj-$(CONFIG_PTDUMP) += ptdump.o\nmm/Makefile-143-obj-$(CONFIG_PAGE_REPORTING) += page_reporting.o\n--\ntools/testing/selftests/bpf/config.aarch64=114=CONFIG_PROVE_LOCKING=y\ntools/testing/selftests/bpf/config.aarch64:115:CONFIG_PTDUMP_DEBUGFS=y\ntools/testing/selftests/bpf/config.aarch64-116-CONFIG_RC_DEVICES=y\n--\ntools/testing/selftests/bpf/config.s390x=95=CONFIG_PROVE_LOCKING=y\ntools/testing/selftests/bpf/config.s390x:96:CONFIG_PTDUMP_DEBUGFS=y\ntools/testing/selftests/bpf/config.s390x-97-CONFIG_RC_DEVICES=y\n"
} |
| 9/2 | 2026/07/11 06:38 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14717 output: 21 thoughts: 247 Thoughts: **My Analysis of the PTDUMP Configuration and VMALLOC Code** Okay, so I'm looking at this patch and immediately, the connection to `CONFIG_PTDUMP_DEBUGFS` is obvious. That's the primary enabler here. But, interestingly, `CONFIG_DEBUG_WX` also triggers ptdump functionality, which is good to keep in mind. The core of this patch seems to be modifying those `vmap_try_huge_*` functions within `mm/vmalloc.c` – specifically `vmap_try_huge_pmd`, `vmap_try_huge_pud`, and `vmap_try_huge_p4d`. I know these are only relevant when `vmap_allow_huge` is true, which, in turn, is directly tied to whether `CONFIG_HAVE_ARCH_HUGE_VMALLOC` is enabled. Seems pretty straightforward: If we want ptdump for this, we'll need to enable `PTDUMP_DEBUGFS`. No surprises there. And, I'm thinking, `DEBUG_WX` is another solid option to consider for this configuration. Now, let's dive deeper and pinpoint any specific functions that might be good candidates to monitor. Beyond those `vmap_try_huge_*` functions, the code mentions `walk_page_range_debug`, `pmd_free_pte_page`, and `pud_free_pmd_page`. I'm going to quickly verify if `walk_page_range_debug` is actually reachable in our setup. It's important to understand the call paths here to make sure we're getting what we need. |
| 10/2 | 2026/07/11 06:38 | tool | grepper |
0mArgs:{
"Expression": "walk_page_range_debug"
}Results: {
"Output": "Documentation/mm/process_addrs.rst=382=We also permit a truly unusual case is the traversal of non-VMA ranges in\nDocumentation/mm/process_addrs.rst:383:**userland** ranges, as provided for by :c:func:`!walk_page_range_debug`.\nDocumentation/mm/process_addrs.rst-384-\n--\nmm/internal.h=1840=int walk_page_range_vma_unsafe(struct vm_area_struct *vma, unsigned long start,\n--\nmm/internal.h-1842-\t\tvoid *private);\nmm/internal.h:1843:int walk_page_range_debug(struct mm_struct *mm, unsigned long start,\nmm/internal.h-1844-\t\t\t unsigned long end, const struct mm_walk_ops *ops,\n--\nmm/pagewalk.c=648=int walk_kernel_page_table_range_lockless(unsigned long start, unsigned long end,\n--\nmm/pagewalk.c-667-/**\nmm/pagewalk.c:668: * walk_page_range_debug - walk a range of pagetables not backed by a vma\nmm/pagewalk.c-669- * @mm:\t\tmm_struct representing the target process of page table walk\n--\nmm/pagewalk.c-683- */\nmm/pagewalk.c:684:int walk_page_range_debug(struct mm_struct *mm, unsigned long start,\nmm/pagewalk.c-685-\t\t\t unsigned long end, const struct mm_walk_ops *ops,\n--\nmm/ptdump.c=175=void ptdump_walk_pgd(struct ptdump_state *st, struct mm_struct *mm, pgd_t *pgd)\n--\nmm/ptdump.c-181-\twhile (range-\u003estart != range-\u003eend) {\nmm/ptdump.c:182:\t\twalk_page_range_debug(mm, range-\u003estart, range-\u003eend,\nmm/ptdump.c-183-\t\t\t\t \u0026ptdump_ops, pgd, st);\n"
} |
| 11/2 | 2026/07/11 06:38 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15878 output: 22 thoughts: 188 Thoughts: **Analyzing the Memory Management Patch** Okay, so I'm digging into this code and trying to get a handle on the changes. It looks like `ptdump` is the central piece here, which calls `ptdump_walk` and `ptdump_check_wx`, and those in turn call `ptdump_walk_pgd`. Then, `ptdump_walk_pgd` makes a call to `walk_page_range_debug`. So, `ptdump` is the primary driver, confirmed. The patch itself seems to be tweaking `vmap_try_huge_pmd`, which, if I recall correctly, comes into play during `vmalloc` operations. Knowing that, my focus should definitely be on the `vmap_try_huge_*` functions: `vmap_try_huge_pmd`, `vmap_try_huge_pud`, and `vmap_try_huge_p4d`. And of course, I need to keep an eye on `walk_page_range_debug`. I need to ensure that I understand how the memory is freed, so `pmd_free_pte_page` and `pud_free_pmd_page` need to be checked as well. Let me just double-check those last two; make sure I'm not missing anything important in their implementation. |
| 12/2 | 2026/07/11 06:38 | tool | grepper |
0mArgs:{
"Expression": "pmd_free_pte_page"
}Results: {
"Output": "arch/arm64/mm/mmu.c=1850=int pmd_clear_huge(pmd_t *pmdp)\n--\narch/arm64/mm/mmu.c-1857-\narch/arm64/mm/mmu.c:1858:int pmd_free_pte_page(pmd_t *pmdp, unsigned long addr)\narch/arm64/mm/mmu.c-1859-{\n--\narch/arm64/mm/mmu.c=1877=int pud_free_pmd_page(pud_t *pudp, unsigned long addr)\n--\narch/arm64/mm/mmu.c-1896-\t\tif (pmd_present(pmdp_get(pmdp)))\narch/arm64/mm/mmu.c:1897:\t\t\tpmd_free_pte_page(pmdp, next);\narch/arm64/mm/mmu.c-1898-\t} while (pmdp++, next += PMD_SIZE, next != end);\n--\narch/powerpc/mm/book3s64/radix_pgtable.c=1675=int pmd_clear_huge(pmd_t *pmd)\n--\narch/powerpc/mm/book3s64/radix_pgtable.c-1684-\narch/powerpc/mm/book3s64/radix_pgtable.c:1685:int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)\narch/powerpc/mm/book3s64/radix_pgtable.c-1686-{\n--\narch/riscv/mm/pgtable.c=117=int pmd_clear_huge(pmd_t *pmd)\n--\narch/riscv/mm/pgtable.c-124-\narch/riscv/mm/pgtable.c:125:int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)\narch/riscv/mm/pgtable.c-126-{\n--\narch/x86/mm/pgtable.c=718=int pud_free_pmd_page(pud_t *pud, unsigned long addr)\n--\narch/x86/mm/pgtable.c-754-/**\narch/x86/mm/pgtable.c:755: * pmd_free_pte_page - Clear PMD entry and free PTE page.\narch/x86/mm/pgtable.c-756- * @pmd: Pointer to the PMD\n--\narch/x86/mm/pgtable.c-761- */\narch/x86/mm/pgtable.c:762:int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)\narch/x86/mm/pgtable.c-763-{\n--\narch/x86/mm/pgtable.c-782- */\narch/x86/mm/pgtable.c:783:int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)\narch/x86/mm/pgtable.c-784-{\n--\ninclude/linux/pgtable.h=2176=int pud_free_pmd_page(pud_t *pud, unsigned long addr);\ninclude/linux/pgtable.h:2177:int pmd_free_pte_page(pmd_t *pmd, unsigned long addr);\ninclude/linux/pgtable.h-2178-#else\t/* !CONFIG_HAVE_ARCH_HUGE_VMAP */\n--\ninclude/linux/pgtable.h=2204=static inline int pud_free_pmd_page(pud_t *pud, unsigned long addr)\n--\ninclude/linux/pgtable.h-2207-}\ninclude/linux/pgtable.h:2208:static inline int pmd_free_pte_page(pmd_t *pmd, unsigned long addr)\ninclude/linux/pgtable.h-2209-{\n--\nmm/vmalloc.c=143=static int vmap_try_huge_pmd(pmd_t *pmd, unsigned long addr, unsigned long end,\n--\nmm/vmalloc.c-173-\tscoped_cond_guard(mmap_read_lock_try, return 0, \u0026init_mm) {\nmm/vmalloc.c:174:\t\tif (!pmd_free_pte_page(pmd, addr))\nmm/vmalloc.c-175-\t\t\treturn 0;\n"
} |
| 13/2 | 2026/07/11 06:38 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17550 output: 210 thoughts: 410 Thoughts: **My Assessment of the Patch**
Okay, so this patch is in `arch/arm64/mm/mmu.c`, specifically focusing on `pmd_free_pte_page`. It's a pretty straightforward change: they're effectively removing `__pmd_free_pte_page` and just renaming it. The other significant removal is the `static_branch_unlikely(&arm64_ptdump_lock_key)` stuff. Functionally, this is core kernel code, architecture specific, and it *is* reachable – that's a key detail for my assessment.
I should verify any config dependencies. Looks like `CONFIG_PTDUMP_DEBUGFS` is the primary one, and `CONFIG_DEBUG_WX` is another, though `CONFIG_HAVE_ARCH_HUGE_VMALLOC` is architecture-selected. So I just need to specify the former. No need to specify the latter.
Now, putting it all together for fuzzing, I'm thinking:
* **WorthFuzzing**: Absolutely. This hits the sweet spot for fuzzing consideration.
* **FocusSymbols**: I'll want to target these functions, because they're related to huge vmalloc mappings and page table manipulation:
* `vmap_try_huge_pmd`
* `vmap_try_huge_pud`
* `vmap_try_huge_p4d`
* `walk_page_range_debug`
* `pmd_free_pte_page`
* `pud_free_pmd_page`
* **EnableConfigs**:
* `PTDUMP_DEBUGFS`
* `DEBUG_WX`
My reasoning is this: the patch fundamentally changes the locking logic for freeing page tables, especially in the context of huge vmalloc mappings. They're replacing a specific static key for arm64 with a more generic lock. I also saw the addition of mmap write lock assertions in `walk_page_range_debug`. This sort of core kernel and architecture code, dealing with concurrency (vmalloc and ptdump specifically) demands rigorous fuzzing. I need to expose potential deadlocks or, even worse, use-after-free conditions that could arise from the changes in lock handling. I need to make sure this is robust, especially given the vmalloc context.
|
| 14/2 | 2026/07/11 06:38 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PTDUMP_DEBUGFS",
"DEBUG_WX"
],
"FocusSymbols": [
"vmap_try_huge_pmd",
"vmap_try_huge_pud",
"vmap_try_huge_p4d",
"walk_page_range_debug",
"pmd_free_pte_page",
"pud_free_pmd_page"
],
"Reasoning": "The patch modifies the locking logic for freeing page tables during huge vmalloc mappings, replacing an arm64-specific static key with a generic mmap_read_lock_try in core vmalloc code. It also adds an mmap write lock assertion in walk_page_range_debug. This is functional core kernel and architecture code that should be fuzzed to ensure no deadlocks or use-after-free issues occur during concurrent vmalloc and ptdump operations.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PTDUMP_DEBUGFS",
"DEBUG_WX"
],
"FocusSymbols": [
"vmap_try_huge_pmd",
"vmap_try_huge_pud",
"vmap_try_huge_p4d",
"walk_page_range_debug",
"pmd_free_pte_page",
"pud_free_pmd_page"
],
"Reasoning": "The patch modifies the locking logic for freeing page tables during huge vmalloc mappings, replacing an arm64-specific static key with a generic mmap_read_lock_try in core vmalloc code. It also adds an mmap write lock assertion in walk_page_range_debug. This is functional core kernel and architecture code that should be fuzzed to ensure no deadlocks or use-after-free issues occur during concurrent vmalloc and ptdump operations.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|