| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/15 05:19 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"CGROUPS",
"MEMCG",
"TRANSPARENT_HUGEPAGE",
"SWAP"
],
"FocusSymbols": [
"__mem_cgroup_large_folio_over_high",
"do_huge_pmd_anonymous_page",
"swap_cache_alloc_folio"
],
"KMSANReasoning": "The patch introduces a memory cgroup check (`mem_cgroup_large_folio_over_high`) to enforce `memory.high` limits by falling back from large folios to order-0 allocations when allocating non-blocking folios above the threshold.\n\nThe changes only alter allocation order fallback heuristics in `mm/huge_memory.c`, `mm/memory.c`, and `mm/swap_state.c`, and add a helper in `mm/memcontrol.c` that checks `current-\u003ememcg_nr_pages_over_high` and reads `page_counter` values. All local variables are initialized, no memory buffers or structures are copied to user space, no structure padding is exposed, and folio initialization semantics are unchanged (anonymous pages/folios continue to be zeroed before mapping). There are no uninitialized memory risks or kernel info-leak risks introduced. Any potential bugs (e.g., refcounting, NULL dereferences, or lockdep issues) are covered by standard sanitizers (KASAN/LOCKDEP). Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies core memory management paths for large folio allocation (THP and swap cache) under memcg memory.high enforcement. It introduces __mem_cgroup_large_folio_over_high() to prevent non-blocking large folio allocations from bypassing memory.high throttling, falling back to smaller orders in anonymous page faults and swap-in faults. These changes directly affect reachable core mm logic and warrant fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/15 05:19 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit c04c8551770467b5d20eb075c5a674a1c01f7ef7\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Sep 15 05:19:30 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h\nindex 46bf724cae7af..defef0d3d710a 100644\n--- a/include/linux/memcontrol.h\n+++ b/include/linux/memcontrol.h\n@@ -941,6 +941,33 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)\n \t\t__mem_cgroup_handle_over_high(gfp_mask);\n }\n \n+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp);\n+\n+/**\n+ * mem_cgroup_large_folio_over_high - would a large folio escape memory.high?\n+ * @mm: mm the folio would be charged against, may be NULL\n+ * @gfp: gfp mask the folio would be allocated and charged with\n+ *\n+ * See __mem_cgroup_large_folio_over_high().\n+ *\n+ * The task's over-high debt is more than a fast path here, it also scopes\n+ * the check to the case that is broken. The debt is settled and cleared on\n+ * every return to userspace, so a task faulting large folios from a\n+ * userspace loop always finds it zero and keeps getting them - memory.high\n+ * is enforced for it on the way out. Only a loop that stays in the kernel,\n+ * where nothing throttles at all, accrues debt and reaches the counters.\n+ *\n+ * Return: %true if the caller should fall back to a smaller order.\n+ */\n+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,\n+\t\t\t\t\t\t gfp_t gfp)\n+{\n+\tif (likely(!current-\u003ememcg_nr_pages_over_high))\n+\t\treturn false;\n+\n+\treturn __mem_cgroup_large_folio_over_high(mm, gfp);\n+}\n+\n unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg);\n \n void mem_cgroup_print_oom_context(struct mem_cgroup *memcg,\n@@ -1390,6 +1417,12 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)\n {\n }\n \n+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,\n+\t\t\t\t\t\t gfp_t gfp)\n+{\n+\treturn false;\n+}\n+\n static inline struct mem_cgroup *mem_cgroup_get_oom_group(\n \tstruct task_struct *victim, struct mem_cgroup *oom_domain)\n {\ndiff --git a/mm/huge_memory.c b/mm/huge_memory.c\nindex 7140a1031fb2e..059519465b244 100644\n--- a/mm/huge_memory.c\n+++ b/mm/huge_memory.c\n@@ -1355,6 +1355,18 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,\n \tconst int order = HPAGE_PMD_ORDER;\n \tstruct folio *folio;\n \n+\t/*\n+\t * A THP charged with a gfp that cannot block escapes the memory.high\n+\t * throttling in try_charge_memcg(). Don't hand out one while the\n+\t * cgroup is already above memory.high: the order-0 fallback is charged\n+\t * with a blocking gfp and throttles as documented.\n+\t */\n+\tif (mem_cgroup_large_folio_over_high(vma-\u003evm_mm, gfp)) {\n+\t\tcount_vm_event(THP_FAULT_FALLBACK);\n+\t\tcount_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);\n+\t\treturn NULL;\n+\t}\n+\n \tfolio = vma_alloc_folio(gfp, order, vma, addr \u0026 HPAGE_PMD_MASK);\n \n \tif (unlikely(!folio)) {\ndiff --git a/mm/memcontrol.c b/mm/memcontrol.c\nindex 1460cba53588e..dd7a62c3dacb2 100644\n--- a/mm/memcontrol.c\n+++ b/mm/memcontrol.c\n@@ -2587,6 +2587,62 @@ static unsigned long calculate_high_delay(unsigned int nr_pages,\n \treturn penalty_jiffies * nr_pages / MEMCG_CHARGE_BATCH;\n }\n \n+/**\n+ * __mem_cgroup_large_folio_over_high - would a large folio escape memory.high?\n+ * @mm: mm the folio would be charged against, may be NULL\n+ * @gfp: gfp mask the folio would be allocated and charged with\n+ *\n+ * memory.high is enforced on return to userspace, or synchronously in\n+ * try_charge_memcg() - but the synchronous path is gated on the charge gfp\n+ * allowing blocking. Large folios are charged with the THP allocation gfp,\n+ * which does not allow blocking unless the allocation policy asks for direct\n+ * compaction, so those charges escape throttling entirely: a fault loop that\n+ * does not return to userspace inbetween - the populate loop of mlock() or\n+ * MADV_POPULATE_*, any GUP-driven population - can grow usage from\n+ * memory.high all the way up to memory.max with no reclaim and no delay.\n+ *\n+ * Above memory.high the cgroup is supposed to be under reclaim pressure, so\n+ * refuse the large folio instead. Callers fall back to order-0, which is\n+ * charged with a blocking gfp and throttled as documented.\n+ *\n+ * This is a lockless snapshot of the counters; a stale result only costs one\n+ * large folio either way.\n+ *\n+ * Callers should use mem_cgroup_large_folio_over_high(), which keeps the\n+ * counter lookup off the fault path unless the task has actually charged\n+ * above memory.high before.\n+ *\n+ * Return: %true if the caller should fall back to a smaller order.\n+ */\n+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp)\n+{\n+\tstruct mem_cgroup *memcg, *iter;\n+\tbool over_high = false;\n+\n+\t/*\n+\t * A charge that can block is throttled by try_charge_memcg() itself,\n+\t * there is no reason to give up the large folio for it.\n+\t */\n+\tif (gfpflags_allow_blocking(gfp))\n+\t\treturn false;\n+\n+\tmemcg = get_mem_cgroup_from_mm(mm);\n+\tif (!memcg)\n+\t\treturn false;\n+\n+\tfor (iter = memcg; iter; iter = parent_mem_cgroup(iter)) {\n+\t\tif (page_counter_read(\u0026iter-\u003ememory) \u003e\n+\t\t READ_ONCE(iter-\u003ememory.high)) {\n+\t\t\tover_high = true;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tmem_cgroup_put(memcg);\n+\n+\treturn over_high;\n+}\n+\n /*\n * Reclaims memory over the high limit. Called directly from\n * try_charge() (context permitting), as well as from the userland\ndiff --git a/mm/memory.c b/mm/memory.c\nindex 926276d419202..b28fccc86216b 100644\n--- a/mm/memory.c\n+++ b/mm/memory.c\n@@ -5321,6 +5321,13 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)\n \n \t/* Try allocating the highest of the remaining orders. */\n \tgfp = vma_thp_gfp_mask(vma);\n+\n+\t/* Same reasoning as in vma_alloc_anon_folio_pmd(). */\n+\tif (mem_cgroup_large_folio_over_high(vma-\u003evm_mm, gfp)) {\n+\t\tcount_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);\n+\t\tgoto fallback;\n+\t}\n+\n \twhile (orders) {\n \t\taddr = ALIGN_DOWN(vmf-\u003eaddress, PAGE_SIZE \u003c\u003c order);\n \t\tfolio = vma_alloc_folio(gfp, order, vma, addr);\ndiff --git a/mm/swap_state.c b/mm/swap_state.c\nindex 625c185a1ca4d..a235c9aecaf18 100644\n--- a/mm/swap_state.c\n+++ b/mm/swap_state.c\n@@ -440,9 +440,23 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci,\n \t * Limit THP gfp. The limitation is a no-op for typical\n \t * GFP_HIGHUSER_MOVABLE but matters for shmem.\n \t */\n-\tif (order)\n+\tif (order) {\n \t\tgfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp);\n \n+\t\t/*\n+\t\t * This gfp may not allow blocking, in which case the charge\n+\t\t * below escapes the memory.high throttling in\n+\t\t * try_charge_memcg(). Above memory.high, have the caller\n+\t\t * retry with a smaller order, down to order-0, which is\n+\t\t * charged with the caller's blocking gfp and throttled as\n+\t\t * documented. Only fault context is throttled here; the\n+\t\t * readahead and zswap writeback callers have no @vmf and are\n+\t\t * not the ones to hold back.\n+\t\t */\n+\t\tif (vmf \u0026\u0026 mem_cgroup_large_folio_over_high(vma-\u003evm_mm, gfp))\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\t}\n+\n \tif (mpol || !vmf) {\n \t\tfolio = folio_alloc_mpol(gfp, order, mpol, ilx, numa_node_id());\n \t} else {\n"
} |
| 2/1 | 2026/09/15 05:19 | agent | patch-evaluator |
1mModel:core Args: null Results: {
"EnableConfigs": [
"CGROUPS",
"MEMCG",
"TRANSPARENT_HUGEPAGE",
"SWAP"
],
"FocusSymbols": [
"__mem_cgroup_large_folio_over_high",
"do_huge_pmd_anonymous_page",
"swap_cache_alloc_folio"
],
"Reasoning": "The patch modifies core memory management paths for large folio allocation (THP and swap cache) under memcg memory.high enforcement. It introduces __mem_cgroup_large_folio_over_high() to prevent non-blocking large folio allocations from bypassing memory.high throttling, falling back to smaller orders in anonymous page faults and swap-in faults. These changes directly affect reachable core mm logic and warrant 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 c04c8551770467b5d20eb075c5a674a1c01f7ef7
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Sep 15 05:19:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 46bf724cae7af..defef0d3d710a 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -941,6 +941,33 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)
__mem_cgroup_handle_over_high(gfp_mask);
}
+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp);
+
+/**
+ * mem_cgroup_large_folio_over_high - would a large folio escape memory.high?
+ * @mm: mm the folio would be charged against, may be NULL
+ * @gfp: gfp mask the folio would be allocated and charged with
+ *
+ * See __mem_cgroup_large_folio_over_high().
+ *
+ * The task's over-high debt is more than a fast path here, it also scopes
+ * the check to the case that is broken. The debt is settled and cleared on
+ * every return to userspace, so a task faulting large folios from a
+ * userspace loop always finds it zero and keeps getting them - memory.high
+ * is enforced for it on the way out. Only a loop that stays in the kernel,
+ * where nothing throttles at all, accrues debt and reaches the counters.
+ *
+ * Return: %true if the caller should fall back to a smaller order.
+ */
+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,
+ gfp_t gfp)
+{
+ if (likely(!current->memcg_nr_pages_over_high))
+ return false;
+
+ return __mem_cgroup_large_folio_over_high(mm, gfp);
+}
+
unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg);
void mem_cgroup_print_oom_context(struct mem_cgroup *memcg,
@@ -1390,6 +1417,12 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)
{
}
+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,
+ gfp_t gfp)
+{
+ return false;
+}
+
static inline struct mem_cgroup *mem_cgroup_get_oom_group(
struct task_struct *victim, struct mem_cgroup *oom_domain)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7140a1031fb2e..059519465b244 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1355,6 +1355,18 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,
const int order = HPAGE_PMD_ORDER;
struct folio *folio;
+ /*
+ * A THP charged with a gfp that cannot block escapes the memory.high
+ * throttling in try_charge_memcg(). Don't hand out one while the
+ * cgroup is already above memory.high: the order-0 fallback is charged
+ * with a blocking gfp and throttles as documented.
+ */
+ if (mem_cgroup_large_folio_over_high(vma->vm_mm, gfp)) {
+ count_vm_event(THP_FAULT_FALLBACK);
+ count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);
+ return NULL;
+ }
+
folio = vma_alloc_folio(gfp, order, vma, addr & HPAGE_PMD_MASK);
if (unlikely(!folio)) {
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 1460cba53588e..dd7a62c3dacb2 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2587,6 +2587,62 @@ static unsigned long calculate_high_delay(unsigned int nr_pages,
return penalty_jiffies * nr_pages / MEMCG_CHARGE_BATCH;
}
+/**
+ * __mem_cgroup_large_folio_over_high - would a large folio escape memory.high?
+ * @mm: mm the folio would be charged against, may be NULL
+ * @gfp: gfp mask the folio would be allocated and charged with
+ *
+ * memory.high is enforced on return to userspace, or synchronously in
+ * try_charge_memcg() - but the synchronous path is gated on the charge gfp
+ * allowing blocking. Large folios are charged with the THP allocation gfp,
+ * which does not allow blocking unless the allocation policy asks for direct
+ * compaction, so those charges escape throttling entirely: a fault loop that
+ * does not return to userspace inbetween - the populate loop of mlock() or
+ * MADV_POPULATE_*, any GUP-driven population - can grow usage from
+ * memory.high all the way up to memory.max with no reclaim and no delay.
+ *
+ * Above memory.high the cgroup is supposed to be under reclaim pressure, so
+ * refuse the large folio instead. Callers fall back to order-0, which is
+ * charged with a blocking gfp and throttled as documented.
+ *
+ * This is a lockless snapshot of the counters; a stale result only costs one
+ * large folio either way.
+ *
+ * Callers should use mem_cgroup_large_folio_over_high(), which keeps the
+ * counter lookup off the fault path unless the task has actually charged
+ * above memory.high before.
+ *
+ * Return: %true if the caller should fall back to a smaller order.
+ */
+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp)
+{
+ struct mem_cgroup *memcg, *iter;
+ bool over_high = false;
+
+ /*
+ * A charge that can block is throttled by try_charge_memcg() itself,
+ * there is no reason to give up the large folio for it.
+ */
+ if (gfpflags_allow_blocking(gfp))
+ return false;
+
+ memcg = get_mem_cgroup_from_mm(mm);
+ if (!memcg)
+ return false;
+
+ for (iter = memcg; iter; iter = parent_mem_cgroup(iter)) {
+ if (page_counter_read(&iter->memory) >
+ READ_ONCE(iter->memory.high)) {
+ over_high = true;
+ break;
+ }
+ }
+
+ mem_cgroup_put(memcg);
+
+ return over_high;
+}
+
/*
* Reclaims memory over the high limit. Called directly from
* try_charge() (context permitting), as well as from the userland
diff --git a/mm/memory.c b/mm/memory.c
index 926276d419202..b28fccc86216b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5321,6 +5321,13 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)
/* Try allocating the highest of the remaining orders. */
gfp = vma_thp_gfp_mask(vma);
+
+ /* Same reasoning as in vma_alloc_anon_folio_pmd(). */
+ if (mem_cgroup_large_folio_over_high(vma->vm_mm, gfp)) {
+ count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);
+ goto fallback;
+ }
+
while (orders) {
addr = ALIGN_DOWN(vmf->address, PAGE_SIZE << order);
folio = vma_alloc_folio(gfp, order, vma, addr);
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 625c185a1ca4d..a235c9aecaf18 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -440,9 +440,23 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci,
* Limit THP gfp. The limitation is a no-op for typical
* GFP_HIGHUSER_MOVABLE but matters for shmem.
*/
- if (order)
+ if (order) {
gfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp);
+ /*
+ * This gfp may not allow blocking, in which case the charge
+ * below escapes the memory.high throttling in
+ * try_charge_memcg(). Above memory.high, have the caller
+ * retry with a smaller order, down to order-0, which is
+ * charged with the caller's blocking gfp and throttled as
+ * documented. Only fault context is throttled here; the
+ * readahead and zswap writeback callers have no @vmf and are
+ * not the ones to hold back.
+ */
+ if (vmf && mem_cgroup_large_folio_over_high(vma->vm_mm, gfp))
+ return ERR_PTR(-ENOMEM);
+ }
+
if (mpol || !vmf) {
folio = folio_alloc_mpol(gfp, order, mpol, ilx, numa_node_id());
} else {
|
| 3/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 5252 output: 35 thoughts: 786 Thoughts: **Analyzing the Patch for Fuzzing Potential** Okay, let's break this down. First, I need to understand what this patch actually *does*. From the looks of it, it's introducing a check related to memory cgroups and large folios, specifically when a memory cgroup's `.high` limit is exceeded. The code snippets give a clear picture: * It introduces a new inline function `mem_cgroup_large_folio_over_high` that checks if the current memory cgroup is over the `.high` limit. When `CONFIG_MEMCG` is enabled, this is a real check, otherwise it's just stubbed. * That function gets called in multiple places within the memory management subsystem: `vma_alloc_anon_folio_pmd` (for THP/huge page allocation), `alloc_anon_folio` (for anonymous folio allocation), and `__swap_cache_alloc` (during swap operations). The calls all seem to be about checking if the `.high` limit is over, and if it is, it either falls back (in THP/folio allocation) or returns an error (in swap allocation). Now, let's evaluate this against the "WorthFuzzing" criteria. The criteria states that any changes in memory management are definitely in scope. I can see core mm code being touched ( `mm/memcontrol.c`, `mm/huge_memory.c`, `mm/memory.c`, `mm/swap_state.c` are all definitely core), and specifically, code related to large folio allocation, THP, and swap allocation *under* memory.high memcg limits. This is absolutely core mm. The patch introduces logic modifications in core memory management, specifically within allocation paths for large folios, THP, and swap cache allocation, which is directly related to the `.high` limit checks. This means it's 100% reachable executable code, therefore I'm immediately leaning towards `WorthFuzzing = true`. Next, I need to determine the `EnableConfigs`. It's clearly going to need `CONFIG_MEMCG` enabled, as that's the conditional for the function's real implementation. I'll need to confirm that `CONFIG_TRANSPARENT_HUGEPAGE` is needed, given the touch to `vma_alloc_anon_folio_pmd`, and `CONFIG_SWAP` would be required, given the touch to `__swap_cache_alloc`. I will need to use a grep or check the Kconfig files, or source code to determine the exact requirements of each configuration option. |
| 4/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "__mem_cgroup_large_folio_over_high",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memcontrol.c=2559=static unsigned long calculate_high_delay(unsigned int nr_pages,\n--\nmm/memcontrol.c-2590-/**\nmm/memcontrol.c:2591: * __mem_cgroup_large_folio_over_high - would a large folio escape memory.high?\nmm/memcontrol.c-2592- * @mm: mm the folio would be charged against, may be NULL\n--\nmm/memcontrol.c-2616- */\nmm/memcontrol.c:2617:bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp)\nmm/memcontrol.c-2618-{\n"
} |
| 5/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 6824 output: 25 thoughts: 20 Thoughts: Okay, let's dive in. **Checking the Build Configuration for `mm/memcontrol.c`** Alright, I need to verify something related to the build process for memory control. I'm going to quickly check the compilation guard for `mm/memcontrol.c` within the `mm/Makefile`. That's the first place I'd look to understand *why* or *if* a specific module is compiled. This will tell me if there's a conditional compilation flag or dependency that's affecting its inclusion in the kernel build. It's a fundamental step to diagnose any build-related issues with this file. I'm assuming there's something specific driving this check - so I'm hoping I'll quickly find the condition I'm looking for. |
| 6/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "memcontrol",
"PathPrefix": "mm/Makefile"
}Results: {
"Output": "mm/Makefile=27=KCOV_INSTRUMENT_kmemleak.o := n\nmm/Makefile:28:KCOV_INSTRUMENT_memcontrol.o := n\nmm/Makefile:29:KCOV_INSTRUMENT_memcontrol-v1.o := n\nmm/Makefile-30-KCOV_INSTRUMENT_mmzone.o := n\n--\nmm/Makefile=104=obj-$(CONFIG_LIVEUPDATE_MEMFD) += memfd_luo.o\nmm/Makefile:105:obj-$(CONFIG_MEMCG_V1) += memcontrol-v1.o\nmm/Makefile:106:obj-$(CONFIG_MEMCG) += memcontrol.o vmpressure.o\nmm/Makefile-107-ifdef CONFIG_BPF_SYSCALL\nmm/Makefile:108:obj-$(CONFIG_MEMCG) += bpf_memcontrol.o\nmm/Makefile-109-endif\n"
} |
| 7/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 7274 output: 32 thoughts: 41 Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue: **Investigating Memory Control Group Dependencies** Alright, so `mmcontrol.o` is built with `CONFIG_MEMCG` enabled. That immediately tells me we're dealing with memory control groups, likely in relation to resource limiting or accounting. Given that context, my next logical step is to delve into the code around large memory pages (hugepages) and swap management within the memory management subsystem. Specifically, I'm going to focus on `huge_memory.c` and `swap_state.c` referenced in `mm/Makefile`. These areas are crucial because memory control groups often need to interact intricately with hugepage allocation/deallocation and how swapped memory is handled with respect to different cgroups. I need to understand how the memory controller interacts with those to see if any specific functionality is conditional or related to the memory control group configuration, or how hugepages or swap behavior is different when memory control groups are in use. I'm looking for the integration points, the potential conflicts, and the design decisions related to how these components play together. |
| 8/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "(huge_memory|swap_state)",
"PathPrefix": "mm/Makefile"
}Results: {
"Output": "mm/Makefile=77=endif\nmm/Makefile-78-\nmm/Makefile:79:obj-$(CONFIG_SWAP)\t+= page_io.o swap_state.o swapfile.o\nmm/Makefile-80-obj-$(CONFIG_ZSWAP)\t+= zswap.o\n--\nmm/Makefile=101=obj-$(CONFIG_DEVICE_MIGRATION) += migrate_device.o\nmm/Makefile:102:obj-$(CONFIG_TRANSPARENT_HUGEPAGE) += huge_memory.o khugepaged.o\nmm/Makefile-103-obj-$(CONFIG_PAGE_COUNTER) += page_counter.o\n"
} |
| 9/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 7756 output: 19 thoughts: 155 Thoughts: **Kernel Configuration Dependency Review** Okay, so I'm looking at these kernel configurations and how they relate to the code. I see that `CONFIG_MEMCG` is definitely required, and I should have known that already, for the `__mem_cgroup_large_folio_over_high` function and anything related to memory.high logic, that makes sense. Also, `CONFIG_TRANSPARENT_HUGEPAGE` is essential for `mm/huge_memory.c`, specifically for `vma_alloc_anon_folio_pmd`. And of course, `CONFIG_SWAP` is needed for `swap_state.c`, particularly for `__swap_cache_alloc`. Now, I need to make sure I haven't missed anything. Are there any *other* dependencies, perhaps less obvious ones, for either `CONFIG_MEMCG` or `CONFIG_TRANSPARENT_HUGEPAGE`? Time to dig into `init/Kconfig` and `mm/Kconfig` and double-check those configuration options to make sure I'm not missing anything critical in the dependency chain. I need to be thorough. |
| 10/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "config MEMCG\\b"
}Results: {
"Output": "init/Kconfig=1068=config CGROUP_FAVOR_DYNMODS\n--\ninit/Kconfig-1077-\ninit/Kconfig:1078:config MEMCG\ninit/Kconfig-1079-\tbool \"Memory controller\"\n"
} |
| 11/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 8254 output: 33 thoughts: 23 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing `init/Kconfig` Section** Okay, let's zero in on lines 1075 to 1100 of `init/Kconfig`. I'm going to quickly parse this segment within the broader context of the initialization configuration. Given the file name, `Kconfig`, and the line numbers, I'm expecting this to be related to *some* aspect of the kernel's initial setup and potentially module loading or early system configuration, likely a configuration option or dependency. I'll be looking for keywords like "config", "bool", "tristate", "depends on", "select", and any module or driver names to glean the purpose and dependencies of these options. I'll need to assess the logical flow - the hierarchy, any dependencies, and potential interactions with other kernel components defined elsewhere in Kconfig and the source code. The specific section will determine how important it is. |
| 12/2 | 2026/09/15 05:19 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 1075,
"LineCount": 30
}Results: {
"Contents": "1075:\t\n1076:\t\t Say N if unsure.\n1077:\t\n1078:\tconfig MEMCG\n1079:\t\tbool \"Memory controller\"\n1080:\t\tselect PAGE_COUNTER\n1081:\t\tselect EVENTFD\n1082:\t\tselect SLAB_OBJ_EXT\n1083:\t\tselect VM_EVENT_COUNTERS\n1084:\t\thelp\n1085:\t\t Provides control over the memory footprint of tasks in a cgroup.\n1086:\t\n1087:\tconfig MEMCG_NMI_UNSAFE\n1088:\t\tbool\n1089:\t\tdepends on MEMCG\n1090:\t\tdepends on HAVE_NMI\n1091:\t\tdepends on !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS \u0026\u0026 !ARCH_HAVE_NMI_SAFE_CMPXCHG\n1092:\t\tdefault y\n1093:\t\n1094:\tconfig MEMCG_NMI_SAFETY_REQUIRES_ATOMIC\n1095:\t\tbool\n1096:\t\tdepends on MEMCG\n1097:\t\tdepends on HAVE_NMI\n1098:\t\tdepends on !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS \u0026\u0026 ARCH_HAVE_NMI_SAFE_CMPXCHG\n1099:\t\tdefault y\n1100:\t\n1101:\tconfig MEMCG_V1\n1102:\t\tbool \"Legacy cgroup v1 memory controller\"\n1103:\t\tdepends on MEMCG\n1104:\t\tdefault n\n"
} |
| 13/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 8930 output: 29 thoughts: 23 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into the `MEMCG` Dependency** Okay, so I need to figure out the dependencies for `MEMCG`. It’s probably a core component, so my initial thought is, it’s not really dependent on anything *else*. But just to be sure, I need to verify its position within the configuration menu. Let me fire up the kernel config, and navigate through `menuconfig`... specifically, I'll go digging within the `CGROUPS` section – that’s the most likely place to find it. Got to make sure there are no surprises in how it's enabled or interacts with other features. Let's see what the configuration reveals. |
| 14/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "menuconfig CGROUPS",
"PathPrefix": "init/Kconfig"
}Results: {
"Output": "init/Kconfig=1046=config SLAB_OBJ_EXT\n--\ninit/Kconfig-1048-\ninit/Kconfig:1049:menuconfig CGROUPS\ninit/Kconfig-1050-\tbool \"Control Group support\"\n"
} |
| 15/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 9218 output: 34 thoughts: 32 Thoughts: **CGROUPS and Transparent Hugepages: A Configuration Query** Okay, so I'm thinking about how this all fits together. Yes, CGROUPS is definitely the overarching structure, the container for all of this. But now, my immediate thought is to zero in on a specific kernel configuration option. Let's see if I can find the setting for `TRANSPARENT_HUGEPAGE`. I'll need to dig into `mm/Kconfig` to confirm its status and how it plays with this setup. I need to understand this configuration in relation to the CGROUPS context. |
| 16/2 | 2026/09/15 05:19 | tool | grepper |
0mArgs:{
"Expression": "config TRANSPARENT_HUGEPAGE\\b",
"PathPrefix": "mm/Kconfig"
}Results: {
"Output": "mm/Kconfig=831=config MM_ID\n--\nmm/Kconfig-833-\nmm/Kconfig:834:menuconfig TRANSPARENT_HUGEPAGE\nmm/Kconfig-835-\tbool \"Transparent Hugepage Support\"\n"
} |
| 17/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 9484 output: 32 thoughts: 20 Thoughts: Here's a summary of my thought process, as if I were the one thinking it: **Examining Kconfig Configuration for Memory Management** Alright, time to dive into the specifics of memory management configuration. I need to understand a particular aspect, so I'm going to start by looking directly at the relevant Kconfig file. The file I need is `mm/Kconfig`, and I'm interested in lines 834 through 850. That should give me the core definition and dependencies for whatever I'm investigating. Hopefully, this section holds the information I'm looking for. Let's see what the Kconfig code reveals about the configurations around this specific area. I'm expecting to find the key dependencies that control whatever it is in memory management that I'm currently working on. |
| 18/2 | 2026/09/15 05:19 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 834,
"LineCount": 25
}Results: {
"Contents": " 834:\tmenuconfig TRANSPARENT_HUGEPAGE\n 835:\t\tbool \"Transparent Hugepage Support\"\n 836:\t\tdepends on HAVE_ARCH_TRANSPARENT_HUGEPAGE \u0026\u0026 !PREEMPT_RT\n 837:\t\tselect COMPACTION\n 838:\t\tselect XARRAY_MULTI\n 839:\t\tselect MM_ID\n 840:\t\thelp\n 841:\t\t Transparent Hugepages allows the kernel to use huge pages and\n 842:\t\t huge tlb transparently to the applications whenever possible.\n 843:\t\t This feature can improve computing performance to certain\n 844:\t\t applications by speeding up page faults during memory\n 845:\t\t allocation, by reducing the number of tlb misses and by speeding\n 846:\t\t up the pagetable walking.\n 847:\t\n 848:\t\t If memory constrained on embedded, you may want to say N.\n 849:\t\n 850:\tif TRANSPARENT_HUGEPAGE\n 851:\t\n 852:\tchoice\n 853:\t\tprompt \"Transparent Hugepage Support sysfs defaults\"\n 854:\t\tdefault TRANSPARENT_HUGEPAGE_ALWAYS\n 855:\t\thelp\n 856:\t\t Selects the sysfs defaults for Transparent Hugepage Support.\n 857:\t\n 858:\t\tconfig TRANSPARENT_HUGEPAGE_ALWAYS\n"
} |
| 19/2 | 2026/09/15 05:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 10073 output: 33 thoughts: 178 Thoughts: **Analyzing the Key Symbols in this Memory Management Context** Okay, let's dive into these symbols. First up, I'm looking at `__mem_cgroup_large_folio_over_high`. Defined right there in `mm/memcontrol.c`. It's non-static, which is interesting, and it's the specific function added by this patch, so I'd say this is definitely a primary focus. This is where the core logic will likely reside, so I'm going to pay very close attention to it. Then, there's `vma_alloc_anon_folio_pmd` in `mm/huge_memory.c`. It's static, which limits its scope, so I should look for its callers. Let's see who's calling this one. I know this code pretty well, so I think this will yield important clues. |
| 20/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "vma_alloc_anon_folio_pmd",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/huge_memory.c=1349=EXPORT_SYMBOL_GPL(thp_get_unmapped_area);\nmm/huge_memory.c-1350-\nmm/huge_memory.c:1351:static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,\nmm/huge_memory.c-1352-\t\tunsigned long addr)\n--\nmm/huge_memory.c=1438=static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf)\n--\nmm/huge_memory.c-1445-\nmm/huge_memory.c:1446:\tfolio = vma_alloc_anon_folio_pmd(vma, vmf-\u003eaddress);\nmm/huge_memory.c-1447-\tif (unlikely(!folio))\n--\nmm/huge_memory.c=2150=static vm_fault_t do_huge_zero_wp_pmd(struct vm_fault *vmf)\n--\nmm/huge_memory.c-2157-\nmm/huge_memory.c:2158:\tfolio = vma_alloc_anon_folio_pmd(vma, vmf-\u003eaddress);\nmm/huge_memory.c-2159-\tif (unlikely(!folio))\n--\nmm/memory.c=5270=static struct folio *alloc_anon_folio(struct vm_fault *vmf)\n--\nmm/memory.c-5324-\nmm/memory.c:5325:\t/* Same reasoning as in vma_alloc_anon_folio_pmd(). */\nmm/memory.c-5326-\tif (mem_cgroup_large_folio_over_high(vma-\u003evm_mm, gfp)) {\n"
} |
| 21/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 10939 output: 45 thoughts: 32 Thoughts: Okay, here's my summary, speaking as an expert: **Tracing Huge Page Allocation Paths** Alright, let's dive into this. I need to understand the call paths that lead to the allocation and handling of huge pages, specifically the ones involving `__do_huge_pmd_anonymous_page` and `do_huge_zero_wp_pmd`. I'm going to look for the key functions that call these. I'm assuming these functions are central to the allocation and handling of anonymous huge pages. From there I'll be able to work backward and understand the larger picture of their use. I'm probably going to have to follow a bunch of `trace_printk` statements and call stack inspections to understand how the allocation is performed and when, and how, the zero page is written. |
| 22/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "(__do_huge_pmd_anonymous_page|do_huge_zero_wp_pmd)",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/huge_memory.c=1428=static void map_anon_folio_pmd_pf(struct folio *folio, pmd_t *pmd,\n--\nmm/huge_memory.c-1437-\nmm/huge_memory.c:1438:static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf)\nmm/huge_memory.c-1439-{\n--\nmm/huge_memory.c=1577=vm_fault_t do_huge_pmd_anonymous_page(struct vm_fault *vmf)\n--\nmm/huge_memory.c-1630-\nmm/huge_memory.c:1631:\treturn __do_huge_pmd_anonymous_page(vmf);\nmm/huge_memory.c-1632-}\n--\nmm/huge_memory.c=2140=bool huge_pmd_set_accessed(struct vm_fault *vmf)\n--\nmm/huge_memory.c-2149-\nmm/huge_memory.c:2150:static vm_fault_t do_huge_zero_wp_pmd(struct vm_fault *vmf)\nmm/huge_memory.c-2151-{\n--\nmm/huge_memory.c=2182=vm_fault_t do_huge_pmd_wp_page(struct vm_fault *vmf)\n--\nmm/huge_memory.c-2194-\tif (is_huge_zero_pmd(orig_pmd)) {\nmm/huge_memory.c:2195:\t\tvm_fault_t ret = do_huge_zero_wp_pmd(vmf);\nmm/huge_memory.c-2196-\n--\nmm/huge_memory.c=2536=static bool has_deposited_pgtable(struct vm_area_struct *vma, pmd_t pmdval,\n--\nmm/huge_memory.c-2551-\t * Otherwise, only anonymous folios are deposited, see\nmm/huge_memory.c:2552:\t * __do_huge_pmd_anonymous_page().\nmm/huge_memory.c-2553-\t */\n--\nmm/migrate_device.c=780=EXPORT_SYMBOL(migrate_vma_setup);\n--\nmm/migrate_device.c-788- * @page needs to be initialized and setup after it's allocated. The code bits\nmm/migrate_device.c:789: * here follow closely the code in __do_huge_pmd_anonymous_page(). This API does\nmm/migrate_device.c-790- * not support THP zero pages.\n"
} |
| 23/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 11868 output: 27 thoughts: 60 Thoughts: Okay, here's my take on those thoughts, framed as my own mental process: **Diving into Huge PMD Anonymous Page Faults** Right, so I'm looking at `do_huge_pmd_anonymous_page`. It's a function, not a static one, which tells me it's likely part of the broader huge page fault handling within the kernel's memory management. I already know it's in `mm/huge_memory.c`, so I'm guessing it's specific to handling faults for anonymous huge PMDs – big pages that aren't backed by any file. My next step? I need to understand how these huge PMD faults actually get an anonymous page allocated. The obvious place to investigate is the calls to `alloc_anon_folio`. I'll want to check the callers in `mm/memory.c`. That should give me the allocation path and insight into where these huge pages are being brought into existence on demand, right when a process tries to access them. This will probably involve understanding the nuances of the anon folio's allocation logic and how it interacts with the huge page infrastructure. |
| 24/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "alloc_anon_folio",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/huge_memory.c=1349=EXPORT_SYMBOL_GPL(thp_get_unmapped_area);\nmm/huge_memory.c-1350-\nmm/huge_memory.c:1351:static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,\nmm/huge_memory.c-1352-\t\tunsigned long addr)\n--\nmm/huge_memory.c=1438=static vm_fault_t __do_huge_pmd_anonymous_page(struct vm_fault *vmf)\n--\nmm/huge_memory.c-1445-\nmm/huge_memory.c:1446:\tfolio = vma_alloc_anon_folio_pmd(vma, vmf-\u003eaddress);\nmm/huge_memory.c-1447-\tif (unlikely(!folio))\n--\nmm/huge_memory.c=2150=static vm_fault_t do_huge_zero_wp_pmd(struct vm_fault *vmf)\n--\nmm/huge_memory.c-2157-\nmm/huge_memory.c:2158:\tfolio = vma_alloc_anon_folio_pmd(vma, vmf-\u003eaddress);\nmm/huge_memory.c-2159-\tif (unlikely(!folio))\n--\nmm/memory.c=5258=static bool pte_range_none(pte_t *pte, int nr_pages)\n--\nmm/memory.c-5269-\nmm/memory.c:5270:static struct folio *alloc_anon_folio(struct vm_fault *vmf)\nmm/memory.c-5271-{\n--\nmm/memory.c-5324-\nmm/memory.c:5325:\t/* Same reasoning as in vma_alloc_anon_folio_pmd(). */\nmm/memory.c-5326-\tif (mem_cgroup_large_folio_over_high(vma-\u003evm_mm, gfp)) {\n--\nmm/memory.c=5403=static vm_fault_t do_anonymous_page(struct vm_fault *vmf)\n--\nmm/memory.c-5457-\t/* Returns NULL on OOM or ERR_PTR(-EAGAIN) if we must retry the fault */\nmm/memory.c:5458:\tfolio = alloc_anon_folio(vmf);\nmm/memory.c-5459-\tif (IS_ERR(folio))\n"
} |
| 25/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 12836 output: 28 thoughts: 65 Thoughts: **Analyzing Anonymous Page Faults** Okay, so `do_anonymous_page` is the entry point for *every* anonymous page fault. It's the central hub, the most common case, so we need to look beyond that function. Focusing on `do_anonymous_page` is a waste of time at this point. I need to dig deeper. My hypothesis is that the problem isn't in the *handling* of the fault, but in how we're managing the swapped pages, so the next place to investigate is probably going to be a function dealing with allocation. Let's see if the code in `swap_state.c`, particularly the `__swap_cache_alloc` function, provides some insights into how anonymous memory is being handled when swapped out. I need to understand its role in this. What causes the swap cache to be used so frequently? Now, I need to see what functions actually *call* `__swap_cache_alloc`. The call graph of this function should reveal which other parts of the system are interacting with the swap cache allocation, and I should be able to get a better view of where the bottleneck is. |
| 26/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "__swap_cache_alloc",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/swap_state.c=369=void __swap_cache_replace_folio(struct swap_cluster_info *ci,\n--\nmm/swap_state.c-415- */\nmm/swap_state.c:416:static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci,\nmm/swap_state.c-417-\t\t\t\t\tswp_entry_t targ_entry, gfp_t gfp,\n--\nmm/swap_state.c=535=struct folio *swap_cache_alloc_folio(swp_entry_t targ_entry, gfp_t gfp,\n--\nmm/swap_state.c-550-\tdo {\nmm/swap_state.c:551:\t\tret = __swap_cache_alloc(ci, targ_entry, gfp, order,\nmm/swap_state.c-552-\t\t\t\t\t vmf, mpol, ilx);\n"
} |
| 27/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13412 output: 29 thoughts: 15 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Analyzing `swap_cache_alloc_folio` Callers** Okay, so I need to understand where `swap_cache_alloc_folio` is being called from. That function is pretty crucial to the swap implementation, so knowing its call paths is vital for debugging, optimization, or understanding a change in behavior. This likely means tracing the execution flow from the higher level functions that interact with the swap cache. My initial thought is to probably start with `swap_readpage`, `swap_writepage`, and the reclaim path. Those are the most obvious candidates. I'll need to look at how these functions, and anything they invoke, interact with the swap cache code. Are there specific folio flags, or page states, that are being passed in which would lead to the invocation of `swap_cache_alloc_folio`? What about error handling – is this call path triggered by a memory allocation failure in another part of the system? I'll also want to consider the context - am I concerned about a particular issue such as performance bottlenecks, memory leaks, or a crash? Knowing this, I can narrow down the search to relevant call paths. For instance, if this is about performance, I'll be particularly interested in the frequently executed paths. If I am looking for a memory leak I want to look at where the folio is *released* after allocation and verify that every allocation has a corresponding free. I'll probably use a tool to traverse the callgraph or just grep for calls to this function throughout the kernel source. I expect it to get quite involved, given that it's a core swap function, so I'll need to keep track of the different caller contexts and what they are trying to achieve. I'll also check for any code paths that may indirectly call the function through some intermediate functions. My general aim will be to map out the entire call chain, from the highest-level VFS functions down to the specific folio allocation. That will give me the most complete picture. |
| 28/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "swap_cache_alloc_folio",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/swap.h=314=void swap_cache_del_folio(struct folio *folio);\nmm/swap.h:315:struct folio *swap_cache_alloc_folio(swp_entry_t target_entry, gfp_t gfp_mask,\nmm/swap.h-316-\t\t\t\t unsigned long orders, struct vm_fault *vmf,\n--\nmm/swap_state.c=416=static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci,\n--\nmm/swap_state.c-518-/**\nmm/swap_state.c:519: * swap_cache_alloc_folio - Allocate folio for swapped out slot in swap cache.\nmm/swap_state.c-520- * @targ_entry: swap entry indicating the target slot\n--\nmm/swap_state.c-534- */\nmm/swap_state.c:535:struct folio *swap_cache_alloc_folio(swp_entry_t targ_entry, gfp_t gfp,\nmm/swap_state.c-536-\t\t\t\t unsigned long orders, struct vm_fault *vmf,\n--\nmm/swap_state.c=663=static struct folio *swap_cache_read_folio(struct swap_io_ctx *ctx,\n--\nmm/swap_state.c-672-\t\t\treturn folio;\nmm/swap_state.c:673:\t\tfolio = swap_cache_alloc_folio(entry, gfp, BIT(0), NULL, mpol, ilx);\nmm/swap_state.c-674-\t} while (PTR_ERR(folio) == -EEXIST);\n--\nmm/swap_state.c=704=struct folio *swapin_sync(swp_entry_t entry, gfp_t gfp, unsigned long orders,\n--\nmm/swap_state.c-713-\t\t\treturn folio;\nmm/swap_state.c:714:\t\tfolio = swap_cache_alloc_folio(entry, gfp, orders, vmf, mpol, ilx);\nmm/swap_state.c-715-\t} while (PTR_ERR(folio) == -EEXIST);\n--\nmm/swapfile.c=1844=void folio_put_swap(struct folio *folio, struct page *page)\n--\nmm/swapfile.c-1889- * ...\t\t\t\tswapoff+swapon\nmm/swapfile.c:1890: * swap_cache_alloc_folio()\nmm/swapfile.c-1891- * // check swap_map\n--\nmm/zswap.c=1005=static int zswap_writeback_entry(struct zswap_entry *entry,\n--\nmm/zswap.c-1021-\tmpol = get_task_policy(current);\nmm/zswap.c:1022:\tfolio = swap_cache_alloc_folio(swpentry, GFP_KERNEL, BIT(0), NULL, mpol,\nmm/zswap.c-1023-\t\t\t\t NO_INTERLEAVE_INDEX);\n"
} |
| 29/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 14663 output: 26 thoughts: 73 Thoughts: Here's my summary, as though I'm thinking through this process: **Diving into Swap Cache Allocation** Okay, so I'm looking at how the kernel handles swap cache allocation during page faults. `swapin_sync` and `swap_cache_alloc_folio` are the key entry points, specifically for fault handling. Interesting that `swap_cache_alloc_folio` isn't static; it's exported internally via `mm/swap.h`. That suggests it's designed for broader use within the memory management subsystem, not just strictly limited to the file where it's defined. My next move is clear: I need to trace the callers. I want to understand the *usage* patterns of these functions. Time to dig through the code and figure out what exactly is calling `swapin_sync` and `swap_cache_alloc_folio`. Knowing the call sites will give me crucial context. |
| 30/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "swapin_sync",
"PathPrefix": "mm/"
}Results: {
"Output": "mm/memory.c=4844=vm_fault_t do_swap_page(struct vm_fault *vmf)\n--\nmm/memory.c-4935-\t\tif (data_race(si-\u003eflags \u0026 SWP_SYNCHRONOUS_IO))\nmm/memory.c:4936:\t\t\tfolio = swapin_sync(entry, GFP_HIGHUSER_MOVABLE,\nmm/memory.c-4937-\t\t\t\t\t thp_swapin_suitable_orders(vmf) | BIT(0),\n--\nmm/shmem.c=2248=static struct folio *shmem_swap_alloc_folio(struct inode *inode,\n--\nmm/shmem.c-2262-\tmpol = shmem_get_pgoff_policy(info, index, order, \u0026ilx);\nmm/shmem.c:2263:\tfolio = swapin_sync(entry, gfp, BIT(order), vmf, mpol, ilx);\nmm/shmem.c-2264-\tmpol_cond_put(mpol);\n--\nmm/swap.h=332=struct folio *swapin_readahead(swp_entry_t entry, gfp_t flag,\nmm/swap.h-333-\t\tstruct vm_fault *vmf);\nmm/swap.h:334:struct folio *swapin_sync(swp_entry_t entry, gfp_t flag, unsigned long orders,\nmm/swap.h-335-\t\t\t struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx);\n--\nmm/swap.h=408=static inline struct folio *swapin_readahead(swp_entry_t swp, gfp_t gfp_mask,\n--\nmm/swap.h-413-\nmm/swap.h:414:static inline struct folio *swapin_sync(\nmm/swap.h-415-\tswp_entry_t entry, gfp_t flag, unsigned long orders,\n--\nmm/swap_state.c=663=static struct folio *swap_cache_read_folio(struct swap_io_ctx *ctx,\n--\nmm/swap_state.c-688-/**\nmm/swap_state.c:689: * swapin_sync - swap-in one or multiple entries skipping readahead.\nmm/swap_state.c-690- * @entry: swap entry indicating the target slot\n--\nmm/swap_state.c-703- */\nmm/swap_state.c:704:struct folio *swapin_sync(swp_entry_t entry, gfp_t gfp, unsigned long orders,\nmm/swap_state.c-705-\t\t\t struct vm_fault *vmf, struct mempolicy *mpol, pgoff_t ilx)\n"
} |
| 31/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15668 output: 33 thoughts: 1341 Thoughts: **Analyzing `__mem_cgroup_large_folio_over_high` and its Callers** Okay, let's break this down. My initial thought is to zero in on `__mem_cgroup_large_folio_over_high` function defined in `mm/memcontrol.c`. I've confirmed it's a non-static symbol, meaning it's explicitly exported in the vmlinux. Now, my goal is to understand its purpose and identify its direct and indirect callers to evaluate the scope of its impact. The code itself, with its check for blocking allocations, makes sense. It's designed to prevent a large folio allocation in a memory cgroup if the memory usage is already over the "high" watermark, unless a blocking allocation is being attempted. The loop through parent cgroups is the standard approach for this kind of hierarchical memory management. Next, I look for places that touch or call this function. From initial inspection, I see `do_huge_pmd_anonymous_page` and `swap_cache_alloc_folio` immediately stand out. `mem_cgroup_large_folio_over_high` is likely used in THP anonymous faults (`do_huge_pmd_anonymous_page`) and when allocating folios in the swap cache (`swap_cache_alloc_folio`). I did a deeper dive and confirmed my initial findings were correct. Now I'm checking the broader context and potential indirect callers. `do_huge_pmd_anonymous_page` is THP-specific, which means its impact isn't as widespread as functions like `do_anonymous_page`. `swap_cache_alloc_folio` deals with swap cache allocation, again a more targeted area. A review of the commit diffs helps me to get a better sense of how all the pieces fit together: `vma_alloc_anon_folio_pmd` is the one that calls `do_huge_pmd_anonymous_page`. `alloc_anon_folio` calls `do_anonymous_page`. `__swap_cache_alloc` (called by `swap_cache_alloc_folio`) is used in swap cache operations. Given this, I'm confident my "FocusSymbols" are: * `__mem_cgroup_large_folio_over_high` * `do_huge_pmd_anonymous_page` * `swap_cache_alloc_folio` Now to verify config requirements, since this is all dependent on certain kernel configurations. `__mem_cgroup_large_folio_over_high` requires `CONFIG_MEMCG=y`. `vma_alloc_anon_folio_pmd` needs `CONFIG_TRANSPARENT_HUGEPAGE=y`. And `__swap_cache_alloc` requires `CONFIG_SWAP=y`. I'll want to ensure these are enabled in the build environment to ensure everything functions as intended. Finally, I'll check that `MEMCG` doesn't have any hidden dependencies on other configs. I'll look at the `init/Kconfig` file to confirm that. |
| 32/2 | 2026/09/15 05:20 | tool | read-file |
0mArgs:{
"File": "init/Kconfig",
"FirstLine": 1049,
"LineCount": 35
}Results: {
"Contents": "1049:\tmenuconfig CGROUPS\n1050:\t\tbool \"Control Group support\"\n1051:\t\tselect KERNFS\n1052:\t\thelp\n1053:\t\t This option adds support for grouping sets of processes together, for\n1054:\t\t use with process control subsystems such as Cpusets, CFS, memory\n1055:\t\t controls or device isolation.\n1056:\t\t See\n1057:\t\t\t- Documentation/scheduler/sched-design-CFS.rst\t(CFS)\n1058:\t\t\t- Documentation/admin-guide/cgroup-v1/ (features for grouping, isolation\n1059:\t\t\t\t\t\t and resource control)\n1060:\t\n1061:\t\t Say N if unsure.\n1062:\t\n1063:\tif CGROUPS\n1064:\t\n1065:\tconfig PAGE_COUNTER\n1066:\t\tbool\n1067:\t\n1068:\tconfig CGROUP_FAVOR_DYNMODS\n1069:\t\tbool \"Favor dynamic modification latency reduction by default\"\n1070:\t\thelp\n1071:\t\t This option enables the \"favordynmods\" mount option by default\n1072:\t\t which reduces the latencies of dynamic cgroup modifications such\n1073:\t\t as task migrations and controller on/offs at the cost of making\n1074:\t\t hot path operations such as forks and exits more expensive.\n1075:\t\n1076:\t\t Say N if unsure.\n1077:\t\n1078:\tconfig MEMCG\n1079:\t\tbool \"Memory controller\"\n1080:\t\tselect PAGE_COUNTER\n1081:\t\tselect EVENTFD\n1082:\t\tselect SLAB_OBJ_EXT\n1083:\t\tselect VM_EVENT_COUNTERS\n"
} |
| 33/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18192 output: 29 thoughts: 54 Thoughts: **My System Configuration Analysis** Okay, so I'm looking at how this system is put together, and my initial thought is that if `CGROUPS` is enabled, then it's wrapping up `config MEMCG`. This means `CGROUPS` and `MEMCG` are tightly coupled, which isn't surprising. I also see that `TRANSPARENT_HUGEPAGE` is in the mix here. And, of course, `SWAP` – can't forget about swap space. Now, I need to understand the dependencies a bit better. My next step is to drill down and specifically check if `SWAP` itself has any dependencies. I want to see if it relies on any of the other configurations, or if it can live independently. Gotta get the architecture mapped out completely. |
| 34/2 | 2026/09/15 05:20 | tool | grepper |
0mArgs:{
"Expression": "config SWAP\\b",
"PathPrefix": "mm/Kconfig"
}Results: {
"Output": "mm/Kconfig=9=config ARCH_NO_SWAP\n--\nmm/Kconfig-11-\nmm/Kconfig:12:menuconfig SWAP\nmm/Kconfig-13-\tbool \"Support for paging of anonymous memory (swap)\"\n"
} |
| 35/2 | 2026/09/15 05:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18518 output: 31 thoughts: 17 Thoughts: Here's my thought process, as though I were examining that question: **Analyzing SWAP's Kconfig Dependencies** Okay, so I need to figure out what `SWAP` in `mm/Kconfig` depends on. That's the key to understanding its configuration. I'm going to jump straight in and check the Kconfig file. I'll need to figure out what other configuration options it relies on to be enabled. I need to understand its relationship to other memory management features. This should give me a solid grasp of when and why `SWAP` gets included or excluded in the kernel build. Let's see what the dependencies are. |
| 36/2 | 2026/09/15 05:21 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig",
"FirstLine": 11,
"LineCount": 10
}Results: {
"Contents": " 11:\t\n 12:\tmenuconfig SWAP\n 13:\t\tbool \"Support for paging of anonymous memory (swap)\"\n 14:\t\tdepends on MMU \u0026\u0026 BLOCK \u0026\u0026 !ARCH_NO_SWAP\n 15:\t\tdefault y\n 16:\t\thelp\n 17:\t\t This option allows you to choose whether you want to have support\n 18:\t\t for so called swap devices or swap files in your kernel that are\n 19:\t\t used to provide more virtual memory than the actual RAM present\n 20:\t\t in your computer. If unsure say Y.\n"
} |
| 37/2 | 2026/09/15 05:21 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18860 output: 180 thoughts: 174 Thoughts: **My Assessment of the Proposed Patch** Okay, so the usual suspects, `MMU` and `BLOCK`, are present. Standard. Let's dig into the reasoning behind this patch. I see that `__mem_cgroup_large_folio_over_high()` is being introduced. The goal here is pretty clear: to enforce `memory.high` limits within a memcg when dealing with large folio allocations. This means we're talking about THPs and the swap cache specifically, and we're looking at scenarios with non-blocking GFP masks. Essentially, this function will influence large folio allocation decisions when handling things like anonymous PMD faults, multi-size THP faults, and swap-cache allocations. If `memory.high` is breached, the fallback strategy will be to use smaller orders, or even order-0 allocations. This directly impacts core memory management code, and I see it's touching several key areas: `mm/memcontrol.c`, `mm/huge_memory.c`, `mm/memory.c`, and `mm/swap_state.c`. That's reasonable, it hits the relevant pieces. Alright, after this quick review, everything looks consistent and in order. I'm ready to call `set-results`. It's a clean implementation of an important policy. |
| 38/2 | 2026/09/15 05:21 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"CGROUPS",
"MEMCG",
"TRANSPARENT_HUGEPAGE",
"SWAP"
],
"FocusSymbols": [
"__mem_cgroup_large_folio_over_high",
"do_huge_pmd_anonymous_page",
"swap_cache_alloc_folio"
],
"Reasoning": "The patch modifies core memory management paths for large folio allocation (THP and swap cache) under memcg memory.high enforcement. It introduces __mem_cgroup_large_folio_over_high() to prevent non-blocking large folio allocations from bypassing memory.high throttling, falling back to smaller orders in anonymous page faults and swap-in faults. These changes directly affect reachable core mm logic and warrant fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"CGROUPS",
"MEMCG",
"TRANSPARENT_HUGEPAGE",
"SWAP"
],
"FocusSymbols": [
"__mem_cgroup_large_folio_over_high",
"do_huge_pmd_anonymous_page",
"swap_cache_alloc_folio"
],
"Reasoning": "The patch modifies core memory management paths for large folio allocation (THP and swap cache) under memcg memory.high enforcement. It introduces __mem_cgroup_large_folio_over_high() to prevent non-blocking large folio allocations from bypassing memory.high throttling, falling back to smaller orders in anonymous page faults and swap-in faults. These changes directly affect reachable core mm logic and warrant fuzzing.",
"WorthFuzzing": true
} |
| 39/1 | 2026/09/15 05:21 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 40/2 | 2026/09/15 05:21 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch introduces a memory cgroup check (`mem_cgroup_large_folio_over_high`) to enforce `memory.high` limits by falling back from large folios to order-0 allocations when allocating non-blocking folios above the threshold.\n\nThe changes only alter allocation order fallback heuristics in `mm/huge_memory.c`, `mm/memory.c`, and `mm/swap_state.c`, and add a helper in `mm/memcontrol.c` that checks `current-\u003ememcg_nr_pages_over_high` and reads `page_counter` values. All local variables are initialized, no memory buffers or structures are copied to user space, no structure padding is exposed, and folio initialization semantics are unchanged (anonymous pages/folios continue to be zeroed before mapping). There are no uninitialized memory risks or kernel info-leak risks introduced. Any potential bugs (e.g., refcounting, NULL dereferences, or lockdep issues) are covered by standard sanitizers (KASAN/LOCKDEP). Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"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 c04c8551770467b5d20eb075c5a674a1c01f7ef7
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Sep 15 05:19:30 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 46bf724cae7af..defef0d3d710a 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -941,6 +941,33 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)
__mem_cgroup_handle_over_high(gfp_mask);
}
+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp);
+
+/**
+ * mem_cgroup_large_folio_over_high - would a large folio escape memory.high?
+ * @mm: mm the folio would be charged against, may be NULL
+ * @gfp: gfp mask the folio would be allocated and charged with
+ *
+ * See __mem_cgroup_large_folio_over_high().
+ *
+ * The task's over-high debt is more than a fast path here, it also scopes
+ * the check to the case that is broken. The debt is settled and cleared on
+ * every return to userspace, so a task faulting large folios from a
+ * userspace loop always finds it zero and keeps getting them - memory.high
+ * is enforced for it on the way out. Only a loop that stays in the kernel,
+ * where nothing throttles at all, accrues debt and reaches the counters.
+ *
+ * Return: %true if the caller should fall back to a smaller order.
+ */
+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,
+ gfp_t gfp)
+{
+ if (likely(!current->memcg_nr_pages_over_high))
+ return false;
+
+ return __mem_cgroup_large_folio_over_high(mm, gfp);
+}
+
unsigned long mem_cgroup_get_max(struct mem_cgroup *memcg);
void mem_cgroup_print_oom_context(struct mem_cgroup *memcg,
@@ -1390,6 +1417,12 @@ static inline void mem_cgroup_handle_over_high(gfp_t gfp_mask)
{
}
+static inline bool mem_cgroup_large_folio_over_high(struct mm_struct *mm,
+ gfp_t gfp)
+{
+ return false;
+}
+
static inline struct mem_cgroup *mem_cgroup_get_oom_group(
struct task_struct *victim, struct mem_cgroup *oom_domain)
{
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 7140a1031fb2e..059519465b244 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1355,6 +1355,18 @@ static struct folio *vma_alloc_anon_folio_pmd(struct vm_area_struct *vma,
const int order = HPAGE_PMD_ORDER;
struct folio *folio;
+ /*
+ * A THP charged with a gfp that cannot block escapes the memory.high
+ * throttling in try_charge_memcg(). Don't hand out one while the
+ * cgroup is already above memory.high: the order-0 fallback is charged
+ * with a blocking gfp and throttles as documented.
+ */
+ if (mem_cgroup_large_folio_over_high(vma->vm_mm, gfp)) {
+ count_vm_event(THP_FAULT_FALLBACK);
+ count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);
+ return NULL;
+ }
+
folio = vma_alloc_folio(gfp, order, vma, addr & HPAGE_PMD_MASK);
if (unlikely(!folio)) {
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 1460cba53588e..dd7a62c3dacb2 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -2587,6 +2587,62 @@ static unsigned long calculate_high_delay(unsigned int nr_pages,
return penalty_jiffies * nr_pages / MEMCG_CHARGE_BATCH;
}
+/**
+ * __mem_cgroup_large_folio_over_high - would a large folio escape memory.high?
+ * @mm: mm the folio would be charged against, may be NULL
+ * @gfp: gfp mask the folio would be allocated and charged with
+ *
+ * memory.high is enforced on return to userspace, or synchronously in
+ * try_charge_memcg() - but the synchronous path is gated on the charge gfp
+ * allowing blocking. Large folios are charged with the THP allocation gfp,
+ * which does not allow blocking unless the allocation policy asks for direct
+ * compaction, so those charges escape throttling entirely: a fault loop that
+ * does not return to userspace inbetween - the populate loop of mlock() or
+ * MADV_POPULATE_*, any GUP-driven population - can grow usage from
+ * memory.high all the way up to memory.max with no reclaim and no delay.
+ *
+ * Above memory.high the cgroup is supposed to be under reclaim pressure, so
+ * refuse the large folio instead. Callers fall back to order-0, which is
+ * charged with a blocking gfp and throttled as documented.
+ *
+ * This is a lockless snapshot of the counters; a stale result only costs one
+ * large folio either way.
+ *
+ * Callers should use mem_cgroup_large_folio_over_high(), which keeps the
+ * counter lookup off the fault path unless the task has actually charged
+ * above memory.high before.
+ *
+ * Return: %true if the caller should fall back to a smaller order.
+ */
+bool __mem_cgroup_large_folio_over_high(struct mm_struct *mm, gfp_t gfp)
+{
+ struct mem_cgroup *memcg, *iter;
+ bool over_high = false;
+
+ /*
+ * A charge that can block is throttled by try_charge_memcg() itself,
+ * there is no reason to give up the large folio for it.
+ */
+ if (gfpflags_allow_blocking(gfp))
+ return false;
+
+ memcg = get_mem_cgroup_from_mm(mm);
+ if (!memcg)
+ return false;
+
+ for (iter = memcg; iter; iter = parent_mem_cgroup(iter)) {
+ if (page_counter_read(&iter->memory) >
+ READ_ONCE(iter->memory.high)) {
+ over_high = true;
+ break;
+ }
+ }
+
+ mem_cgroup_put(memcg);
+
+ return over_high;
+}
+
/*
* Reclaims memory over the high limit. Called directly from
* try_charge() (context permitting), as well as from the userland
diff --git a/mm/memory.c b/mm/memory.c
index 926276d419202..b28fccc86216b 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -5321,6 +5321,13 @@ static struct folio *alloc_anon_folio(struct vm_fault *vmf)
/* Try allocating the highest of the remaining orders. */
gfp = vma_thp_gfp_mask(vma);
+
+ /* Same reasoning as in vma_alloc_anon_folio_pmd(). */
+ if (mem_cgroup_large_folio_over_high(vma->vm_mm, gfp)) {
+ count_mthp_stat(order, MTHP_STAT_ANON_FAULT_FALLBACK);
+ goto fallback;
+ }
+
while (orders) {
addr = ALIGN_DOWN(vmf->address, PAGE_SIZE << order);
folio = vma_alloc_folio(gfp, order, vma, addr);
diff --git a/mm/swap_state.c b/mm/swap_state.c
index 625c185a1ca4d..a235c9aecaf18 100644
--- a/mm/swap_state.c
+++ b/mm/swap_state.c
@@ -440,9 +440,23 @@ static struct folio *__swap_cache_alloc(struct swap_cluster_info *ci,
* Limit THP gfp. The limitation is a no-op for typical
* GFP_HIGHUSER_MOVABLE but matters for shmem.
*/
- if (order)
+ if (order) {
gfp = thp_shmem_limit_gfp_mask(vma_thp_gfp_mask(vma), gfp);
+ /*
+ * This gfp may not allow blocking, in which case the charge
+ * below escapes the memory.high throttling in
+ * try_charge_memcg(). Above memory.high, have the caller
+ * retry with a smaller order, down to order-0, which is
+ * charged with the caller's blocking gfp and throttled as
+ * documented. Only fault context is throttled here; the
+ * readahead and zswap writeback callers have no @vmf and are
+ * not the ones to hold back.
+ */
+ if (vmf && mem_cgroup_large_folio_over_high(vma->vm_mm, gfp))
+ return ERR_PTR(-ENOMEM);
+ }
+
if (mpol || !vmf) {
folio = folio_alloc_mpol(gfp, order, mpol, ilx, numa_node_id());
} else {
|
| 41/3 | 2026/09/15 05:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 4464 output: 257 thoughts: 1777 Thoughts: **KMSAN Evaluation: Patch Series for memcg `memory.high` Throttling** Okay, let's break this down. The objective is to determine if this patch series, which modifies how the kernel handles `memory.high` throttling for large folios (THP/mTHP) within memory cgroups, warrants a dedicated KMSAN fuzzing session, or if KASAN is sufficient. First, I examined the code changes. The patch adds checks using `mem_cgroup_large_folio_over_high` in several memory allocation paths, including `huge_memory.c`, `memory.c`, and `swap_state.c`. Essentially, if a memcg is above its `memory.high` limit and the allocation can't block, the large folio allocation falls back to order-0, using blocking GFP, allowing for proper throttling. Next, I methodically went through the crucial areas where KMSAN would be relevant. I checked for potential issues like kernel structures being sent to userspace with uninitialized fields, conditional logic relying on uninitialized variables, the allocation/initialization of complex data structures where uninitialized fields could be read later, and the possibility of bounds checks or logic accessing uninitialized bytes. None of these vulnerabilities are present. I then scrutinized the `__mem_cgroup_large_folio_over_high` function and the various call sites to ensure that all variables were properly initialized and that no uninitialized memory was being read. `current->memcg_nr_pages_over_high`, `iter->memory`, `iter->memory.high`, `vma->vm_mm`, `gfp` are all accounted for and checked. The order-0 folio fallback doesn't introduce any new uninitialized page issues as those folios are always zeroed before being mapped. I confirmed there are no info leaks or uninitialized memory reads. Considering KMSAN's purpose, which is to detect uninitialized memory reads, and given that the changes don't introduce any risk of uninitialized memory usage or info-leaks, KMSAN isn't needed here. All existing issues are covered by KASAN. This patch focuses on the correct fallback path in specific cases. Therefore, after a thorough review of the code and the nature of the modifications, it's clear: `NeedsKMSAN = false`. This patch series is safe from uninitialized memory reads and KMSAN fuzzing is not necessary. |
| 42/3 | 2026/09/15 05:21 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces a memory cgroup check (`mem_cgroup_large_folio_over_high`) to enforce `memory.high` limits by falling back from large folios to order-0 allocations when allocating non-blocking folios above the threshold.\n\nThe changes only alter allocation order fallback heuristics in `mm/huge_memory.c`, `mm/memory.c`, and `mm/swap_state.c`, and add a helper in `mm/memcontrol.c` that checks `current-\u003ememcg_nr_pages_over_high` and reads `page_counter` values. All local variables are initialized, no memory buffers or structures are copied to user space, no structure padding is exposed, and folio initialization semantics are unchanged (anonymous pages/folios continue to be zeroed before mapping). There are no uninitialized memory risks or kernel info-leak risks introduced. Any potential bugs (e.g., refcounting, NULL dereferences, or lockdep issues) are covered by standard sanitizers (KASAN/LOCKDEP). Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces a memory cgroup check (`mem_cgroup_large_folio_over_high`) to enforce `memory.high` limits by falling back from large folios to order-0 allocations when allocating non-blocking folios above the threshold.\n\nThe changes only alter allocation order fallback heuristics in `mm/huge_memory.c`, `mm/memory.c`, and `mm/swap_state.c`, and add a helper in `mm/memcontrol.c` that checks `current-\u003ememcg_nr_pages_over_high` and reads `page_counter` values. All local variables are initialized, no memory buffers or structures are copied to user space, no structure padding is exposed, and folio initialization semantics are unchanged (anonymous pages/folios continue to be zeroed before mapping). There are no uninitialized memory risks or kernel info-leak risks introduced. Any potential bugs (e.g., refcounting, NULL dereferences, or lockdep issues) are covered by standard sanitizers (KASAN/LOCKDEP). Therefore, a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|