| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/24 12:16 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"PAGEALLOC_HOGGER"
],
"FocusSymbols": [
"req_page_alloc_write",
"free_page_alloc_write",
"make_alloc",
"free_alloc_helper"
],
"Reasoning": "The patch introduces a new debugfs interface for allocating pages from specific nodes, zones, migrate types, and orders. This is a functional testing/debugging tool that can be used to generate memory pressure and test the core page allocator and memory management subsystem. Fuzzing it is valuable to uncover potential bugs in memory management or OOM handling.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/24 12:16 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 8ab90b56f71bb1d10727757b8f69c491ec8576a0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Jul 24 12:16:19 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/mm/Kconfig.debug b/mm/Kconfig.debug\nindex 7638d75b27db8c..c8fcecdca6aca9 100644\n--- a/mm/Kconfig.debug\n+++ b/mm/Kconfig.debug\n@@ -309,3 +309,15 @@ config PER_VMA_LOCK_STATS\n \t overhead in the page fault path.\n \n \t If in doubt, say N.\n+\n+config PAGEALLOC_HOGGER\n+\tbool \"Alloc pages from different nodes, zones, migrate types and orders\"\n+\tdepends on DEBUG_FS\n+\thelp\n+\t Say Y to alloc pages from different nodes, zones, migrate type and\n+\t orders using the debug fs filesystem. This helps to generate memory\n+\t pressure easily. This is useful to debug memory reclaims issues,\n+\t OOM triggers, page migration issues, etc.\n+\n+\t If in doubt, say N.\n+\ndiff --git a/mm/Makefile b/mm/Makefile\nindex 2d0570a16e5beb..b48f2ac6ee8089 100644\n--- a/mm/Makefile\n+++ b/mm/Makefile\n@@ -133,6 +133,7 @@ obj-$(CONFIG_IDLE_PAGE_TRACKING) += page_idle.o\n obj-$(CONFIG_DEBUG_PAGEALLOC) += debug_page_alloc.o\n obj-$(CONFIG_DEBUG_PAGE_REF) += debug_page_ref.o\n obj-$(CONFIG_DAMON) += damon/\n+obj-$(CONFIG_PAGEALLOC_HOGGER) += page_alloc_hogger.o\n obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o\n obj-$(CONFIG_PERCPU_STATS) += percpu-stats.o\n obj-$(CONFIG_ZONE_DEVICE) += memremap.o\ndiff --git a/mm/page_alloc_hogger.c b/mm/page_alloc_hogger.c\nnew file mode 100644\nindex 00000000000000..f554bbb4afa730\n--- /dev/null\n+++ b/mm/page_alloc_hogger.c\n@@ -0,0 +1,611 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * Page allocator DebugFS Interface\n+ * Author: Juan Yescas \u003cjyescas@google.com\u003e\n+ *\n+ * This module allows to make page allocs per node, zone, migrate type\n+ * and order using the DebugFS filesystem.\n+ *\n+ * When this module is installed and the debugfs is mounted, the \"mm\" directory\n+ \" will be created in \u003cdebugfs mount point\u003e/mm\" directory. Under this directory,\n+ * there will be subdirs to select the nodes, zones, orders and migrate type.\n+ *\n+ * For example:\n+ *\n+ * /sys/kernel/debug/mm/\n+ * |-- free\n+ * `-- node-0\n+ * |-- zone-DMA\n+ * | |-- order-0\n+ * | | |-- migrate-HighAtomic\n+ * | | | `-- nr_pages_allocs\n+ * | | |-- migrate-Movable\n+ * | | | `-- nr_pages_allocs\n+ * | | |-- migrate-Reclaimable\n+ * | | | `-- nr_pages_allocs\n+ * | | `-- migrate-Unmovable\n+ * | | `-- nr_pages_allocs\n+ * | |-- ...\n+ * | |\n+ * | `-- order-9\n+ * | |-- migrate-HighAtomic\n+ * | | `-- nr_pages_allocs\n+ * | |-- migrate-Movable\n+ * | | `-- nr_pages_allocs\n+ * | |-- migrate-Reclaimable\n+ * | | `-- nr_pages_allocs\n+ * | `-- migrate-Unmovable\n+ * | `-- nr_pages_allocs\n+ * `-- zone-Normal\n+ * |-- order-0\n+ * | |-- migrate-HighAtomic\n+ * | | `-- nr_pages_allocs\n+ * | |-- migrate-Movable\n+ * | | `-- nr_pages_allocs\n+ * | |-- migrate-Reclaimable\n+ * | | `-- nr_pages_allocs\n+ * | `-- migrate-Unmovable\n+ * | `-- nr_pages_allocs\n+ * |-- ....\n+ * |\n+ * `-- order-9\n+ * |-- migrate-HighAtomic\n+ * | `-- nr_pages_allocs\n+ * |-- migrate-Movable\n+ * | `-- nr_pages_allocs\n+ * |-- migrate-Reclaimable\n+ * | `-- nr_pages_allocs\n+ * `-- migrate-Unmovable\n+ * `-- nr_pages_allocs\n+ *\n+ * Usage:\n+ *\n+ * 1. To trigger the allocation, navigate to the debugfs path corresponding\n+ * to your target node, memory zone, allocation order, and migration type,\n+ * then write the requested allocation count to nr_pages_allocs.\n+ *\n+ * For example, to make 3 allocs of order 9, Migrate Type Movable,\n+ * Zone Normal and Node 0, run:\n+ *\n+ * $ echo 3 \u003e /sys/kernel/debug/mm/node-0/zone-Normal/order-9/migrate-Movable/nr_pages_allocs\n+ *\n+ * 2. For each allocation created, a corresponding file named sequentially\n+ * (1, 2, n) will appear in that directory.\n+ *\n+ * $ ls /sys/kernel/debug/mm/node-0/zone-Normal/order-9/migrate-Movable/\n+ * 1 2 3 nr_pages_allocs\n+ *\n+ * 3. To free the allocation, write the allocation file name in /sys/kernel/debug/mm/free.\n+ * For example, to release the 2nd allocation run:\n+ *\n+ * $ echo 2 \u003e /sys/kernel/debug/mm/free\n+ */\n+\n+#define pr_fmt(fmt) KBUILD_MODNAME \": \" fmt\n+\n+#include \u003clinux/atomic.h\u003e\n+#include \u003clinux/errno.h\u003e\n+#include \u003clinux/debugfs.h\u003e\n+#include \u003clinux/gfp.h\u003e\n+#include \u003clinux/gfp_types.h\u003e\n+#include \u003clinux/highmem.h\u003e\n+#include \u003clinux/kernel.h\u003e\n+#include \u003clinux/mmzone.h\u003e\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/nodemask.h\u003e\n+#include \u003clinux/printk.h\u003e\n+#include \u003clinux/slab.h\u003e\n+#include \u003clinux/xarray.h\u003e\n+\n+struct dentry *mmdir;\n+\n+/**\n+ * atomic_long_t allocs_file_seq - Represents the naming sequence used for\n+ * allocation files.\n+ */\n+atomic_long_t allocs_file_seq = ATOMIC_INIT(0);\n+\n+/**\n+ * allocs_xa - Represent the xarray that contains the actual allocations perform\n+ * by this driver.\n+ */\n+static DEFINE_XARRAY(allocs_xa);\n+\n+/**\n+ * struct req_alloc - Represents the requested allocation\n+ * @node_idx: The Node index to allocate from.\n+ * @zone_idx: The Zone index to allocate from.\n+ * @order: The Order of pages to allocate.\n+ * @migrate_type: The Migrate type to allocate from.\n+ * @parentdir: The parent dir where the file representing the alloc was created.\n+ */\n+struct req_alloc {\n+\tint node_idx;\n+\tint zone_idx;\n+\tint order;\n+\tint migrate_type;\n+\tstruct dentry *parentdir;\n+};\n+\n+/*\n+ * struct page_alloc - Represents the page allocation.\n+ * @page: The pointer to the page(s) allocated.\n+ * @req_alloc: The details of the allocation.\n+ * @alloc_dentry: The pointer to the file that represents the allocation.\n+ */\n+struct page_alloc {\n+\tstruct page *page;\n+\tstruct req_alloc *req_alloc;\n+\tstruct dentry *alloc_dentry;\n+};\n+\n+struct kmem_cache *req_alloc_cache;\n+struct kmem_cache *page_alloc_cache;\n+\n+inline void set_zone_to_alloc_from(int zone_idx, gfp_t *flags_ptr)\n+{\n+\tswitch (zone_idx) {\n+#ifdef CONFIG_ZONE_DMA\n+\tcase ZONE_DMA:\n+\t\t*flags_ptr |= __GFP_DMA;\n+\t\tbreak;\n+#endif\n+#ifdef CONFIG_ZONE_DMA32\n+\tcase ZONE_DMA32:\n+\t\t*flags_ptr |= __GFP_DMA32;\n+\t\tbreak;\n+#endif\n+#ifdef CONFIG_HIGHMEM\n+\tcase ZONE_HIGHMEM:\n+\t\t*flags_ptr |= __GFP_HIGHMEM;\n+\t\tbreak;\n+#endif\n+\tcase ZONE_MOVABLE:\n+\t\t*flags_ptr |= __GFP_MOVABLE;\n+\t\tbreak;\n+\tcase ZONE_NORMAL:\n+\tdefault:\n+\t\t*flags_ptr |= 0;\n+\t\tbreak;\n+\t}\n+}\n+\n+inline void set_migrate_type_to_alloc_from(int migrate_type, gfp_t *flags_ptr)\n+{\n+\tswitch (migrate_type) {\n+\tcase MIGRATE_UNMOVABLE:\n+\t\t*flags_ptr \u0026= ~(__GFP_RECLAIMABLE | __GFP_MOVABLE);\n+\t\tbreak;\n+\tcase MIGRATE_RECLAIMABLE:\n+\t\t*flags_ptr |= __GFP_RECLAIMABLE;\n+\t\tbreak;\n+\tcase MIGRATE_HIGHATOMIC:\n+\t\t*flags_ptr |= __GFP_HIGH;\n+\t\tbreak;\n+\tcase MIGRATE_MOVABLE: default:\n+\t\t*flags_ptr |= __GFP_MOVABLE;\n+\t\tbreak;\n+\t}\n+}\n+\n+static ssize_t page_alloc_read(struct file *file, char __user *ubuf,\n+\t\t\t\t size_t cnt, loff_t *ppos)\n+{\n+\t/* Which information should be print about the page or allocation? */\n+\treturn 0;\n+}\n+\n+static const struct file_operations page_alloc_fops = {\n+\t.owner = THIS_MODULE,\n+\t.open = simple_open,\n+\t.read = page_alloc_read,\n+};\n+\n+static int make_alloc(struct req_alloc *req,\n+\t\t\t gfp_t flags, unsigned long *alloc_id)\n+{\n+\tstruct page_alloc *pa;\n+\tstruct page *page;\n+\tchar new_alloc_name[12];\n+\tstruct dentry *new_alloc_dentry;\n+\tint ret;\n+\n+\tpage = alloc_pages_node_noprof(req-\u003enode_idx, flags, req-\u003eorder);\n+\tif (page) {\n+\t\tpa = kmem_cache_alloc(page_alloc_cache, GFP_KERNEL);\n+\t\tif (!pa) {\n+\t\t\tret = -ENOMEM;\n+\t\t\tgoto free_pages;\n+\t\t}\n+\n+\t\t*alloc_id = atomic_long_inc_return(\u0026allocs_file_seq);\n+\t\tret = xa_insert(\u0026allocs_xa, *alloc_id, pa, GFP_KERNEL);\n+\t\tif (ret)\n+\t\t\tgoto free_page_alloc;\n+\n+\t\tsnprintf(new_alloc_name, sizeof(new_alloc_name), \"%lu\",\n+\t\t\t *alloc_id);\n+\n+\t\tnew_alloc_dentry = debugfs_create_file(\n+\t\t\tnew_alloc_name, 0644, req-\u003eparentdir,\n+\t\t\tpa, \u0026page_alloc_fops);\n+\t\tif (IS_ERR(new_alloc_dentry)) {\n+\t\t\tret = PTR_ERR(new_alloc_dentry);\n+\t\t\tgoto erase_xa_entry;\n+\t\t}\n+\n+\t\tpa-\u003ereq_alloc = req;\n+\t\tpa-\u003epage = page;\n+\t\tpa-\u003ealloc_dentry = new_alloc_dentry;\n+\t} else {\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+\n+erase_xa_entry:\n+\txa_erase(\u0026allocs_xa, *alloc_id);\n+\n+free_page_alloc:\n+\tkmem_cache_free(page_alloc_cache, pa);\n+\n+free_pages:\n+\t__free_pages(page, pa-\u003ereq_alloc-\u003eorder);\n+\n+\treturn ret;\n+}\n+\n+static int free_alloc_helper(unsigned long alloc_id)\n+{\n+\tstruct page_alloc *pa;\n+\n+\tpa = xa_erase(\u0026allocs_xa, alloc_id);\n+\tif (!pa) {\n+\t\tpr_err(\"The alloc_id %lu was not found!\", alloc_id);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t__free_pages(pa-\u003epage, pa-\u003ereq_alloc-\u003eorder);\n+\tdebugfs_remove(pa-\u003ealloc_dentry);\n+\tkmem_cache_free(page_alloc_cache, pa);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * req_page_alloc_write() - Allocates the pages on the requested node, zone,\n+ * order and migrate type. Once the allocation is performed, a file is created\n+ * to free the allocation later.\n+ */\n+static ssize_t req_page_alloc_write(struct file *file, const char __user *ubuf,\n+\t\t\t\t size_t cnt, loff_t *ppos)\n+{\n+\tstruct req_alloc *req = file-\u003eprivate_data;\n+\tunsigned long alloc_id;\n+\tunsigned long nr_pages_allocs;\n+\tunsigned long *allocs_ids;\n+\tgfp_t flags = 0;\n+\tint ret;\n+\tint i;\n+\n+\tret = kstrtoul_from_user(ubuf, cnt, 10, \u0026nr_pages_allocs);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tallocs_ids = kmalloc_array(nr_pages_allocs, sizeof(unsigned long),\n+\t\t\t\t\tGFP_KERNEL);\n+\tif (!allocs_ids)\n+\t\treturn -ENOMEM;\n+\n+\tset_zone_to_alloc_from(req-\u003ezone_idx, \u0026flags);\n+\tset_migrate_type_to_alloc_from(req-\u003emigrate_type, \u0026flags);\n+\n+\tfor (i = 0; i \u003c nr_pages_allocs; i++) {\n+\t\tret = make_alloc(req, flags, \u0026alloc_id);\n+\t\tif (ret)\n+\t\t\tgoto free_allocs;\n+\n+\t\tallocs_ids[i] = alloc_id;\n+\t}\n+\n+\tkfree(allocs_ids);\n+\n+\treturn cnt;\n+\n+free_allocs:\n+\t/* Free all the pages previously allocated. */\n+\tfor (int j = 0; j \u003c i; j++) {\n+\t\tint ret2 = free_alloc_helper(allocs_ids[j]);\n+\n+\t\tif (ret2)\n+\t\t\tpr_err(\"Unable to free pages associated with file %lu\",\n+\t\t\t allocs_ids[j]);\n+\t}\n+\n+\tkfree(allocs_ids);\n+\n+\treturn ret;\n+}\n+\n+static const struct file_operations req_page_alloc_fops = {\n+\t.owner = THIS_MODULE,\n+\t.open = simple_open,\n+\t.write = req_page_alloc_write,\n+};\n+\n+/**\n+ * free_page_alloc_write() - free the previously allocated pages.\n+ * @file: File where the user is writing.\n+ * @ubuf: Contains the name of the file the user wants to delete and have the\n+ * memory associated with that file freed.\n+ * @cnt: Size of @ubuf.\n+ * @ppos: Current position in the file.\n+ */\n+static ssize_t free_page_alloc_write(struct file *file,\n+\t\t\t\t const char __user *ubuf, size_t cnt,\n+\t\t\t\t loff_t *ppos)\n+{\n+\tint ret;\n+\tunsigned long alloc_id;\n+\n+\tret = kstrtoul_from_user(ubuf, cnt, 10, \u0026alloc_id);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tret = free_alloc_helper(alloc_id);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn cnt;\n+}\n+\n+static const struct file_operations free_page_alloc_fops = {\n+\t.owner = THIS_MODULE,\n+\t.write = free_page_alloc_write,\n+};\n+\n+/**\n+ * create_nr_pages_allocs_file() - Creates the file \"nr_pages_allocs\".\n+ *\n+ * The \"nr_pages_allocs\" file will be used to write the number of allocations\n+ * that will be performed. When the allocations are performed, a new file for\n+ * each allocation will be created in @migratedir.\n+ */\n+static inline int create_nr_pages_allocs_file(struct dentry *migratedir,\n+\t\t\t\t\t int node_idx, int zone_idx,\n+\t\t\t\t\t int order, int mtype)\n+{\n+\tstruct req_alloc *req;\n+\tstruct dentry *nr_pages;\n+\n+\treq = kmem_cache_alloc(req_alloc_cache, GFP_KERNEL);\n+\tif (!req) {\n+\t\tpr_err(\"Failed to create nr_pages_allocs_info\");\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treq-\u003enode_idx = node_idx;\n+\treq-\u003ezone_idx = zone_idx;\n+\treq-\u003eorder = order;\n+\treq-\u003emigrate_type = mtype;\n+\treq-\u003eparentdir = migratedir;\n+\n+\tnr_pages = debugfs_create_file(\"nr_pages_allocs\", 0644, migratedir, req,\n+\t\t\t\t \u0026req_page_alloc_fops);\n+\tif (IS_ERR(nr_pages))\n+\t\treturn PTR_ERR(nr_pages);\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * create_migrate_type_subdirs() - Creates a directory for each migrate type\n+ * inside the order directory. Once the migrate type directory is created, it\n+ * creates the \"nr_allocs\" file and the \"free\" file.\n+ */\n+static inline int create_migrate_type_subdirs(struct dentry *orderdir,\n+\t\t\t\t\t int node_idx, int zone_idx,\n+\t\t\t\t\t int order)\n+{\n+\tstruct dentry *migratedir;\n+\tchar dirname[24];\n+\tint ret;\n+\n+\tfor (int mtype = 0; mtype \u003c MIGRATE_TYPES; mtype++) {\n+#ifdef CONFIG_CMA\n+\t\tif (mtype == MIGRATE_CMA) /* CMA allocs are not supported yet*/\n+\t\t\tcontinue;\n+#endif\n+#ifdef CONFIG_MEMORY_ISOLATION\n+\t\tif (mtype == MIGRATE_ISOLATE) /* can't allocate from here */\n+\t\t\tcontinue;\n+#endif\n+\t\tsnprintf(dirname, sizeof(dirname), \"migrate-%s\",\n+\t\t\t migratetype_names[mtype]);\n+\t\tmigratedir = debugfs_create_dir(dirname, orderdir);\n+\t\tif (IS_ERR(migratedir))\n+\t\t\treturn PTR_ERR(migratedir);\n+\n+\t\tret = create_nr_pages_allocs_file(migratedir, node_idx,\n+\t\t\t\t\t\t zone_idx, order, mtype);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * create_page_orders_subdirs() - Creates a directory for each page order inside\n+ * the zone directory. Once that the page order directory is created, it creates\n+ * a directory for each migrate type.\n+ */\n+static inline int create_page_orders_subdirs(struct dentry *zonedir,\n+\t\t\t\t\t int node_idx, int zone_idx,\n+\t\t\t\t\t struct zone *zone)\n+{\n+\tstruct dentry *orderdir;\n+\tchar dirname[12];\n+\tint ret;\n+\n+\tfor (int order = 0; order \u003c NR_PAGE_ORDERS; order++) {\n+\t\tsnprintf(dirname, sizeof(dirname), \"order-%d\", order);\n+\t\torderdir = debugfs_create_dir(dirname, zonedir);\n+\t\tif (IS_ERR(orderdir))\n+\t\t\treturn PTR_ERR(orderdir);\n+\n+\t\tret = create_migrate_type_subdirs(orderdir, node_idx, zone_idx,\n+\t\t\t\t\t\t order);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * create_zones_subdirs() - Creates a directory for each populated zone in the\n+ * node. Once that the zone directory is created, it creates a directory for\n+ * each order supported.\n+ *\n+ * Note: ZONE_DEVICE pages won't be allocated using this driver. The main reason\n+ * is because they are not managed by the Buddy Allocator or CMA allocator.\n+ */\n+static inline int create_zones_subdirs(struct dentry *nodedir, int node_idx,\n+\t\t\t\t struct pglist_data *pgdata)\n+{\n+\tstruct dentry *zonedir;\n+\tstruct zone *zone;\n+\tstruct zone *node_zones = pgdata-\u003enode_zones;\n+\tint zone_idx;\n+\tchar dirname[24];\n+\tint ret;\n+\n+\tfor (zone = node_zones, zone_idx = 0; zone - node_zones \u003c MAX_NR_ZONES;\n+\t ++zone, ++zone_idx) {\n+\t\tif (!populated_zone(zone))\n+\t\t\tcontinue;\n+\n+\t\t/* ZONE_DEVICE pages won't be allocated using this driver. */\n+\t\tif (zone_is_zone_device(zone))\n+\t\t\tcontinue;\n+\n+\t\tsnprintf(dirname, sizeof(dirname), \"zone-%s\", zone-\u003ename);\n+\t\tzonedir = debugfs_create_dir(dirname, nodedir);\n+\t\tif (IS_ERR(zonedir))\n+\t\t\treturn PTR_ERR(zonedir);\n+\n+\t\tret = create_page_orders_subdirs(zonedir, node_idx, zone_idx,\n+\t\t\t\t\t\t zone);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * create_nodes_subdirs() - Creates a directory for each online node in the\n+ * system. Once that the node directory is created, it creates a directory for\n+ * each zone in the node.\n+ */\n+static inline int create_nodes_subdirs(struct dentry *mmdir)\n+{\n+\tstruct dentry *nodedir;\n+\tint node_idx;\n+\tchar dirname[12];\n+\tint ret;\n+\n+\tfor_each_online_node(node_idx) {\n+\t\tstruct pglist_data *pgdata = NODE_DATA(node_idx);\n+\n+\t\tsnprintf(dirname, sizeof(dirname), \"node-%d\", node_idx);\n+\t\tnodedir = debugfs_create_dir(dirname, mmdir);\n+\t\tif (IS_ERR(nodedir))\n+\t\t\treturn PTR_ERR(nodedir);\n+\n+\t\tret = create_zones_subdirs(nodedir, node_idx, pgdata);\n+\t\tif (ret)\n+\t\t\treturn ret;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int __init page_alloc_hogger_debugfs_init(void)\n+{\n+\tstruct dentry *free_file;\n+\tint ret;\n+\n+\treq_alloc_cache = kmem_cache_create(\n+\t\t\"req_alloc_cache\", sizeof(struct req_alloc), 0,\n+\t\tSLAB_HWCACHE_ALIGN, NULL);\n+\tif (!req_alloc_cache) {\n+\t\tpr_err(\"The req_alloc_cache couldn't be created\");\n+\t\tret = -ENOMEM;\n+\t\tgoto error_exit;\n+\t}\n+\n+\tpage_alloc_cache = kmem_cache_create(\n+\t\t\"page_alloc_cache\", sizeof(struct page_alloc), 0,\n+\t\tSLAB_HWCACHE_ALIGN, NULL);\n+\tif (!page_alloc_cache) {\n+\t\tpr_err(\"The page_alloc_cache couldn't be created\");\n+\t\tret = -ENOMEM;\n+\t\tgoto clean_req_alloc_cache;\n+\t}\n+\n+\tmmdir = debugfs_create_dir(\"mm\", NULL);\n+\tif (IS_ERR(mmdir)) {\n+\t\tpr_err(\"Unable to create mm directory\");\n+\t\tret = PTR_ERR(mmdir);\n+\t\tgoto clean_page_alloc_cache;\n+\t}\n+\n+\tret = create_nodes_subdirs(mmdir);\n+\tif (ret)\n+\t\tgoto clean_dir;\n+\n+\tfree_file = debugfs_create_file(\"free\", 0644, mmdir, NULL,\n+\t\t\t\t\t\u0026free_page_alloc_fops);\n+\tif (IS_ERR(free_file)) {\n+\t\tret = PTR_ERR(free_file);\n+\t\tgoto clean_dir;\n+\t}\n+\n+\treturn 0;\n+\n+clean_dir:\n+\tdebugfs_remove_recursive(mmdir);\n+\n+clean_page_alloc_cache:\n+\tkmem_cache_destroy(page_alloc_cache);\n+\n+clean_req_alloc_cache:\n+\tkmem_cache_destroy(req_alloc_cache);\n+\n+error_exit:\n+\treturn ret;\n+}\n+\n+static void __exit page_alloc_hogger_debugfs_exit(void)\n+{\n+\tstruct page_alloc *pa;\n+\tunsigned long idx;\n+\n+\txa_for_each(\u0026allocs_xa, idx, pa) {\n+\t\t__free_pages(pa-\u003epage, pa-\u003ereq_alloc-\u003eorder);\n+\t\txa_erase(\u0026allocs_xa, idx);\n+\t}\n+\n+\tdebugfs_remove_recursive(mmdir);\n+\tkmem_cache_destroy(req_alloc_cache);\n+\tkmem_cache_destroy(page_alloc_cache);\n+}\n+\n+module_init(page_alloc_hogger_debugfs_init);\n+module_exit(page_alloc_hogger_debugfs_exit);\n+\n+MODULE_AUTHOR(\"Juan Yescas\");\n+MODULE_DESCRIPTION(\"Module to alloc pages to generate memory pressure\");\n+MODULE_LICENSE(\"GPL\");\n"
} |
| 2/1 | 2026/07/24 12:16 | agent | patch-evaluator |
0mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"PAGEALLOC_HOGGER"
],
"FocusSymbols": [
"req_page_alloc_write",
"free_page_alloc_write",
"make_alloc",
"free_alloc_helper"
],
"Reasoning": "The patch introduces a new debugfs interface for allocating pages from specific nodes, zones, migrate types, and orders. This is a functional testing/debugging tool that can be used to generate memory pressure and test the core page allocator and memory management subsystem. Fuzzing it is valuable to uncover potential bugs in memory management or OOM handling.",
"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 8ab90b56f71bb1d10727757b8f69c491ec8576a0
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Jul 24 12:16:19 2026 +0000
syz-cluster: applied patch under review
diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug
index 7638d75b27db8c..c8fcecdca6aca9 100644
--- a/mm/Kconfig.debug
+++ b/mm/Kconfig.debug
@@ -309,3 +309,15 @@ config PER_VMA_LOCK_STATS
overhead in the page fault path.
If in doubt, say N.
+
+config PAGEALLOC_HOGGER
+ bool "Alloc pages from different nodes, zones, migrate types and orders"
+ depends on DEBUG_FS
+ help
+ Say Y to alloc pages from different nodes, zones, migrate type and
+ orders using the debug fs filesystem. This helps to generate memory
+ pressure easily. This is useful to debug memory reclaims issues,
+ OOM triggers, page migration issues, etc.
+
+ If in doubt, say N.
+
diff --git a/mm/Makefile b/mm/Makefile
index 2d0570a16e5beb..b48f2ac6ee8089 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -133,6 +133,7 @@ obj-$(CONFIG_IDLE_PAGE_TRACKING) += page_idle.o
obj-$(CONFIG_DEBUG_PAGEALLOC) += debug_page_alloc.o
obj-$(CONFIG_DEBUG_PAGE_REF) += debug_page_ref.o
obj-$(CONFIG_DAMON) += damon/
+obj-$(CONFIG_PAGEALLOC_HOGGER) += page_alloc_hogger.o
obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o
obj-$(CONFIG_PERCPU_STATS) += percpu-stats.o
obj-$(CONFIG_ZONE_DEVICE) += memremap.o
diff --git a/mm/page_alloc_hogger.c b/mm/page_alloc_hogger.c
new file mode 100644
index 00000000000000..f554bbb4afa730
--- /dev/null
+++ b/mm/page_alloc_hogger.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Page allocator DebugFS Interface
+ * Author: Juan Yescas <jyescas@google.com>
+ *
+ * This module allows to make page allocs per node, zone, migrate type
+ * and order using the DebugFS filesystem.
+ *
+ * When this module is installed and the debugfs is mounted, the "mm" directory
+ " will be created in <debugfs mount point>/mm" directory. Under this directory,
+ * there will be subdirs to select the nodes, zones, orders and migrate type.
+ *
+ * For example:
+ *
+ * /sys/kernel/debug/mm/
+ * |-- free
+ * `-- node-0
+ * |-- zone-DMA
+ * | |-- order-0
+ * | | |-- migrate-HighAtomic
+ * | | | `-- nr_pages_allocs
+ * | | |-- migrate-Movable
+ * | | | `-- nr_pages_allocs
+ * | | |-- migrate-Reclaimable
+ * | | | `-- nr_pages_allocs
+ * | | `-- migrate-Unmovable
+ * | | `-- nr_pages_allocs
+ * | |-- ...
+ * | |
+ * | `-- order-9
+ * | |-- migrate-HighAtomic
+ * | | `-- nr_pages_allocs
+ * | |-- migrate-Movable
+ * | | `-- nr_pages_allocs
+ * | |-- migrate-Reclaimable
+ * | | `-- nr_pages_allocs
+ * | `-- migrate-Unmovable
+ * | `-- nr_pages_allocs
+ * `-- zone-Normal
+ * |-- order-0
+ * | |-- migrate-HighAtomic
+ * | | `-- nr_pages_allocs
+ * | |-- migrate-Movable
+ * | | `-- nr_pages_allocs
+ * | |-- migrate-Reclaimable
+ * | | `-- nr_pages_allocs
+ * | `-- migrate-Unmovable
+ * | `-- nr_pages_allocs
+ * |-- ....
+ * |
+ * `-- order-9
+ * |-- migrate-HighAtomic
+ * | `-- nr_pages_allocs
+ * |-- migrate-Movable
+ * | `-- nr_pages_allocs
+ * |-- migrate-Reclaimable
+ * | `-- nr_pages_allocs
+ * `-- migrate-Unmovable
+ * `-- nr_pages_allocs
+ *
+ * Usage:
+ *
+ * 1. To trigger the allocation, navigate to the debugfs path corresponding
+ * to your target node, memory zone, allocation order, and migration type,
+ * then write the requested allocation count to nr_pages_allocs.
+ *
+ * For example, to make 3 allocs of order 9, Migrate Type Movable,
+ * Zone Normal and Node 0, run:
+ *
+ * $ echo 3 > /sys/kernel/debug/mm/node-0/zone-Normal/order-9/migrate-Movable/nr_pages_allocs
+ *
+ * 2. For each allocation created, a corresponding file named sequentially
+ * (1, 2, n) will appear in that directory.
+ *
+ * $ ls /sys/kernel/debug/mm/node-0/zone-Normal/order-9/migrate-Movable/
+ * 1 2 3 nr_pages_allocs
+ *
+ * 3. To free the allocation, write the allocation file name in /sys/kernel/debug/mm/free.
+ * For example, to release the 2nd allocation run:
+ *
+ * $ echo 2 > /sys/kernel/debug/mm/free
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/atomic.h>
+#include <linux/errno.h>
+#include <linux/debugfs.h>
+#include <linux/gfp.h>
+#include <linux/gfp_types.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/mmzone.h>
+#include <linux/module.h>
+#include <linux/nodemask.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/xarray.h>
+
+struct dentry *mmdir;
+
+/**
+ * atomic_long_t allocs_file_seq - Represents the naming sequence used for
+ * allocation files.
+ */
+atomic_long_t allocs_file_seq = ATOMIC_INIT(0);
+
+/**
+ * allocs_xa - Represent the xarray that contains the actual allocations perform
+ * by this driver.
+ */
+static DEFINE_XARRAY(allocs_xa);
+
+/**
+ * struct req_alloc - Represents the requested allocation
+ * @node_idx: The Node index to allocate from.
+ * @zone_idx: The Zone index to allocate from.
+ * @order: The Order of pages to allocate.
+ * @migrate_type: The Migrate type to allocate from.
+ * @parentdir: The parent dir where the file representing the alloc was created.
+ */
+struct req_alloc {
+ int node_idx;
+ int zone_idx;
+ int order;
+ int migrate_type;
+ struct dentry *parentdir;
+};
+
+/*
+ * struct page_alloc - Represents the page allocation.
+ * @page: The pointer to the page(s) allocated.
+ * @req_alloc: The details of the allocation.
+ * @alloc_dentry: The pointer to the file that represents the allocation.
+ */
+struct page_alloc {
+ struct page *page;
+ struct req_alloc *req_alloc;
+ struct dentry *alloc_dentry;
+};
+
+struct kmem_cache *req_alloc_cache;
+struct kmem_cache *page_alloc_cache;
+
+inline void set_zone_to_alloc_from(int zone_idx, gfp_t *flags_ptr)
+{
+ switch (zone_idx) {
+#ifdef CONFIG_ZONE_DMA
+ case ZONE_DMA:
+ *flags_ptr |= __GFP_DMA;
+ break;
+#endif
+#ifdef CONFIG_ZONE_DMA32
+ case ZONE_DMA32:
+ *flags_ptr |= __GFP_DMA32;
+ break;
+#endif
+#ifdef CONFIG_HIGHMEM
+ case ZONE_HIGHMEM:
+ *flags_ptr |= __GFP_HIGHMEM;
+ break;
+#endif
+ case ZONE_MOVABLE:
+ *flags_ptr |= __GFP_MOVABLE;
+ break;
+ case ZONE_NORMAL:
+ default:
+ *flags_ptr |= 0;
+ break;
+ }
+}
+
+inline void set_migrate_type_to_alloc_from(int migrate_type, gfp_t *flags_ptr)
+{
+ switch (migrate_type) {
+ case MIGRATE_UNMOVABLE:
+ *flags_ptr &= ~(__GFP_RECLAIMABLE | __GFP_MOVABLE);
+ break;
+ case MIGRATE_RECLAIMABLE:
+ *flags_ptr |= __GFP_RECLAIMABLE;
+ break;
+ case MIGRATE_HIGHATOMIC:
+ *flags_ptr |= __GFP_HIGH;
+ break;
+ case MIGRATE_MOVABLE: default:
+ *flags_ptr |= __GFP_MOVABLE;
+ break;
+ }
+}
+
+static ssize_t page_alloc_read(struct file *file, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ /* Which information should be print about the page or allocation? */
+ return 0;
+}
+
+static const struct file_operations page_alloc_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = page_alloc_read,
+};
+
+static int make_alloc(struct req_alloc *req,
+ gfp_t flags, unsigned long *alloc_id)
+{
+ struct page_alloc *pa;
+ struct page *page;
+ char new_alloc_name[12];
+ struct dentry *new_alloc_dentry;
+ int ret;
+
+ page = alloc_pages_node_noprof(req->node_idx, flags, req->order);
+ if (page) {
+ pa = kmem_cache_alloc(page_alloc_cache, GFP_KERNEL);
+ if (!pa) {
+ ret = -ENOMEM;
+ goto free_pages;
+ }
+
+ *alloc_id = atomic_long_inc_return(&allocs_file_seq);
+ ret = xa_insert(&allocs_xa, *alloc_id, pa, GFP_KERNEL);
+ if (ret)
+ goto free_page_alloc;
+
+ snprintf(new_alloc_name, sizeof(new_alloc_name), "%lu",
+ *alloc_id);
+
+ new_alloc_dentry = debugfs_create_file(
+ new_alloc_name, 0644, req->parentdir,
+ pa, &page_alloc_fops);
+ if (IS_ERR(new_alloc_dentry)) {
+ ret = PTR_ERR(new_alloc_dentry);
+ goto erase_xa_entry;
+ }
+
+ pa->req_alloc = req;
+ pa->page = page;
+ pa->alloc_dentry = new_alloc_dentry;
+ } else {
+ return -ENOMEM;
+ }
+
+ return 0;
+
+erase_xa_entry:
+ xa_erase(&allocs_xa, *alloc_id);
+
+free_page_alloc:
+ kmem_cache_free(page_alloc_cache, pa);
+
+free_pages:
+ __free_pages(page, pa->req_alloc->order);
+
+ return ret;
+}
+
+static int free_alloc_helper(unsigned long alloc_id)
+{
+ struct page_alloc *pa;
+
+ pa = xa_erase(&allocs_xa, alloc_id);
+ if (!pa) {
+ pr_err("The alloc_id %lu was not found!", alloc_id);
+ return -EINVAL;
+ }
+
+ __free_pages(pa->page, pa->req_alloc->order);
+ debugfs_remove(pa->alloc_dentry);
+ kmem_cache_free(page_alloc_cache, pa);
+
+ return 0;
+}
+
+/**
+ * req_page_alloc_write() - Allocates the pages on the requested node, zone,
+ * order and migrate type. Once the allocation is performed, a file is created
+ * to free the allocation later.
+ */
+static ssize_t req_page_alloc_write(struct file *file, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct req_alloc *req = file->private_data;
+ unsigned long alloc_id;
+ unsigned long nr_pages_allocs;
+ unsigned long *allocs_ids;
+ gfp_t flags = 0;
+ int ret;
+ int i;
+
+ ret = kstrtoul_from_user(ubuf, cnt, 10, &nr_pages_allocs);
+ if (ret)
+ return ret;
+
+ allocs_ids = kmalloc_array(nr_pages_allocs, sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!allocs_ids)
+ return -ENOMEM;
+
+ set_zone_to_alloc_from(req->zone_idx, &flags);
+ set_migrate_type_to_alloc_from(req->migrate_type, &flags);
+
+ for (i = 0; i < nr_pages_allocs; i++) {
+ ret = make_alloc(req, flags, &alloc_id);
+ if (ret)
+ goto free_allocs;
+
+ allocs_ids[i] = alloc_id;
+ }
+
+ kfree(allocs_ids);
+
+ return cnt;
+
+free_allocs:
+ /* Free all the pages previously allocated. */
+ for (int j = 0; j < i; j++) {
+ int ret2 = free_alloc_helper(allocs_ids[j]);
+
+ if (ret2)
+ pr_err("Unable to free pages associated with file %lu",
+ allocs_ids[j]);
+ }
+
+ kfree(allocs_ids);
+
+ return ret;
+}
+
+static const struct file_operations req_page_alloc_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .write = req_page_alloc_write,
+};
+
+/**
+ * free_page_alloc_write() - free the previously allocated pages.
+ * @file: File where the user is writing.
+ * @ubuf: Contains the name of the file the user wants to delete and have the
+ * memory associated with that file freed.
+ * @cnt: Size of @ubuf.
+ * @ppos: Current position in the file.
+ */
+static ssize_t free_page_alloc_write(struct file *file,
+ const char __user *ubuf, size_t cnt,
+ loff_t *ppos)
+{
+ int ret;
+ unsigned long alloc_id;
+
+ ret = kstrtoul_from_user(ubuf, cnt, 10, &alloc_id);
+ if (ret)
+ return ret;
+
+ ret = free_alloc_helper(alloc_id);
+ if (ret)
+ return ret;
+
+ return cnt;
+}
+
+static const struct file_operations free_page_alloc_fops = {
+ .owner = THIS_MODULE,
+ .write = free_page_alloc_write,
+};
+
+/**
+ * create_nr_pages_allocs_file() - Creates the file "nr_pages_allocs".
+ *
+ * The "nr_pages_allocs" file will be used to write the number of allocations
+ * that will be performed. When the allocations are performed, a new file for
+ * each allocation will be created in @migratedir.
+ */
+static inline int create_nr_pages_allocs_file(struct dentry *migratedir,
+ int node_idx, int zone_idx,
+ int order, int mtype)
+{
+ struct req_alloc *req;
+ struct dentry *nr_pages;
+
+ req = kmem_cache_alloc(req_alloc_cache, GFP_KERNEL);
+ if (!req) {
+ pr_err("Failed to create nr_pages_allocs_info");
+ return -ENOMEM;
+ }
+
+ req->node_idx = node_idx;
+ req->zone_idx = zone_idx;
+ req->order = order;
+ req->migrate_type = mtype;
+ req->parentdir = migratedir;
+
+ nr_pages = debugfs_create_file("nr_pages_allocs", 0644, migratedir, req,
+ &req_page_alloc_fops);
+ if (IS_ERR(nr_pages))
+ return PTR_ERR(nr_pages);
+
+ return 0;
+}
+
+/**
+ * create_migrate_type_subdirs() - Creates a directory for each migrate type
+ * inside the order directory. Once the migrate type directory is created, it
+ * creates the "nr_allocs" file and the "free" file.
+ */
+static inline int create_migrate_type_subdirs(struct dentry *orderdir,
+ int node_idx, int zone_idx,
+ int order)
+{
+ struct dentry *migratedir;
+ char dirname[24];
+ int ret;
+
+ for (int mtype = 0; mtype < MIGRATE_TYPES; mtype++) {
+#ifdef CONFIG_CMA
+ if (mtype == MIGRATE_CMA) /* CMA allocs are not supported yet*/
+ continue;
+#endif
+#ifdef CONFIG_MEMORY_ISOLATION
+ if (mtype == MIGRATE_ISOLATE) /* can't allocate from here */
+ continue;
+#endif
+ snprintf(dirname, sizeof(dirname), "migrate-%s",
+ migratetype_names[mtype]);
+ migratedir = debugfs_create_dir(dirname, orderdir);
+ if (IS_ERR(migratedir))
+ return PTR_ERR(migratedir);
+
+ ret = create_nr_pages_allocs_file(migratedir, node_idx,
+ zone_idx, order, mtype);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * create_page_orders_subdirs() - Creates a directory for each page order inside
+ * the zone directory. Once that the page order directory is created, it creates
+ * a directory for each migrate type.
+ */
+static inline int create_page_orders_subdirs(struct dentry *zonedir,
+ int node_idx, int zone_idx,
+ struct zone *zone)
+{
+ struct dentry *orderdir;
+ char dirname[12];
+ int ret;
+
+ for (int order = 0; order < NR_PAGE_ORDERS; order++) {
+ snprintf(dirname, sizeof(dirname), "order-%d", order);
+ orderdir = debugfs_create_dir(dirname, zonedir);
+ if (IS_ERR(orderdir))
+ return PTR_ERR(orderdir);
+
+ ret = create_migrate_type_subdirs(orderdir, node_idx, zone_idx,
+ order);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * create_zones_subdirs() - Creates a directory for each populated zone in the
+ * node. Once that the zone directory is created, it creates a directory for
+ * each order supported.
+ *
+ * Note: ZONE_DEVICE pages won't be allocated using this driver. The main reason
+ * is because they are not managed by the Buddy Allocator or CMA allocator.
+ */
+static inline int create_zones_subdirs(struct dentry *nodedir, int node_idx,
+ struct pglist_data *pgdata)
+{
+ struct dentry *zonedir;
+ struct zone *zone;
+ struct zone *node_zones = pgdata->node_zones;
+ int zone_idx;
+ char dirname[24];
+ int ret;
+
+ for (zone = node_zones, zone_idx = 0; zone - node_zones < MAX_NR_ZONES;
+ ++zone, ++zone_idx) {
+ if (!populated_zone(zone))
+ continue;
+
+ /* ZONE_DEVICE pages won't be allocated using this driver. */
+ if (zone_is_zone_device(zone))
+ continue;
+
+ snprintf(dirname, sizeof(dirname), "zone-%s", zone->name);
+ zonedir = debugfs_create_dir(dirname, nodedir);
+ if (IS_ERR(zonedir))
+ return PTR_ERR(zonedir);
+
+ ret = create_page_orders_subdirs(zonedir, node_idx, zone_idx,
+ zone);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * create_nodes_subdirs() - Creates a directory for each online node in the
+ * system. Once that the node directory is created, it creates a directory for
+ * each zone in the node.
+ */
+static inline int create_nodes_subdirs(struct dentry *mmdir)
+{
+ struct dentry *nodedir;
+ int node_idx;
+ char dirname[12];
+ int ret;
+
+ for_each_online_node(node_idx) {
+ struct pglist_data *pgdata = NODE_DATA(node_idx);
+
+ snprintf(dirname, sizeof(dirname), "node-%d", node_idx);
+ nodedir = debugfs_create_dir(dirname, mmdir);
+ if (IS_ERR(nodedir))
+ return PTR_ERR(nodedir);
+
+ ret = create_zones_subdirs(nodedir, node_idx, pgdata);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init page_alloc_hogger_debugfs_init(void)
+{
+ struct dentry *free_file;
+ int ret;
+
+ req_alloc_cache = kmem_cache_create(
+ "req_alloc_cache", sizeof(struct req_alloc), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!req_alloc_cache) {
+ pr_err("The req_alloc_cache couldn't be created");
+ ret = -ENOMEM;
+ goto error_exit;
+ }
+
+ page_alloc_cache = kmem_cache_create(
+ "page_alloc_cache", sizeof(struct page_alloc), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!page_alloc_cache) {
+ pr_err("The page_alloc_cache couldn't be created");
+ ret = -ENOMEM;
+ goto clean_req_alloc_cache;
+ }
+
+ mmdir = debugfs_create_dir("mm", NULL);
+ if (IS_ERR(mmdir)) {
+ pr_err("Unable to create mm directory");
+ ret = PTR_ERR(mmdir);
+ goto clean_page_alloc_cache;
+ }
+
+ ret = create_nodes_subdirs(mmdir);
+ if (ret)
+ goto clean_dir;
+
+ free_file = debugfs_create_file("free", 0644, mmdir, NULL,
+ &free_page_alloc_fops);
+ if (IS_ERR(free_file)) {
+ ret = PTR_ERR(free_file);
+ goto clean_dir;
+ }
+
+ return 0;
+
+clean_dir:
+ debugfs_remove_recursive(mmdir);
+
+clean_page_alloc_cache:
+ kmem_cache_destroy(page_alloc_cache);
+
+clean_req_alloc_cache:
+ kmem_cache_destroy(req_alloc_cache);
+
+error_exit:
+ return ret;
+}
+
+static void __exit page_alloc_hogger_debugfs_exit(void)
+{
+ struct page_alloc *pa;
+ unsigned long idx;
+
+ xa_for_each(&allocs_xa, idx, pa) {
+ __free_pages(pa->page, pa->req_alloc->order);
+ xa_erase(&allocs_xa, idx);
+ }
+
+ debugfs_remove_recursive(mmdir);
+ kmem_cache_destroy(req_alloc_cache);
+ kmem_cache_destroy(page_alloc_cache);
+}
+
+module_init(page_alloc_hogger_debugfs_init);
+module_exit(page_alloc_hogger_debugfs_exit);
+
+MODULE_AUTHOR("Juan Yescas");
+MODULE_DESCRIPTION("Module to alloc pages to generate memory pressure");
+MODULE_LICENSE("GPL");
|
| 3/2 | 2026/07/24 12:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 8427 output: 20 thoughts: 384 Thoughts: **Analyzing the "Page Alloc Hogger" Debugfs Interface for Fuzzing** Okay, so there's this new patch, `page_alloc_hogger.c`, that introduces a debugfs interface, specifically under `/sys/kernel/debug/mm/`. The goal here is to create artificial memory pressure and test things like memory reclaim, the Out-of-Memory (OOM) killer, and page migration. The important thing is, it's configurable via `CONFIG_PAGEALLOC_HOGGER` and relies on `DEBUG_FS`. My initial thought is, is this fuzzable? Absolutely. It gives us a way to allocate memory with a variety of `GFP` flags and allocation orders, which could be a goldmine for triggering interesting memory management bugs or hitting those edge cases that lead to OOM conditions. It seems to be a straightforward interface that essentially just calls `alloc_pages_node_noprof` and `__free_pages`, so it's a testing and debugging tool. But that's exactly why fuzzing it could be so valuable, testing the page allocator and the memory management subsystem. Now, syzkaller, being the fuzzing behemoth that it is, will likely go after any reachable debugfs interfaces. So let's think: is it reachable? Debugfs is typically mounted, and, critically, syzkaller runs as root. Bingo. So it's definitely accessible. Let's quickly review the core functions in `page_alloc_hogger.c`: `req_page_alloc_write`, `free_page_alloc_write`, `make_alloc`, and `free_alloc_helper`. Specifically, `req_page_alloc_write` and `free_page_alloc_write` seem to be the primary targets, those are the entry points. I will use my existing tools to verify that this is indeed the case. |
| 4/2 | 2026/07/24 12:16 | tool | grepper |
0mArgs:{
"Expression": "PAGEALLOC_HOGGER"
}Results: {
"Output": "mm/Kconfig.debug=300=config PER_VMA_LOCK_STATS\n--\nmm/Kconfig.debug-312-\nmm/Kconfig.debug:313:config PAGEALLOC_HOGGER\nmm/Kconfig.debug-314-\tbool \"Alloc pages from different nodes, zones, migrate types and orders\"\n--\nmm/Makefile=135=obj-$(CONFIG_DAMON) += damon/\nmm/Makefile:136:obj-$(CONFIG_PAGEALLOC_HOGGER) += page_alloc_hogger.o\nmm/Makefile-137-obj-$(CONFIG_HARDENED_USERCOPY) += usercopy.o\n"
} |
| 5/2 | 2026/07/24 12:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9412 output: 39 thoughts: 95 Thoughts: Here's my analysis of the situation: **Investigating the PAGEALLOC_HOGGER Configuration** Okay, so we're dealing with a system configured with `PAGEALLOC_HOGGER`. My primary targets are the functions `req_page_alloc_write`, `free_page_alloc_write`, `make_alloc`, and `free_alloc_helper`. I need to understand how this config interacts with those core allocation routines. Before diving deep, let's just make sure there isn't some obvious other place I need to consider - like an init function or a pre-defined set of constants. Right, let me just quickly glance at `page_alloc_read`. Ah, it just returns zero. That's good to know, but likely won't give me any clues. So, it looks like my attention should be primarily on `req_page_alloc_write` and `make_alloc` for understanding the behavior under this specific configuration. I need to dig into the implementation details of those two functions. Let's see what's actually going on in those implementations. |
| 6/2 | 2026/07/24 12:16 | tool | read-file |
0mArgs:{
"File": "mm/page_alloc_hogger.c",
"FirstLine": 210,
"LineCount": 100
}Results: {
"Contents": " 210:\t\tstruct dentry *new_alloc_dentry;\n 211:\t\tint ret;\n 212:\t\n 213:\t\tpage = alloc_pages_node_noprof(req-\u003enode_idx, flags, req-\u003eorder);\n 214:\t\tif (page) {\n 215:\t\t\tpa = kmem_cache_alloc(page_alloc_cache, GFP_KERNEL);\n 216:\t\t\tif (!pa) {\n 217:\t\t\t\tret = -ENOMEM;\n 218:\t\t\t\tgoto free_pages;\n 219:\t\t\t}\n 220:\t\n 221:\t\t\t*alloc_id = atomic_long_inc_return(\u0026allocs_file_seq);\n 222:\t\t\tret = xa_insert(\u0026allocs_xa, *alloc_id, pa, GFP_KERNEL);\n 223:\t\t\tif (ret)\n 224:\t\t\t\tgoto free_page_alloc;\n 225:\t\n 226:\t\t\tsnprintf(new_alloc_name, sizeof(new_alloc_name), \"%lu\",\n 227:\t\t\t\t *alloc_id);\n 228:\t\n 229:\t\t\tnew_alloc_dentry = debugfs_create_file(\n 230:\t\t\t\tnew_alloc_name, 0644, req-\u003eparentdir,\n 231:\t\t\t\tpa, \u0026page_alloc_fops);\n 232:\t\t\tif (IS_ERR(new_alloc_dentry)) {\n 233:\t\t\t\tret = PTR_ERR(new_alloc_dentry);\n 234:\t\t\t\tgoto erase_xa_entry;\n 235:\t\t\t}\n 236:\t\n 237:\t\t\tpa-\u003ereq_alloc = req;\n 238:\t\t\tpa-\u003epage = page;\n 239:\t\t\tpa-\u003ealloc_dentry = new_alloc_dentry;\n 240:\t\t} else {\n 241:\t\t\treturn -ENOMEM;\n 242:\t\t}\n 243:\t\n 244:\t\treturn 0;\n 245:\t\n 246:\terase_xa_entry:\n 247:\t\txa_erase(\u0026allocs_xa, *alloc_id);\n 248:\t\n 249:\tfree_page_alloc:\n 250:\t\tkmem_cache_free(page_alloc_cache, pa);\n 251:\t\n 252:\tfree_pages:\n 253:\t\t__free_pages(page, pa-\u003ereq_alloc-\u003eorder);\n 254:\t\n 255:\t\treturn ret;\n 256:\t}\n 257:\t\n 258:\tstatic int free_alloc_helper(unsigned long alloc_id)\n 259:\t{\n 260:\t\tstruct page_alloc *pa;\n 261:\t\n 262:\t\tpa = xa_erase(\u0026allocs_xa, alloc_id);\n 263:\t\tif (!pa) {\n 264:\t\t\tpr_err(\"The alloc_id %lu was not found!\", alloc_id);\n 265:\t\t\treturn -EINVAL;\n 266:\t\t}\n 267:\t\n 268:\t\t__free_pages(pa-\u003epage, pa-\u003ereq_alloc-\u003eorder);\n 269:\t\tdebugfs_remove(pa-\u003ealloc_dentry);\n 270:\t\tkmem_cache_free(page_alloc_cache, pa);\n 271:\t\n 272:\t\treturn 0;\n 273:\t}\n 274:\t\n 275:\t/**\n 276:\t * req_page_alloc_write() - Allocates the pages on the requested node, zone,\n 277:\t * order and migrate type. Once the allocation is performed, a file is created\n 278:\t * to free the allocation later.\n 279:\t */\n 280:\tstatic ssize_t req_page_alloc_write(struct file *file, const char __user *ubuf,\n 281:\t\t\t\t\t size_t cnt, loff_t *ppos)\n 282:\t{\n 283:\t\tstruct req_alloc *req = file-\u003eprivate_data;\n 284:\t\tunsigned long alloc_id;\n 285:\t\tunsigned long nr_pages_allocs;\n 286:\t\tunsigned long *allocs_ids;\n 287:\t\tgfp_t flags = 0;\n 288:\t\tint ret;\n 289:\t\tint i;\n 290:\t\n 291:\t\tret = kstrtoul_from_user(ubuf, cnt, 10, \u0026nr_pages_allocs);\n 292:\t\tif (ret)\n 293:\t\t\treturn ret;\n 294:\t\n 295:\t\tallocs_ids = kmalloc_array(nr_pages_allocs, sizeof(unsigned long),\n 296:\t\t\t\t\t\tGFP_KERNEL);\n 297:\t\tif (!allocs_ids)\n 298:\t\t\treturn -ENOMEM;\n 299:\t\n 300:\t\tset_zone_to_alloc_from(req-\u003ezone_idx, \u0026flags);\n 301:\t\tset_migrate_type_to_alloc_from(req-\u003emigrate_type, \u0026flags);\n 302:\t\n 303:\t\tfor (i = 0; i \u003c nr_pages_allocs; i++) {\n 304:\t\t\tret = make_alloc(req, flags, \u0026alloc_id);\n 305:\t\t\tif (ret)\n 306:\t\t\t\tgoto free_allocs;\n 307:\t\n 308:\t\t\tallocs_ids[i] = alloc_id;\n 309:\t\t}\n"
} |
| 7/2 | 2026/07/24 12:16 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11256 output: 140 thoughts: 158 Thoughts: **My Assessment of the Page Allocator Testing Code** Okay, so the code is functional – good start! It's designed to test the page allocator, specifically by providing a debugfs interface. This allows for controlled allocation and freeing of pages, with specific parameters. Hmm, this is interesting... I can see the immediate application here: it looks like it'd be pretty darn useful for fuzzing the memory management subsystem. That's a valuable capability to have. I'm going to flag this as something WorthFuzzing. I need to focus on a few key symbols here: `req_page_alloc_write`, `free_page_alloc_write`, `make_alloc`, and `free_alloc_helper`. Those are the entry points and core functions that I'll need to understand in detail to fully leverage this for testing. And of course, I should make sure that the `PAGEALLOC_HOGGER` config is enabled to use the debugging tool. Alright, let me just double-check, because I'm a bit obsessive about being thorough. Ah, yes, the patch is a new file, `mm/page_alloc_hogger.c`, which makes sense. It's adding that debugfs interface, which confirms my initial impression. The functionality is definitely there, and it's reachable via debugfs. Great. Looks like I've got everything I need. Time to set-results. |
| 8/2 | 2026/07/24 12:16 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PAGEALLOC_HOGGER"
],
"FocusSymbols": [
"req_page_alloc_write",
"free_page_alloc_write",
"make_alloc",
"free_alloc_helper"
],
"Reasoning": "The patch introduces a new debugfs interface for allocating pages from specific nodes, zones, migrate types, and orders. This is a functional testing/debugging tool that can be used to generate memory pressure and test the core page allocator and memory management subsystem. Fuzzing it is valuable to uncover potential bugs in memory management or OOM handling.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PAGEALLOC_HOGGER"
],
"FocusSymbols": [
"req_page_alloc_write",
"free_page_alloc_write",
"make_alloc",
"free_alloc_helper"
],
"Reasoning": "The patch introduces a new debugfs interface for allocating pages from specific nodes, zones, migrate types, and orders. This is a functional testing/debugging tool that can be used to generate memory pressure and test the core page allocator and memory management subsystem. Fuzzing it is valuable to uncover potential bugs in memory management or OOM handling.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|