| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/01 11:12 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"PAGE_OWNER"
],
"FocusSymbols": [
"read_page_owner",
"pagetypeinfo_showmixedcount_print"
],
"Reasoning": "The patch introduces a functional change in mm/page_owner.c to safely skip buddy pages during iteration, preventing potential out-of-bounds PFN accesses when reading /sys/kernel/debug/page_owner or /proc/pagetypeinfo. It also fixes memcg data reading in page owner.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/01 11:12 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 3d436bf690ea0e60c36a6cee9b3235fc2bbe8c99\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 1 11:12:36 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h\nindex 2abaf99321e90d..fa828232dfcc18 100644\n--- a/include/linux/hugetlb.h\n+++ b/include/linux/hugetlb.h\n@@ -154,7 +154,8 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,\n bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);\n int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);\n void folio_putback_hugetlb(struct folio *folio);\n-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);\n+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,\n+\t\t\tenum migrate_reason reason);\n void hugetlb_fix_reserve_counts(struct inode *inode);\n extern struct mutex *hugetlb_fault_mutex_table;\n u32 hugetlb_fault_mutex_hash(struct address_space *mapping, pgoff_t idx);\n@@ -424,7 +425,7 @@ static inline void folio_putback_hugetlb(struct folio *folio)\n }\n \n static inline void move_hugetlb_state(struct folio *old_folio,\n-\t\t\t\t\tstruct folio *new_folio, int reason)\n+\t\t\t\t\tstruct folio *new_folio, enum migrate_reason reason)\n {\n }\n \n@@ -956,7 +957,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)\n \treturn modified_mask;\n }\n \n-static inline bool htlb_allow_alloc_fallback(int reason)\n+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)\n {\n \tbool allowed_fallback = false;\n \n@@ -1238,7 +1239,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)\n \treturn 0;\n }\n \n-static inline bool htlb_allow_alloc_fallback(int reason)\n+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)\n {\n \treturn false;\n }\ndiff --git a/include/linux/migrate.h b/include/linux/migrate.h\nindex d5af2b7f577b33..1f83924615d6d3 100644\n--- a/include/linux/migrate.h\n+++ b/include/linux/migrate.h\n@@ -57,7 +57,8 @@ void putback_movable_pages(struct list_head *l);\n int migrate_folio(struct address_space *mapping, struct folio *dst,\n \t\tstruct folio *src, enum migrate_mode mode);\n int migrate_pages(struct list_head *l, new_folio_t new, free_folio_t free,\n-\t\t unsigned long private, enum migrate_mode mode, int reason,\n+\t\t unsigned long private, enum migrate_mode mode,\n+\t\t enum migrate_reason reason,\n \t\t unsigned int *ret_succeeded);\n struct folio *alloc_migration_target(struct folio *src, unsigned long private);\n bool isolate_movable_ops_page(struct page *page, isolate_mode_t mode);\n@@ -77,7 +78,8 @@ int set_movable_ops(const struct movable_operations *ops, enum pagetype type);\n static inline void putback_movable_pages(struct list_head *l) {}\n static inline int migrate_pages(struct list_head *l, new_folio_t new,\n \t\tfree_folio_t free, unsigned long private,\n-\t\tenum migrate_mode mode, int reason, unsigned int *ret_succeeded)\n+\t\tenum migrate_mode mode, enum migrate_reason reason,\n+\t\tunsigned int *ret_succeeded)\n \t{ return -ENOSYS; }\n static inline struct folio *alloc_migration_target(struct folio *src,\n \t\tunsigned long private)\ndiff --git a/include/linux/migrate_mode.h b/include/linux/migrate_mode.h\nindex 265c4328b36ad5..05102d4d2490e6 100644\n--- a/include/linux/migrate_mode.h\n+++ b/include/linux/migrate_mode.h\n@@ -25,6 +25,7 @@ enum migrate_reason {\n \tMR_LONGTERM_PIN,\n \tMR_DEMOTION,\n \tMR_DAMON,\n+\tMR_NEVER,\t\t/* page has never been migrated */\n \tMR_TYPES\n };\n \ndiff --git a/include/linux/page_owner.h b/include/linux/page_owner.h\nindex 3328357f6dba72..9fe51dfccf2685 100644\n--- a/include/linux/page_owner.h\n+++ b/include/linux/page_owner.h\n@@ -3,6 +3,7 @@\n #define __LINUX_PAGE_OWNER_H\n \n #include \u003clinux/jump_label.h\u003e\n+#include \u003clinux/migrate_mode.h\u003e\n \n #ifdef CONFIG_PAGE_OWNER\n extern struct static_key_false page_owner_inited;\n@@ -14,7 +15,7 @@ extern void __set_page_owner(struct page *page,\n extern void __split_page_owner(struct page *page, int old_order,\n \t\t\tint new_order);\n extern void __folio_copy_owner(struct folio *newfolio, struct folio *old);\n-extern void __folio_set_owner_migrate_reason(struct folio *folio, int reason);\n+extern void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason);\n extern void __dump_page_owner(const struct page *page);\n extern void pagetypeinfo_showmixedcount_print(struct seq_file *m,\n \t\t\t\t\tpg_data_t *pgdat, struct zone *zone);\n@@ -43,7 +44,7 @@ static inline void folio_copy_owner(struct folio *newfolio, struct folio *old)\n \tif (static_branch_unlikely(\u0026page_owner_inited))\n \t\t__folio_copy_owner(newfolio, old);\n }\n-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)\n+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)\n {\n \tif (static_branch_unlikely(\u0026page_owner_inited))\n \t\t__folio_set_owner_migrate_reason(folio, reason);\n@@ -68,7 +69,7 @@ static inline void split_page_owner(struct page *page, int old_order,\n static inline void folio_copy_owner(struct folio *newfolio, struct folio *folio)\n {\n }\n-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)\n+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)\n {\n }\n static inline void dump_page_owner(const struct page *page)\ndiff --git a/include/trace/events/migrate.h b/include/trace/events/migrate.h\nindex cd01dd7b3640dc..15ee2ef201b5f9 100644\n--- a/include/trace/events/migrate.h\n+++ b/include/trace/events/migrate.h\n@@ -23,7 +23,8 @@\n \tEM( MR_CONTIG_RANGE,\t\"contig_range\")\t\t\t\\\n \tEM( MR_LONGTERM_PIN,\t\"longterm_pin\")\t\t\t\\\n \tEM( MR_DEMOTION,\t\"demotion\")\t\t\t\\\n-\tEMe(MR_DAMON,\t\t\"damon\")\n+\tEM( MR_DAMON,\t\t\"damon\")\t\t\t\\\n+\tEMe(MR_NEVER,\t\t\"never_migrated\")\n \n /*\n * First define the enums in the above macros to be exported to userspace\n@@ -51,7 +52,7 @@ TRACE_EVENT(mm_migrate_pages,\n \tTP_PROTO(unsigned long succeeded, unsigned long failed,\n \t\t unsigned long thp_succeeded, unsigned long thp_failed,\n \t\t unsigned long thp_split, unsigned long large_folio_split,\n-\t\t enum migrate_mode mode, int reason),\n+\t\t enum migrate_mode mode, enum migrate_reason reason),\n \n \tTP_ARGS(succeeded, failed, thp_succeeded, thp_failed,\n \t\tthp_split, large_folio_split, mode, reason),\n@@ -64,7 +65,7 @@ TRACE_EVENT(mm_migrate_pages,\n \t\t__field(\tunsigned long,\t\tthp_split)\n \t\t__field(\tunsigned long,\t\tlarge_folio_split)\n \t\t__field(\tenum migrate_mode,\tmode)\n-\t\t__field(\tint,\t\t\treason)\n+\t\t__field(\tenum migrate_reason,\treason)\n \t),\n \n \tTP_fast_assign(\n@@ -91,13 +92,13 @@ TRACE_EVENT(mm_migrate_pages,\n \n TRACE_EVENT(mm_migrate_pages_start,\n \n-\tTP_PROTO(enum migrate_mode mode, int reason),\n+\tTP_PROTO(enum migrate_mode mode, enum migrate_reason reason),\n \n \tTP_ARGS(mode, reason),\n \n \tTP_STRUCT__entry(\n \t\t__field(enum migrate_mode, mode)\n-\t\t__field(int, reason)\n+\t\t__field(enum migrate_reason, reason)\n \t),\n \n \tTP_fast_assign(\ndiff --git a/mm/hugetlb.c b/mm/hugetlb.c\nindex 571212b80835ec..17732d1fdc5efd 100644\n--- a/mm/hugetlb.c\n+++ b/mm/hugetlb.c\n@@ -7182,7 +7182,8 @@ void folio_putback_hugetlb(struct folio *folio)\n \tfolio_put(folio);\n }\n \n-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason)\n+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,\n+\t\t\tenum migrate_reason reason)\n {\n \tstruct hstate *h = folio_hstate(old_folio);\n \ndiff --git a/mm/migrate.c b/mm/migrate.c\nindex d9b23909d716c6..49e10feeb094f0 100644\n--- a/mm/migrate.c\n+++ b/mm/migrate.c\n@@ -1469,7 +1469,7 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private,\n static int unmap_and_move_huge_page(new_folio_t get_new_folio,\n \t\tfree_folio_t put_new_folio, unsigned long private,\n \t\tstruct folio *src, int force, enum migrate_mode mode,\n-\t\tint reason, struct list_head *ret)\n+\t\tenum migrate_reason reason, struct list_head *ret)\n {\n \tstruct folio *dst;\n \tint rc = -EAGAIN;\n@@ -1626,7 +1626,7 @@ struct migrate_pages_stats {\n */\n static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,\n \t\t\t free_folio_t put_new_folio, unsigned long private,\n-\t\t\t enum migrate_mode mode, int reason,\n+\t\t\t enum migrate_mode mode, enum migrate_reason reason,\n \t\t\t struct migrate_pages_stats *stats,\n \t\t\t struct list_head *ret_folios)\n {\n@@ -1716,7 +1716,7 @@ static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,\n static void migrate_folios_move(struct list_head *src_folios,\n \t\tstruct list_head *dst_folios,\n \t\tfree_folio_t put_new_folio, unsigned long private,\n-\t\tenum migrate_mode mode, int reason,\n+\t\tenum migrate_mode mode, enum migrate_reason reason,\n \t\tstruct list_head *ret_folios,\n \t\tstruct migrate_pages_stats *stats,\n \t\tint *retry, int *thp_retry, int *nr_failed,\n@@ -1799,7 +1799,7 @@ static void migrate_folios_undo(struct list_head *src_folios,\n */\n static int migrate_pages_batch(struct list_head *from,\n \t\tnew_folio_t get_new_folio, free_folio_t put_new_folio,\n-\t\tunsigned long private, enum migrate_mode mode, int reason,\n+\t\tunsigned long private, enum migrate_mode mode, enum migrate_reason reason,\n \t\tstruct list_head *ret_folios, struct list_head *split_folios,\n \t\tstruct migrate_pages_stats *stats, int nr_pass)\n {\n@@ -2011,7 +2011,7 @@ static int migrate_pages_batch(struct list_head *from,\n \n static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,\n \t\tfree_folio_t put_new_folio, unsigned long private,\n-\t\tenum migrate_mode mode, int reason,\n+\t\tenum migrate_mode mode, enum migrate_reason reason,\n \t\tstruct list_head *ret_folios, struct list_head *split_folios,\n \t\tstruct migrate_pages_stats *stats)\n {\n@@ -2088,7 +2088,7 @@ static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,\n */\n int migrate_pages(struct list_head *from, new_folio_t get_new_folio,\n \t\tfree_folio_t put_new_folio, unsigned long private,\n-\t\tenum migrate_mode mode, int reason, unsigned int *ret_succeeded)\n+\t\tenum migrate_mode mode, enum migrate_reason reason, unsigned int *ret_succeeded)\n {\n \tint rc, rc_gather;\n \tint nr_pages;\ndiff --git a/mm/page_owner.c b/mm/page_owner.c\nindex 2dddcb6510aa1e..efbf67d54ee266 100644\n--- a/mm/page_owner.c\n+++ b/mm/page_owner.c\n@@ -339,13 +339,13 @@ noinline void __set_page_owner(struct page *page, unsigned short order,\n \tdepot_stack_handle_t handle;\n \n \thandle = save_stack(gfp_mask);\n-\t__update_page_owner_handle(page, handle, order, gfp_mask, -1,\n+\t__update_page_owner_handle(page, handle, order, gfp_mask, MR_NEVER,\n \t\t\t\t ts_nsec, current-\u003epid, current-\u003etgid,\n \t\t\t\t current-\u003ecomm);\n \tinc_stack_record_count(handle, gfp_mask, 1 \u003c\u003c order);\n }\n \n-void __folio_set_owner_migrate_reason(struct folio *folio, int reason)\n+void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)\n {\n \tstruct page_ext *page_ext = page_ext_get(\u0026folio-\u003epage);\n \tstruct page_owner *page_owner;\n@@ -422,6 +422,39 @@ void __folio_copy_owner(struct folio *newfolio, struct folio *old)\n \trcu_read_unlock();\n }\n \n+/*\n+ * Check if a page is a buddy page and advance @pfn past the entire buddy block.\n+ * This safely reads the buddy order without the zone lock, which may cause us\n+ * to skip less than the full buddy block, but that is acceptable for page owner\n+ * iteration purposes.\n+ *\n+ * The lockless read of buddy_order_unsafe() can also return a garbage order if\n+ * the page is concurrently allocated and PageBuddy is cleared between the check\n+ * and the read. Clamp the advance at the next MAX_ORDER_NR_PAGES boundary so\n+ * that a bogus order cannot carry @pfn into an unvalidated memory section,\n+ * which would break callers that rely on boundary-aligned pfn_valid() checks.\n+ *\n+ * Return: true if the page was skipped (caller should continue its loop),\n+ * false if the page is not a buddy page and should be processed normally.\n+ */\n+static inline bool skip_buddy_pages(unsigned long *pfn, struct page *page)\n+{\n+\tunsigned long order;\n+\n+\tif (!PageBuddy(page))\n+\t\treturn false;\n+\n+\torder = buddy_order_unsafe(page);\n+\tif (order \u003c= MAX_PAGE_ORDER) {\n+\t\tunsigned long new_pfn = *pfn + (1UL \u003c\u003c order);\n+\t\tunsigned long boundary = ALIGN(*pfn + 1, MAX_ORDER_NR_PAGES);\n+\n+\t\t*pfn = min(new_pfn, boundary) - 1;\n+\t}\n+\n+\treturn true;\n+}\n+\n void pagetypeinfo_showmixedcount_print(struct seq_file *m,\n \t\t\t\t pg_data_t *pgdat, struct zone *zone)\n {\n@@ -461,14 +494,8 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,\n \t\t\tif (page_zone(page) != zone)\n \t\t\t\tcontinue;\n \n-\t\t\tif (PageBuddy(page)) {\n-\t\t\t\tunsigned long freepage_order;\n-\n-\t\t\t\tfreepage_order = buddy_order_unsafe(page);\n-\t\t\t\tif (freepage_order \u003c= MAX_PAGE_ORDER)\n-\t\t\t\t\tpfn += (1UL \u003c\u003c freepage_order) - 1;\n+\t\t\tif (skip_buddy_pages(\u0026pfn, page))\n \t\t\t\tcontinue;\n-\t\t\t}\n \n \t\t\tif (PageReserved(page))\n \t\t\t\tcontinue;\n@@ -505,13 +532,13 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,\n \tseq_putc(m, '\\n');\n }\n \n+#ifdef CONFIG_MEMCG\n /*\n * Looking for memcg information and print it out\n */\n static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,\n \t\t\t\t\t struct page *page)\n {\n-#ifdef CONFIG_MEMCG\n \tunsigned long memcg_data;\n \tstruct mem_cgroup *memcg;\n \tbool online;\n@@ -534,15 +561,21 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,\n \tcgroup_name(memcg-\u003ecss.cgroup, name, sizeof(name));\n \tret += scnprintf(kbuf + ret, count - ret,\n \t\t\t\"Charged %sto %smemcg %s\\n\",\n-\t\t\tPageMemcgKmem(page) ? \"(via objcg) \" : \"\",\n+\t\t\t(memcg_data \u0026 MEMCG_DATA_KMEM) ? \"(via objcg) \" : \"\",\n \t\t\tonline ? \"\" : \"offline \",\n \t\t\tname);\n out_unlock:\n \trcu_read_unlock();\n-#endif /* CONFIG_MEMCG */\n \n \treturn ret;\n }\n+#else\n+static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,\n+\t\t\t\t\t struct page *page)\n+{\n+\treturn ret;\n+}\n+#endif\n \n static ssize_t\n print_page_owner(char __user *buf, size_t count, unsigned long pfn,\n@@ -579,7 +612,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,\n \tif (ret \u003e= count)\n \t\tgoto err;\n \n-\tif (page_owner-\u003elast_migrate_reason != -1) {\n+\tif (page_owner-\u003elast_migrate_reason != MR_NEVER) {\n \t\tret += scnprintf(kbuf + ret, count - ret,\n \t\t\t\"Page has been migrated, last migrate reason: %s\\n\",\n \t\t\tmigrate_reason_names[page_owner-\u003elast_migrate_reason]);\n@@ -630,10 +663,10 @@ void __dump_page_owner(const struct page *page)\n \telse\n \t\tpr_alert(\"page_owner tracks the page as freed\\n\");\n \n-\tpr_alert(\"page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu, free_ts %llu\\n\",\n+\tpr_alert(\"page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu\\n\",\n \t\t page_owner-\u003eorder, migratetype_names[mt], gfp_mask, \u0026gfp_mask,\n \t\t page_owner-\u003epid, page_owner-\u003etgid, page_owner-\u003ecomm,\n-\t\t page_owner-\u003ets_nsec, page_owner-\u003efree_ts_nsec);\n+\t\t page_owner-\u003ets_nsec);\n \n \thandle = READ_ONCE(page_owner-\u003ehandle);\n \tif (!handle)\n@@ -645,12 +678,13 @@ void __dump_page_owner(const struct page *page)\n \tif (!handle) {\n \t\tpr_alert(\"page_owner free stack trace missing\\n\");\n \t} else {\n-\t\tpr_alert(\"page last free pid %d tgid %d stack trace:\\n\",\n-\t\t\t page_owner-\u003efree_pid, page_owner-\u003efree_tgid);\n+\t\tpr_alert(\"page last free pid %d tgid %d ts %llu stack trace:\\n\",\n+\t\t\t page_owner-\u003efree_pid, page_owner-\u003efree_tgid,\n+\t\t\t page_owner-\u003efree_ts_nsec);\n \t\tstack_depot_print(handle);\n \t}\n \n-\tif (page_owner-\u003elast_migrate_reason != -1)\n+\tif (page_owner-\u003elast_migrate_reason != MR_NEVER)\n \t\tpr_alert(\"page has been migrated, last migrate reason: %s\\n\",\n \t\t\tmigrate_reason_names[page_owner-\u003elast_migrate_reason]);\n \tpage_ext_put(page_ext);\n@@ -697,13 +731,8 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)\n \t\t}\n \n \t\tpage = pfn_to_page(pfn);\n-\t\tif (PageBuddy(page)) {\n-\t\t\tunsigned long freepage_order = buddy_order_unsafe(page);\n-\n-\t\t\tif (freepage_order \u003c= MAX_PAGE_ORDER)\n-\t\t\t\tpfn += (1UL \u003c\u003c freepage_order) - 1;\n+\t\tif (skip_buddy_pages(\u0026pfn, page))\n \t\t\tcontinue;\n-\t\t}\n \n \t\tpage_ext = page_ext_get(page);\n \t\tif (unlikely(!page_ext))\n@@ -798,20 +827,8 @@ static void init_pages_in_zone(struct zone *zone)\n \t\t\tif (page_zone(page) != zone)\n \t\t\t\tcontinue;\n \n-\t\t\t/*\n-\t\t\t * To avoid having to grab zone-\u003elock, be a little\n-\t\t\t * careful when reading buddy page order. The only\n-\t\t\t * danger is that we skip too much and potentially miss\n-\t\t\t * some early allocated pages, which is better than\n-\t\t\t * heavy lock contention.\n-\t\t\t */\n-\t\t\tif (PageBuddy(page)) {\n-\t\t\t\tunsigned long order = buddy_order_unsafe(page);\n-\n-\t\t\t\tif (order \u003e 0 \u0026\u0026 order \u003c= MAX_PAGE_ORDER)\n-\t\t\t\t\tpfn += (1UL \u003c\u003c order) - 1;\n+\t\t\tif (skip_buddy_pages(\u0026pfn, page))\n \t\t\t\tcontinue;\n-\t\t\t}\n \n \t\t\tif (PageReserved(page))\n \t\t\t\tcontinue;\n@@ -826,7 +843,7 @@ static void init_pages_in_zone(struct zone *zone)\n \n \t\t\t/* Found early allocated page */\n \t\t\t__update_page_owner_handle(page, early_handle, 0, 0,\n-\t\t\t\t\t\t -1, local_clock(), current-\u003epid,\n+\t\t\t\t\t\t MR_NEVER, local_clock(), current-\u003epid,\n \t\t\t\t\t\t current-\u003etgid, current-\u003ecomm);\n \t\t\tcount++;\n ext_put_continue:\n@@ -887,7 +904,7 @@ static void *stack_next(struct seq_file *m, void *v, loff_t *ppos)\n \treturn stack;\n }\n \n-static unsigned long page_owner_pages_threshold;\n+static unsigned long pages_threshold;\n \n static int stack_print(struct seq_file *m, void *v)\n {\n@@ -904,7 +921,7 @@ static int stack_print(struct seq_file *m, void *v)\n \tnr_base_pages = refcount_read(\u0026stack_record-\u003ecount) - 1;\n \n \tif (ctx-\u003eflags \u0026 STACK_PRINT_FLAG_PAGES \u0026\u0026\n-\t (nr_base_pages \u003c 1 || nr_base_pages \u003c page_owner_pages_threshold))\n+\t (nr_base_pages \u003c 1 || nr_base_pages \u003c pages_threshold))\n \t\treturn 0;\n \n \tif (ctx-\u003eflags \u0026 STACK_PRINT_FLAG_STACK) {\n@@ -926,16 +943,16 @@ static void stack_stop(struct seq_file *m, void *v)\n {\n }\n \n-static const struct seq_operations page_owner_stack_op = {\n+static const struct seq_operations stack_op = {\n \t.start\t= stack_start,\n \t.next\t= stack_next,\n \t.stop\t= stack_stop,\n \t.show\t= stack_print\n };\n \n-static int page_owner_stack_open(struct inode *inode, struct file *file)\n+static int stack_open(struct inode *inode, struct file *file)\n {\n-\tint ret = seq_open_private(file, \u0026page_owner_stack_op,\n+\tint ret = seq_open_private(file, \u0026stack_op,\n \t\t\t\t sizeof(struct stack_print_ctx));\n \n \tif (!ret) {\n@@ -948,28 +965,26 @@ static int page_owner_stack_open(struct inode *inode, struct file *file)\n \treturn ret;\n }\n \n-static const struct file_operations page_owner_stack_fops = {\n-\t.open\t\t= page_owner_stack_open,\n+static const struct file_operations stack_fops = {\n+\t.open\t\t= stack_open,\n \t.read\t\t= seq_read,\n \t.llseek\t\t= seq_lseek,\n \t.release\t= seq_release_private,\n };\n \n-static int page_owner_threshold_get(void *data, u64 *val)\n+static int threshold_get(void *data, u64 *val)\n {\n-\t*val = READ_ONCE(page_owner_pages_threshold);\n+\t*val = READ_ONCE(pages_threshold);\n \treturn 0;\n }\n \n-static int page_owner_threshold_set(void *data, u64 val)\n+static int threshold_set(void *data, u64 val)\n {\n-\tWRITE_ONCE(page_owner_pages_threshold, val);\n+\tWRITE_ONCE(pages_threshold, val);\n \treturn 0;\n }\n \n-DEFINE_SIMPLE_ATTRIBUTE(page_owner_threshold_fops, \u0026page_owner_threshold_get,\n-\t\t\t\u0026page_owner_threshold_set, \"%llu\");\n-\n+DEFINE_SIMPLE_ATTRIBUTE(threshold_fops, \u0026threshold_get, \u0026threshold_set, \"%llu\\n\");\n \n static int __init pageowner_init(void)\n {\n@@ -985,17 +1000,17 @@ static int __init pageowner_init(void)\n \tdebugfs_create_file(\"show_stacks\", 0400, dir,\n \t\t\t (void *)(STACK_PRINT_FLAG_STACK |\n \t\t\t\t STACK_PRINT_FLAG_PAGES),\n-\t\t\t \u0026page_owner_stack_fops);\n+\t\t\t \u0026stack_fops);\n \tdebugfs_create_file(\"show_handles\", 0400, dir,\n \t\t\t (void *)(STACK_PRINT_FLAG_HANDLE |\n \t\t\t\t STACK_PRINT_FLAG_PAGES),\n-\t\t\t \u0026page_owner_stack_fops);\n+\t\t\t \u0026stack_fops);\n \tdebugfs_create_file(\"show_stacks_handles\", 0400, dir,\n \t\t\t (void *)(STACK_PRINT_FLAG_STACK |\n \t\t\t\t STACK_PRINT_FLAG_HANDLE),\n-\t\t\t \u0026page_owner_stack_fops);\n+\t\t\t \u0026stack_fops);\n \tdebugfs_create_file(\"count_threshold\", 0600, dir, NULL,\n-\t\t\t \u0026page_owner_threshold_fops);\n+\t\t\t \u0026threshold_fops);\n \treturn 0;\n }\n late_initcall(pageowner_init)\ndiff --git a/scripts/gdb/linux/page_owner.py b/scripts/gdb/linux/page_owner.py\nindex 8e713a09cfe78f..eeabaeed438b2a 100644\n--- a/scripts/gdb/linux/page_owner.py\n+++ b/scripts/gdb/linux/page_owner.py\n@@ -34,6 +34,7 @@ class DumpPageOwner(gdb.Command):\n max_pfn = None\n p_ops = None\n migrate_reason_names = None\n+ mr_never = None\n \n def __init__(self):\n super(DumpPageOwner, self).__init__(\"lx-dump-page-owner\", gdb.COMMAND_SUPPORT)\n@@ -65,6 +66,7 @@ class DumpPageOwner(gdb.Command):\n self.max_pfn = int(gdb.parse_and_eval(\"max_pfn\"))\n self.page_ext_size = int(gdb.parse_and_eval(\"page_ext_size\"))\n self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names')\n+ self.mr_never = int(gdb.parse_and_eval('MR_NEVER'))\n \n def page_ext_invalid(self, page_ext):\n if page_ext == gdb.Value(0):\n@@ -138,7 +140,7 @@ class DumpPageOwner(gdb.Command):\n else:\n gdb.write('page last free stack trace:\\n')\n stackdepot.stack_depot_print(page_owner[\"free_handle\"])\n- if page_owner['last_migrate_reason'] != -1:\n+ if page_owner['last_migrate_reason'] != self.mr_never:\n gdb.write('page has been migrated, last migrate reason: %s\\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])\n \n def read_page_owner(self):\n"
} |
| 2/1 | 2026/07/01 11:12 | agent | patch-evaluator |
1mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"PAGE_OWNER"
],
"FocusSymbols": [
"read_page_owner",
"pagetypeinfo_showmixedcount_print"
],
"Reasoning": "The patch introduces a functional change in mm/page_owner.c to safely skip buddy pages during iteration, preventing potential out-of-bounds PFN accesses when reading /sys/kernel/debug/page_owner or /proc/pagetypeinfo. It also fixes memcg data reading in page owner.",
"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 3d436bf690ea0e60c36a6cee9b3235fc2bbe8c99
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 1 11:12:36 2026 +0000
syz-cluster: applied patch under review
diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
index 2abaf99321e90d..fa828232dfcc18 100644
--- a/include/linux/hugetlb.h
+++ b/include/linux/hugetlb.h
@@ -154,7 +154,8 @@ long hugetlb_unreserve_pages(struct inode *inode, long start, long end,
bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list);
int get_hwpoison_hugetlb_folio(struct folio *folio, bool *hugetlb, bool unpoison);
void folio_putback_hugetlb(struct folio *folio);
-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason);
+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
+ enum migrate_reason reason);
void hugetlb_fix_reserve_counts(struct inode *inode);
extern struct mutex *hugetlb_fault_mutex_table;
u32 hugetlb_fault_mutex_hash(struct address_space *mapping, pgoff_t idx);
@@ -424,7 +425,7 @@ static inline void folio_putback_hugetlb(struct folio *folio)
}
static inline void move_hugetlb_state(struct folio *old_folio,
- struct folio *new_folio, int reason)
+ struct folio *new_folio, enum migrate_reason reason)
{
}
@@ -956,7 +957,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)
return modified_mask;
}
-static inline bool htlb_allow_alloc_fallback(int reason)
+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)
{
bool allowed_fallback = false;
@@ -1238,7 +1239,7 @@ static inline gfp_t htlb_modify_alloc_mask(struct hstate *h, gfp_t gfp_mask)
return 0;
}
-static inline bool htlb_allow_alloc_fallback(int reason)
+static inline bool htlb_allow_alloc_fallback(enum migrate_reason reason)
{
return false;
}
diff --git a/include/linux/migrate.h b/include/linux/migrate.h
index d5af2b7f577b33..1f83924615d6d3 100644
--- a/include/linux/migrate.h
+++ b/include/linux/migrate.h
@@ -57,7 +57,8 @@ void putback_movable_pages(struct list_head *l);
int migrate_folio(struct address_space *mapping, struct folio *dst,
struct folio *src, enum migrate_mode mode);
int migrate_pages(struct list_head *l, new_folio_t new, free_folio_t free,
- unsigned long private, enum migrate_mode mode, int reason,
+ unsigned long private, enum migrate_mode mode,
+ enum migrate_reason reason,
unsigned int *ret_succeeded);
struct folio *alloc_migration_target(struct folio *src, unsigned long private);
bool isolate_movable_ops_page(struct page *page, isolate_mode_t mode);
@@ -77,7 +78,8 @@ int set_movable_ops(const struct movable_operations *ops, enum pagetype type);
static inline void putback_movable_pages(struct list_head *l) {}
static inline int migrate_pages(struct list_head *l, new_folio_t new,
free_folio_t free, unsigned long private,
- enum migrate_mode mode, int reason, unsigned int *ret_succeeded)
+ enum migrate_mode mode, enum migrate_reason reason,
+ unsigned int *ret_succeeded)
{ return -ENOSYS; }
static inline struct folio *alloc_migration_target(struct folio *src,
unsigned long private)
diff --git a/include/linux/migrate_mode.h b/include/linux/migrate_mode.h
index 265c4328b36ad5..05102d4d2490e6 100644
--- a/include/linux/migrate_mode.h
+++ b/include/linux/migrate_mode.h
@@ -25,6 +25,7 @@ enum migrate_reason {
MR_LONGTERM_PIN,
MR_DEMOTION,
MR_DAMON,
+ MR_NEVER, /* page has never been migrated */
MR_TYPES
};
diff --git a/include/linux/page_owner.h b/include/linux/page_owner.h
index 3328357f6dba72..9fe51dfccf2685 100644
--- a/include/linux/page_owner.h
+++ b/include/linux/page_owner.h
@@ -3,6 +3,7 @@
#define __LINUX_PAGE_OWNER_H
#include <linux/jump_label.h>
+#include <linux/migrate_mode.h>
#ifdef CONFIG_PAGE_OWNER
extern struct static_key_false page_owner_inited;
@@ -14,7 +15,7 @@ extern void __set_page_owner(struct page *page,
extern void __split_page_owner(struct page *page, int old_order,
int new_order);
extern void __folio_copy_owner(struct folio *newfolio, struct folio *old);
-extern void __folio_set_owner_migrate_reason(struct folio *folio, int reason);
+extern void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason);
extern void __dump_page_owner(const struct page *page);
extern void pagetypeinfo_showmixedcount_print(struct seq_file *m,
pg_data_t *pgdat, struct zone *zone);
@@ -43,7 +44,7 @@ static inline void folio_copy_owner(struct folio *newfolio, struct folio *old)
if (static_branch_unlikely(&page_owner_inited))
__folio_copy_owner(newfolio, old);
}
-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)
+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
{
if (static_branch_unlikely(&page_owner_inited))
__folio_set_owner_migrate_reason(folio, reason);
@@ -68,7 +69,7 @@ static inline void split_page_owner(struct page *page, int old_order,
static inline void folio_copy_owner(struct folio *newfolio, struct folio *folio)
{
}
-static inline void folio_set_owner_migrate_reason(struct folio *folio, int reason)
+static inline void folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
{
}
static inline void dump_page_owner(const struct page *page)
diff --git a/include/trace/events/migrate.h b/include/trace/events/migrate.h
index cd01dd7b3640dc..15ee2ef201b5f9 100644
--- a/include/trace/events/migrate.h
+++ b/include/trace/events/migrate.h
@@ -23,7 +23,8 @@
EM( MR_CONTIG_RANGE, "contig_range") \
EM( MR_LONGTERM_PIN, "longterm_pin") \
EM( MR_DEMOTION, "demotion") \
- EMe(MR_DAMON, "damon")
+ EM( MR_DAMON, "damon") \
+ EMe(MR_NEVER, "never_migrated")
/*
* First define the enums in the above macros to be exported to userspace
@@ -51,7 +52,7 @@ TRACE_EVENT(mm_migrate_pages,
TP_PROTO(unsigned long succeeded, unsigned long failed,
unsigned long thp_succeeded, unsigned long thp_failed,
unsigned long thp_split, unsigned long large_folio_split,
- enum migrate_mode mode, int reason),
+ enum migrate_mode mode, enum migrate_reason reason),
TP_ARGS(succeeded, failed, thp_succeeded, thp_failed,
thp_split, large_folio_split, mode, reason),
@@ -64,7 +65,7 @@ TRACE_EVENT(mm_migrate_pages,
__field( unsigned long, thp_split)
__field( unsigned long, large_folio_split)
__field( enum migrate_mode, mode)
- __field( int, reason)
+ __field( enum migrate_reason, reason)
),
TP_fast_assign(
@@ -91,13 +92,13 @@ TRACE_EVENT(mm_migrate_pages,
TRACE_EVENT(mm_migrate_pages_start,
- TP_PROTO(enum migrate_mode mode, int reason),
+ TP_PROTO(enum migrate_mode mode, enum migrate_reason reason),
TP_ARGS(mode, reason),
TP_STRUCT__entry(
__field(enum migrate_mode, mode)
- __field(int, reason)
+ __field(enum migrate_reason, reason)
),
TP_fast_assign(
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 571212b80835ec..17732d1fdc5efd 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -7182,7 +7182,8 @@ void folio_putback_hugetlb(struct folio *folio)
folio_put(folio);
}
-void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio, int reason)
+void move_hugetlb_state(struct folio *old_folio, struct folio *new_folio,
+ enum migrate_reason reason)
{
struct hstate *h = folio_hstate(old_folio);
diff --git a/mm/migrate.c b/mm/migrate.c
index d9b23909d716c6..49e10feeb094f0 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1469,7 +1469,7 @@ static int migrate_folio_move(free_folio_t put_new_folio, unsigned long private,
static int unmap_and_move_huge_page(new_folio_t get_new_folio,
free_folio_t put_new_folio, unsigned long private,
struct folio *src, int force, enum migrate_mode mode,
- int reason, struct list_head *ret)
+ enum migrate_reason reason, struct list_head *ret)
{
struct folio *dst;
int rc = -EAGAIN;
@@ -1626,7 +1626,7 @@ struct migrate_pages_stats {
*/
static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,
free_folio_t put_new_folio, unsigned long private,
- enum migrate_mode mode, int reason,
+ enum migrate_mode mode, enum migrate_reason reason,
struct migrate_pages_stats *stats,
struct list_head *ret_folios)
{
@@ -1716,7 +1716,7 @@ static int migrate_hugetlbs(struct list_head *from, new_folio_t get_new_folio,
static void migrate_folios_move(struct list_head *src_folios,
struct list_head *dst_folios,
free_folio_t put_new_folio, unsigned long private,
- enum migrate_mode mode, int reason,
+ enum migrate_mode mode, enum migrate_reason reason,
struct list_head *ret_folios,
struct migrate_pages_stats *stats,
int *retry, int *thp_retry, int *nr_failed,
@@ -1799,7 +1799,7 @@ static void migrate_folios_undo(struct list_head *src_folios,
*/
static int migrate_pages_batch(struct list_head *from,
new_folio_t get_new_folio, free_folio_t put_new_folio,
- unsigned long private, enum migrate_mode mode, int reason,
+ unsigned long private, enum migrate_mode mode, enum migrate_reason reason,
struct list_head *ret_folios, struct list_head *split_folios,
struct migrate_pages_stats *stats, int nr_pass)
{
@@ -2011,7 +2011,7 @@ static int migrate_pages_batch(struct list_head *from,
static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,
free_folio_t put_new_folio, unsigned long private,
- enum migrate_mode mode, int reason,
+ enum migrate_mode mode, enum migrate_reason reason,
struct list_head *ret_folios, struct list_head *split_folios,
struct migrate_pages_stats *stats)
{
@@ -2088,7 +2088,7 @@ static int migrate_pages_sync(struct list_head *from, new_folio_t get_new_folio,
*/
int migrate_pages(struct list_head *from, new_folio_t get_new_folio,
free_folio_t put_new_folio, unsigned long private,
- enum migrate_mode mode, int reason, unsigned int *ret_succeeded)
+ enum migrate_mode mode, enum migrate_reason reason, unsigned int *ret_succeeded)
{
int rc, rc_gather;
int nr_pages;
diff --git a/mm/page_owner.c b/mm/page_owner.c
index 2dddcb6510aa1e..efbf67d54ee266 100644
--- a/mm/page_owner.c
+++ b/mm/page_owner.c
@@ -339,13 +339,13 @@ noinline void __set_page_owner(struct page *page, unsigned short order,
depot_stack_handle_t handle;
handle = save_stack(gfp_mask);
- __update_page_owner_handle(page, handle, order, gfp_mask, -1,
+ __update_page_owner_handle(page, handle, order, gfp_mask, MR_NEVER,
ts_nsec, current->pid, current->tgid,
current->comm);
inc_stack_record_count(handle, gfp_mask, 1 << order);
}
-void __folio_set_owner_migrate_reason(struct folio *folio, int reason)
+void __folio_set_owner_migrate_reason(struct folio *folio, enum migrate_reason reason)
{
struct page_ext *page_ext = page_ext_get(&folio->page);
struct page_owner *page_owner;
@@ -422,6 +422,39 @@ void __folio_copy_owner(struct folio *newfolio, struct folio *old)
rcu_read_unlock();
}
+/*
+ * Check if a page is a buddy page and advance @pfn past the entire buddy block.
+ * This safely reads the buddy order without the zone lock, which may cause us
+ * to skip less than the full buddy block, but that is acceptable for page owner
+ * iteration purposes.
+ *
+ * The lockless read of buddy_order_unsafe() can also return a garbage order if
+ * the page is concurrently allocated and PageBuddy is cleared between the check
+ * and the read. Clamp the advance at the next MAX_ORDER_NR_PAGES boundary so
+ * that a bogus order cannot carry @pfn into an unvalidated memory section,
+ * which would break callers that rely on boundary-aligned pfn_valid() checks.
+ *
+ * Return: true if the page was skipped (caller should continue its loop),
+ * false if the page is not a buddy page and should be processed normally.
+ */
+static inline bool skip_buddy_pages(unsigned long *pfn, struct page *page)
+{
+ unsigned long order;
+
+ if (!PageBuddy(page))
+ return false;
+
+ order = buddy_order_unsafe(page);
+ if (order <= MAX_PAGE_ORDER) {
+ unsigned long new_pfn = *pfn + (1UL << order);
+ unsigned long boundary = ALIGN(*pfn + 1, MAX_ORDER_NR_PAGES);
+
+ *pfn = min(new_pfn, boundary) - 1;
+ }
+
+ return true;
+}
+
void pagetypeinfo_showmixedcount_print(struct seq_file *m,
pg_data_t *pgdat, struct zone *zone)
{
@@ -461,14 +494,8 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,
if (page_zone(page) != zone)
continue;
- if (PageBuddy(page)) {
- unsigned long freepage_order;
-
- freepage_order = buddy_order_unsafe(page);
- if (freepage_order <= MAX_PAGE_ORDER)
- pfn += (1UL << freepage_order) - 1;
+ if (skip_buddy_pages(&pfn, page))
continue;
- }
if (PageReserved(page))
continue;
@@ -505,13 +532,13 @@ void pagetypeinfo_showmixedcount_print(struct seq_file *m,
seq_putc(m, '\n');
}
+#ifdef CONFIG_MEMCG
/*
* Looking for memcg information and print it out
*/
static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
struct page *page)
{
-#ifdef CONFIG_MEMCG
unsigned long memcg_data;
struct mem_cgroup *memcg;
bool online;
@@ -534,15 +561,21 @@ static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
cgroup_name(memcg->css.cgroup, name, sizeof(name));
ret += scnprintf(kbuf + ret, count - ret,
"Charged %sto %smemcg %s\n",
- PageMemcgKmem(page) ? "(via objcg) " : "",
+ (memcg_data & MEMCG_DATA_KMEM) ? "(via objcg) " : "",
online ? "" : "offline ",
name);
out_unlock:
rcu_read_unlock();
-#endif /* CONFIG_MEMCG */
return ret;
}
+#else
+static inline int print_page_owner_memcg(char *kbuf, size_t count, int ret,
+ struct page *page)
+{
+ return ret;
+}
+#endif
static ssize_t
print_page_owner(char __user *buf, size_t count, unsigned long pfn,
@@ -579,7 +612,7 @@ print_page_owner(char __user *buf, size_t count, unsigned long pfn,
if (ret >= count)
goto err;
- if (page_owner->last_migrate_reason != -1) {
+ if (page_owner->last_migrate_reason != MR_NEVER) {
ret += scnprintf(kbuf + ret, count - ret,
"Page has been migrated, last migrate reason: %s\n",
migrate_reason_names[page_owner->last_migrate_reason]);
@@ -630,10 +663,10 @@ void __dump_page_owner(const struct page *page)
else
pr_alert("page_owner tracks the page as freed\n");
- pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu, free_ts %llu\n",
+ pr_alert("page last allocated via order %u, migratetype %s, gfp_mask %#x(%pGg), pid %d, tgid %d (%s), ts %llu\n",
page_owner->order, migratetype_names[mt], gfp_mask, &gfp_mask,
page_owner->pid, page_owner->tgid, page_owner->comm,
- page_owner->ts_nsec, page_owner->free_ts_nsec);
+ page_owner->ts_nsec);
handle = READ_ONCE(page_owner->handle);
if (!handle)
@@ -645,12 +678,13 @@ void __dump_page_owner(const struct page *page)
if (!handle) {
pr_alert("page_owner free stack trace missing\n");
} else {
- pr_alert("page last free pid %d tgid %d stack trace:\n",
- page_owner->free_pid, page_owner->free_tgid);
+ pr_alert("page last free pid %d tgid %d ts %llu stack trace:\n",
+ page_owner->free_pid, page_owner->free_tgid,
+ page_owner->free_ts_nsec);
stack_depot_print(handle);
}
- if (page_owner->last_migrate_reason != -1)
+ if (page_owner->last_migrate_reason != MR_NEVER)
pr_alert("page has been migrated, last migrate reason: %s\n",
migrate_reason_names[page_owner->last_migrate_reason]);
page_ext_put(page_ext);
@@ -697,13 +731,8 @@ read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)
}
page = pfn_to_page(pfn);
- if (PageBuddy(page)) {
- unsigned long freepage_order = buddy_order_unsafe(page);
-
- if (freepage_order <= MAX_PAGE_ORDER)
- pfn += (1UL << freepage_order) - 1;
+ if (skip_buddy_pages(&pfn, page))
continue;
- }
page_ext = page_ext_get(page);
if (unlikely(!page_ext))
@@ -798,20 +827,8 @@ static void init_pages_in_zone(struct zone *zone)
if (page_zone(page) != zone)
continue;
- /*
- * To avoid having to grab zone->lock, be a little
- * careful when reading buddy page order. The only
- * danger is that we skip too much and potentially miss
- * some early allocated pages, which is better than
- * heavy lock contention.
- */
- if (PageBuddy(page)) {
- unsigned long order = buddy_order_unsafe(page);
-
- if (order > 0 && order <= MAX_PAGE_ORDER)
- pfn += (1UL << order) - 1;
+ if (skip_buddy_pages(&pfn, page))
continue;
- }
if (PageReserved(page))
continue;
@@ -826,7 +843,7 @@ static void init_pages_in_zone(struct zone *zone)
/* Found early allocated page */
__update_page_owner_handle(page, early_handle, 0, 0,
- -1, local_clock(), current->pid,
+ MR_NEVER, local_clock(), current->pid,
current->tgid, current->comm);
count++;
ext_put_continue:
@@ -887,7 +904,7 @@ static void *stack_next(struct seq_file *m, void *v, loff_t *ppos)
return stack;
}
-static unsigned long page_owner_pages_threshold;
+static unsigned long pages_threshold;
static int stack_print(struct seq_file *m, void *v)
{
@@ -904,7 +921,7 @@ static int stack_print(struct seq_file *m, void *v)
nr_base_pages = refcount_read(&stack_record->count) - 1;
if (ctx->flags & STACK_PRINT_FLAG_PAGES &&
- (nr_base_pages < 1 || nr_base_pages < page_owner_pages_threshold))
+ (nr_base_pages < 1 || nr_base_pages < pages_threshold))
return 0;
if (ctx->flags & STACK_PRINT_FLAG_STACK) {
@@ -926,16 +943,16 @@ static void stack_stop(struct seq_file *m, void *v)
{
}
-static const struct seq_operations page_owner_stack_op = {
+static const struct seq_operations stack_op = {
.start = stack_start,
.next = stack_next,
.stop = stack_stop,
.show = stack_print
};
-static int page_owner_stack_open(struct inode *inode, struct file *file)
+static int stack_open(struct inode *inode, struct file *file)
{
- int ret = seq_open_private(file, &page_owner_stack_op,
+ int ret = seq_open_private(file, &stack_op,
sizeof(struct stack_print_ctx));
if (!ret) {
@@ -948,28 +965,26 @@ static int page_owner_stack_open(struct inode *inode, struct file *file)
return ret;
}
-static const struct file_operations page_owner_stack_fops = {
- .open = page_owner_stack_open,
+static const struct file_operations stack_fops = {
+ .open = stack_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
};
-static int page_owner_threshold_get(void *data, u64 *val)
+static int threshold_get(void *data, u64 *val)
{
- *val = READ_ONCE(page_owner_pages_threshold);
+ *val = READ_ONCE(pages_threshold);
return 0;
}
-static int page_owner_threshold_set(void *data, u64 val)
+static int threshold_set(void *data, u64 val)
{
- WRITE_ONCE(page_owner_pages_threshold, val);
+ WRITE_ONCE(pages_threshold, val);
return 0;
}
-DEFINE_SIMPLE_ATTRIBUTE(page_owner_threshold_fops, &page_owner_threshold_get,
- &page_owner_threshold_set, "%llu");
-
+DEFINE_SIMPLE_ATTRIBUTE(threshold_fops, &threshold_get, &threshold_set, "%llu\n");
static int __init pageowner_init(void)
{
@@ -985,17 +1000,17 @@ static int __init pageowner_init(void)
debugfs_create_file("show_stacks", 0400, dir,
(void *)(STACK_PRINT_FLAG_STACK |
STACK_PRINT_FLAG_PAGES),
- &page_owner_stack_fops);
+ &stack_fops);
debugfs_create_file("show_handles", 0400, dir,
(void *)(STACK_PRINT_FLAG_HANDLE |
STACK_PRINT_FLAG_PAGES),
- &page_owner_stack_fops);
+ &stack_fops);
debugfs_create_file("show_stacks_handles", 0400, dir,
(void *)(STACK_PRINT_FLAG_STACK |
STACK_PRINT_FLAG_HANDLE),
- &page_owner_stack_fops);
+ &stack_fops);
debugfs_create_file("count_threshold", 0600, dir, NULL,
- &page_owner_threshold_fops);
+ &threshold_fops);
return 0;
}
late_initcall(pageowner_init)
diff --git a/scripts/gdb/linux/page_owner.py b/scripts/gdb/linux/page_owner.py
index 8e713a09cfe78f..eeabaeed438b2a 100644
--- a/scripts/gdb/linux/page_owner.py
+++ b/scripts/gdb/linux/page_owner.py
@@ -34,6 +34,7 @@ class DumpPageOwner(gdb.Command):
max_pfn = None
p_ops = None
migrate_reason_names = None
+ mr_never = None
def __init__(self):
super(DumpPageOwner, self).__init__("lx-dump-page-owner", gdb.COMMAND_SUPPORT)
@@ -65,6 +66,7 @@ class DumpPageOwner(gdb.Command):
self.max_pfn = int(gdb.parse_and_eval("max_pfn"))
self.page_ext_size = int(gdb.parse_and_eval("page_ext_size"))
self.migrate_reason_names = gdb.parse_and_eval('migrate_reason_names')
+ self.mr_never = int(gdb.parse_and_eval('MR_NEVER'))
def page_ext_invalid(self, page_ext):
if page_ext == gdb.Value(0):
@@ -138,7 +140,7 @@ class DumpPageOwner(gdb.Command):
else:
gdb.write('page last free stack trace:\n')
stackdepot.stack_depot_print(page_owner["free_handle"])
- if page_owner['last_migrate_reason'] != -1:
+ if page_owner['last_migrate_reason'] != self.mr_never:
gdb.write('page has been migrated, last migrate reason: %s\n' % self.migrate_reason_names[page_owner['last_migrate_reason']])
def read_page_owner(self):
|
| 3/2 | 2026/07/01 11:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 9581 output: 32 thoughts: 1041 Thoughts: **Fuzzing Decision for the Kernel Patch**
Okay, let's break this down. My initial assessment is that we need to determine if this patch introduces any functional changes that are suitable for fuzzing. The affected files are: `include/linux/hugetlb.h`, `include/linux/migrate.h`, `include/linux/migrate_mode.h`, `include/linux/page_owner.h`, `trace/events/migrate.h`, `mm/hugetlb.c`, `mm/migrate.c`, `mm/page_owner.c`, and `scripts/gdb/linux/page_owner.py`. That's a decent spread.
Looking at the code, it seems there's a good amount of refactoring. Parameter signatures for functions like `move_hugetlb_state`, `htlb_allow_alloc_fallback`, `migrate_pages`, and `__folio_set_owner_migrate_reason` are changing from `int reason` to `enum migrate_reason reason`. The `MR_NEVER` value is being added to that enum. Tracepoints are being updated to use the new enum as well. That's good practice. Clean up.
Diving deeper into `mm/page_owner.c`, there are several changes: replacing `-1` with `MR_NEVER`, a new helper function `skip_buddy_pages`, and updates to functions like `pagetypeinfo_showmixedcount_print`, `read_page_owner`, `init_pages_in_zone`, `print_page_owner_memcg`, `print_page_owner`, and `__dump_page_owner`. Plus, some renaming of variables. The use of `MEMCG_DATA_KMEM` instead of `PageMemcgKmem` in `print_page_owner_memcg` is something I need to keep in mind, because that's likely a significant change in how memory cgroups are being handled. I should probably double-check that `PageMemcgKmem` wasn't just removed and replaced.
The key thing is this `skip_buddy_pages` function. It looks like the main functional change in `mm/page_owner.c`. This is where the fuzzing relevance comes in. Now, is it reachable and fuzz-worthy?
`mm/page_owner.c` is controlled by `CONFIG_PAGE_OWNER`. Okay, so it's a debug feature. `pagetypeinfo_showmixedcount_print` is used in `/proc/pagetypeinfo`, and `read_page_owner` is used for `/sys/kernel/debug/page_owner`, and `init_pages_in_zone` is called during initialization.
So, is this worth fuzzing? Mostly, these changes seem like refactoring and fixing some debugfs and procfs output, along with an optimization/fix for buddy page skipping in page_owner. This debugging code is exposed in proc and debugfs, so user space interaction is possible.
That `skip_buddy_pages` function logic:
```c
static inline bool skip_buddy_pages(unsigned long *pfn, struct page *page)
{
unsigned long order;
if (!PageBuddy(page))
return false;
order = buddy_order_unsafe(page);
if (order <= MAX_PAGE_ORDER) {
unsigned long new_pfn = *pfn + (1UL << order);
unsigned long boundary = ALIGN(*pfn + 1, MAX_ORDER_NR_PAGES);
*pfn = min(new_pfn, boundary) - 1;
}
return true;
}
```
This *could* prevent out-of-bounds reads or even an infinite loop when interacting with `/sys/kernel/debug/page_owner` or `/proc/pagetypeinfo`. This is a *functional* change within a debug feature. It's an important fix.
Oh, and `skip_buddy_pages` is actually used in `read_page_owner`, a *file read* operation. Yes, this is definitely fuzzable.
|
| 4/2 | 2026/07/01 11:12 | tool | grepper |
0mArgs:{
"Expression": "read_page_owner",
"PathPrefix": "mm/page_owner.c"
}Results: {
"Output": "mm/page_owner.c=693=static ssize_t\nmm/page_owner.c:694:read_page_owner(struct file *file, char __user *buf, size_t count, loff_t *ppos)\nmm/page_owner.c-695-{\n--\nmm/page_owner.c=867=static const struct file_operations page_owner_fops = {\nmm/page_owner.c:868:\t.read\t\t= read_page_owner,\nmm/page_owner.c-869-\t.llseek\t\t= lseek_page_owner,\n"
} |
| 5/2 | 2026/07/01 11:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 11716 output: 29 thoughts: 55 Thoughts: Okay, here's my take on those thoughts, framed as my own mental process: **Diving into Page Owner & Pagetypeinfo** Right, so I'm seeing this `/sys/kernel/debug/page_owner` trigger something. Interesting. And it looks like `pagetypeinfo_showmixedcount_print` plays a role in `/proc/pagetypeinfo`. Given my experience, the connection is pretty clear. These are likely related to tracking and reporting on memory allocation and page types, particularly mixed pages. Now I need to dive into `pagetypeinfo_showmixedcount_print` specifically. That function is probably where the core logic resides for displaying the counts of mixed page types, which is essential information when you're dealing with memory fragmentation and trying to understand how the kernel is using different page sizes. Let's see what's really happening in that function. |
| 6/2 | 2026/07/01 11:12 | tool | grepper |
0mArgs:{
"Expression": "pagetypeinfo_showmixedcount_print",
"PathPrefix": ""
}Results: {
"Output": "include/linux/page_owner.h=19=extern void __dump_page_owner(const struct page *page);\ninclude/linux/page_owner.h:20:extern void pagetypeinfo_showmixedcount_print(struct seq_file *m,\ninclude/linux/page_owner.h-21-\t\t\t\t\tpg_data_t *pgdat, struct zone *zone);\n--\nmm/page_owner.c=440=static inline bool skip_buddy_pages(unsigned long *pfn, struct page *page)\n--\nmm/page_owner.c-457-\nmm/page_owner.c:458:void pagetypeinfo_showmixedcount_print(struct seq_file *m,\nmm/page_owner.c-459-\t\t\t\t pg_data_t *pgdat, struct zone *zone)\n--\nmm/vmstat.c=1684=static void pagetypeinfo_showmixedcount(struct seq_file *m, pg_data_t *pgdat)\n--\nmm/vmstat.c-1699-\twalk_zones_in_node(m, pgdat, true, true,\nmm/vmstat.c:1700:\t\tpagetypeinfo_showmixedcount_print);\nmm/vmstat.c-1701-#endif /* CONFIG_PAGE_OWNER */\n"
} |
| 7/2 | 2026/07/01 11:12 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 12319 output: 25 thoughts: 440 Thoughts: **Fuzzing Plan for `mm/page_owner.c` Changes** Okay, so looking at these changes, it seems like the most significant functional alterations are happening in `mm/page_owner.c`, specifically related to how buddy pages are skipped. The modifications in `mm/migrate.c`, `mm/hugetlb.c`, and `include/trace/events/migrate.h` are mostly type changes and tracepoint updates – probably not the primary focus for fuzzing. And the `scripts/gdb/linux/page_owner.py` changes are just for GDB scripting. My gut says this is worth a fuzzing effort. The commit message likely highlights a bug fix – potentially a fix for out-of-bounds access or issues with invalid PFNs when iterating through pages in `page_owner` and `pagetypeinfo`. The `skip_buddy_pages` function is likely the crux of the fix, aiming to address a situation where `buddy_order_unsafe` might return a garbage order, leading to unvalidated memory access. We should target fuzzing by reading from `/sys/kernel/debug/page_owner` and `/proc/pagetypeinfo`. The `PAGE_OWNER` config option is essential, of course. Initially, I was thinking of focusing on `read_page_owner`, `pagetypeinfo_showmixedcount_print`, and `skip_buddy_pages`. But wait, `skip_buddy_pages` is *inline*! That complicates things, so we need to concentrate on `read_page_owner` and `pagetypeinfo_showmixedcount_print` instead. I also want to double-check that there aren't any other functional changes I'm missing. Ah, `__folio_set_owner_migrate_reason` is just a type change. Then, `print_page_owner_memcg` is changing `PageMemcgKmem(page)` to `(memcg_data & MEMCG_DATA_KMEM)`. Hmmm, I need to look into that `PageMemcgKmem` bit... did it get removed or renamed? Let's check that out. |
| 8/2 | 2026/07/01 11:13 | tool | grepper |
0mArgs:{
"Expression": "PageMemcgKmem",
"PathPrefix": ""
}Results: {
"Output": "include/linux/memcontrol.h=506=static inline bool folio_memcg_kmem(struct folio *folio)\n--\ninclude/linux/memcontrol.h-512-\ninclude/linux/memcontrol.h:513:static inline bool PageMemcgKmem(struct page *page)\ninclude/linux/memcontrol.h-514-{\n--\ninclude/linux/memcontrol.h=1059=static inline bool folio_memcg_kmem(struct folio *folio)\n--\ninclude/linux/memcontrol.h-1063-\ninclude/linux/memcontrol.h:1064:static inline bool PageMemcgKmem(struct page *page)\ninclude/linux/memcontrol.h-1065-{\n--\nmm/page_alloc.c=1314=static __always_inline bool __free_pages_prepare(struct page *page,\n--\nmm/page_alloc.c-1330-\nmm/page_alloc.c:1331:\tif (memcg_kmem_online() \u0026\u0026 PageMemcgKmem(page))\nmm/page_alloc.c-1332-\t\t__memcg_kmem_uncharge_page(page, order);\n--\nmm/slub.c=2508=bool memcg_slab_post_charge(void *p, gfp_t flags)\n--\nmm/slub.c-2521-\nmm/slub.c:2522:\t\tif (PageMemcgKmem(page))\nmm/slub.c-2523-\t\t\treturn true;\n"
} |
| 9/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 13647 output: 25 thoughts: 232 Thoughts: **Analyzing a Recent Kernel Patch** Okay, so I'm looking at this kernel patch and immediately spot a familiar face: `PageMemcgKmem`. Ah, right, they've updated how it's handled, now it's tucked away in `mm/page_owner.c` and represented as `(memcg_data & MEMCG_DATA_KMEM)`. Makes sense. Now, let me see... I see they're reading `memcg_data` locklessly: ```c memcg_data = READ_ONCE(page->memcg_data); if (!memcg_data) goto out_unlock; memcg = page_memcg_check(page); ``` Then they're checking it with a bitwise AND operation - `(memcg_data & MEMCG_DATA_KMEM)`. Seems like a straightforward, minor adjustment. But the meat of this seems to be the introduction of `skip_buddy_pages`. Interesting. The question now is: is this worth fuzzing? Let's see... Yep, this addresses a bug in page iteration, which I see can be triggered from userspace by reading `/sys/kernel/debug/page_owner` or `/proc/pagetypeinfo`. Given it's user-accessible and fixes a bug, absolutely, fuzzing this is the right call. The risk/reward ratio is definitely there. Before I go further, let me just quickly confirm `CONFIG_PAGE_OWNER` is enabled, just to be sure... |
| 10/2 | 2026/07/01 11:13 | tool | grepper |
0mArgs:{
"Expression": "config PAGE_OWNER",
"PathPrefix": ""
}Results: {
"Output": "mm/Kconfig.debug=73=config SLUB_RCU_DEBUG\n--\nmm/Kconfig.debug-104-\nmm/Kconfig.debug:105:config PAGE_OWNER\nmm/Kconfig.debug-106-\tbool \"Track page owner\"\n"
} |
| 11/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14319 output: 27 thoughts: 77 Thoughts: Okay, let's break this down. **Analyzing Page Owner Configuration and Runtime Reachability** Right, so `PAGE_OWNER`... that's our key configuration point. My focus is on understanding how this affects the system, and right now, the primary symbols drawing my attention are `read_page_owner` and `pagetypeinfo_showmixedcount_print`. These are likely crucial for accessing and displaying page ownership information, which, given the context, probably means tracking memory allocation. Now, hold on... I see that `init_pages_in_zone` gets modified too. That's interesting, because that's called during initialization and isn't usually something I'd immediately associate with runtime behavior. The question now is: is `init_pages_in_zone` actually *reachable* during runtime? Even though it's an initialization function, could something later on cause it to be executed again, or interact with its results? That's the critical question I need to answer. I need to trace the code path and see if it's potentially called at some point, or its initial state used at runtime in an unexpected way. |
| 12/2 | 2026/07/01 11:13 | tool | grepper |
0mArgs:{
"Expression": "init_pages_in_zone",
"PathPrefix": ""
}Results: {
"Output": "mm/page_owner.c=298=void __reset_page_owner(struct page *page, unsigned short order)\n--\nmm/page_owner.c-326-\t\t * early_handle is being set as a handle for all those\nmm/page_owner.c:327:\t\t * early allocated pages. See init_pages_in_zone().\nmm/page_owner.c-328-\t\t * Since their refcount is not being incremented because\n--\nmm/page_owner.c=786=static loff_t lseek_page_owner(struct file *file, loff_t offset, int orig)\n--\nmm/page_owner.c-800-\nmm/page_owner.c:801:static void init_pages_in_zone(struct zone *zone)\nmm/page_owner.c-802-{\n--\nmm/page_owner.c=859=static void init_early_allocated_pages(void)\n--\nmm/page_owner.c-863-\tfor_each_populated_zone(zone)\nmm/page_owner.c:864:\t\tinit_pages_in_zone(zone);\nmm/page_owner.c-865-}\n"
} |
| 13/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14978 output: 31 thoughts: 167 Thoughts: **Analyzing Page Owner Functionality** Okay, so `init_pages_in_zone` is definitely a kernel-level function, called way early during the boot process via `init_early_allocated_pages`. No user space interaction there. But `read_page_owner` and `pagetypeinfo_showmixedcount_print`, those are accessible. I need to understand how these work. I should check the source for `read_page_owner`, that's in `mm/page_owner.c`. From what I know, it appears that this is the `.read` operation for `/sys/kernel/debug/page_owner`. That makes sense. And `pagetypeinfo_showmixedcount_print` shows up in `/proc/pagetypeinfo`. Now, I'm thinking, are there any other configuration dependencies? I know that `PAGE_OWNER` depends on `DEBUG_KERNEL` and `STACKTRACE_SUPPORT`, so let me take a look at `mm/Kconfig.debug`. I need to ensure that the proper config options are enabled to use these features properly and that I understand any related dependencies. |
| 14/2 | 2026/07/01 11:13 | tool | grepper |
0mArgs:{
"Expression": "config PAGE_OWNER",
"PathPrefix": "mm/Kconfig.debug"
}Results: {
"Output": "mm/Kconfig.debug=73=config SLUB_RCU_DEBUG\n--\nmm/Kconfig.debug-104-\nmm/Kconfig.debug:105:config PAGE_OWNER\nmm/Kconfig.debug-106-\tbool \"Track page owner\"\n"
} |
| 15/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 15498 output: 34 thoughts: 19 Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience: **Diving into Debug Configuration** Okay, so I'm thinking, where's the best place to dig into these debug options? My instinct is to check the `mm/Kconfig.debug` file, specifically around line 105. That's usually where these granular debug features are defined, right? I'm anticipating that's where I'll find the relevant configuration options that determine how the kernel's memory management debugging is handled, especially since we're looking at things that are typically enabled during development or for very specific troubleshooting. Hopefully, the Kconfig file will offer a clear overview of the dependencies, defaults, and descriptions. This should give me a solid starting point for understanding how the debug features in this part of the kernel work. |
| 16/2 | 2026/07/01 11:13 | tool | read-file |
0mArgs:{
"File": "mm/Kconfig.debug",
"FirstLine": 100,
"LineCount": 20
}Results: {
"Contents": " 100:\t\t This is designed as a debugging feature, not a security feature.\n 101:\t\t Objects are sometimes recycled without RCU delay under memory pressure.\n 102:\t\n 103:\t\t If unsure, say N.\n 104:\t\n 105:\tconfig PAGE_OWNER\n 106:\t\tbool \"Track page owner\"\n 107:\t\tdepends on DEBUG_KERNEL \u0026\u0026 STACKTRACE_SUPPORT\n 108:\t\tselect DEBUG_FS\n 109:\t\tselect STACKTRACE\n 110:\t\tselect STACKDEPOT\n 111:\t\tselect PAGE_EXTENSION\n 112:\t\thelp\n 113:\t\t This keeps track of what call chain is the owner of a page, may\n 114:\t\t help to find bare alloc_page(s) leaks. Even if you include this\n 115:\t\t feature on your build, it is disabled in default. You should pass\n 116:\t\t \"page_owner=on\" to boot parameter in order to enable it. Eats\n 117:\t\t a fair amount of memory if enabled. See tools/mm/page_owner_sort.c\n 118:\t\t for user-space helper.\n 119:\t\n"
} |
| 17/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16050 output: 35 thoughts: 291 Thoughts: **Analyzing the Implications of a Configuration Change** Okay, so I'm looking at this change, and the initial note says, "You should pass 'page_owner=on' to boot parameter in order to enable it." Right, that makes sense. I know syzkaller *usually* handles that automatically if `CONFIG_PAGE_OWNER` is set. However, my understanding is that it only *might* do that. So, to be safe, we can just explicitly specify `PAGE_OWNER` in `EnableConfigs`. That should be the cleanest way to do this. Wait, syzkaller doesn't automatically pass boot parameters based on configs unless configured in the syzkaller manager, right? That's right, and we were asked to provide `EnableConfigs`. Fine, so that's handled. Is there anything else? No, just `PAGE_OWNER` is mentioned. But wait, the patch also touches `mm/migrate.c` and `mm/hugetlb.c`. Oh, those are just minor, purely decorative, refactoring changes. They're changing `int reason` to `enum migrate_reason reason`. Doesn't affect anything functionally. The *real* functional change is the addition of `skip_buddy_pages` in `mm/page_owner.c`. Now, `skip_buddy_pages` is interesting. It *prevents* reading a garbage order and skipping into an unvalidated memory section. That sounds like a legitimate bug fix. A very important one. Let me just quickly check if `skip_buddy_pages` is used anywhere else in the kernel besides `mm/page_owner.c`. Nope, only there. It's localized to that module, which makes it easier to reason about. Just to be absolutely sure, let me double check `pagetypeinfo_showmixedcount_print`, just to make sure no subtle changes. |
| 18/2 | 2026/07/01 11:13 | tool | grepper |
0mArgs:{
"Expression": "pagetypeinfo_showmixedcount_print",
"PathPrefix": "mm/vmstat.c"
}Results: {
"Output": "mm/vmstat.c=1684=static void pagetypeinfo_showmixedcount(struct seq_file *m, pg_data_t *pgdat)\n--\nmm/vmstat.c-1699-\twalk_zones_in_node(m, pgdat, true, true,\nmm/vmstat.c:1700:\t\tpagetypeinfo_showmixedcount_print);\nmm/vmstat.c-1701-#endif /* CONFIG_PAGE_OWNER */\n"
} |
| 19/2 | 2026/07/01 11:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16909 output: 119 thoughts: 929 Thoughts: **Analysis of the page_owner Patch for Fuzzing** Okay, so this patch affects `mm/vmstat.c` under `CONFIG_PAGE_OWNER`. My initial assessment is that there are two primary functional changes: 1. The `skip_buddy_pages` logic is modified within `read_page_owner` and `pagetypeinfo_showmixedcount_print`. 2. `print_page_owner_memcg` now uses `memcg_data & MEMCG_DATA_KMEM` instead of the `PageMemcgKmem(page)` macro. Both of these changes are located within `mm/page_owner.c` and are guarded by `CONFIG_PAGE_OWNER`. My focus symbols are `read_page_owner` and `pagetypeinfo_showmixedcount_print`. Let's confirm if either of these are in a hot path. It looks like `read_page_owner` is part of a debugfs read operation, and `pagetypeinfo_showmixedcount_print` is a procfs read operation. That means neither are performance critical, so the change isn't about performance, but safety. I should check if `PAGE_OWNER` is enabled by default in syzbot. It usually enables a lot of debug configs, so it's likely already on, but it's still good practice to explicitly list it in `EnableConfigs`. Let me take another look at the `skip_buddy_pages` change. I see the code prevents `pfn` from advancing too far if `buddy_order_unsafe` returns a potentially bad value. This is a clear functional change that protects against out-of-bounds memory accesses or infinite loops. This is absolutely worth fuzzing because it can lead to very interesting behaviors with buddy allocator and memory management. Now let's review the `print_page_owner_memcg` change. This seems to be a functional change, as it impacts how the "via objcg" string is printed. It avoids calling `PageMemcgKmem(page)`, which could be unsafe or inaccurate in the new context. Overall, this is definitely a patch I should prioritize for fuzzing. It introduces changes to protect the buddy allocator and potentially alters the information provided in debugging tools. So, here's what I'm going to prepare for the results: * **WorthFuzzing**: `true` * **FocusSymbols**: `["read_page_owner", "pagetypeinfo_showmixedcount_print"]` * **EnableConfigs**: `["PAGE_OWNER"]` * **Reasoning**: "The patch introduces a functional change in `mm/page_owner.c` to safely skip buddy pages during iteration, preventing potential out-of-bounds PFN accesses when reading `/sys/kernel/debug/page_owner` or `/proc/pagetypeinfo`. It also fixes memcg data reading in page owner." |
| 20/2 | 2026/07/01 11:13 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"PAGE_OWNER"
],
"FocusSymbols": [
"read_page_owner",
"pagetypeinfo_showmixedcount_print"
],
"Reasoning": "The patch introduces a functional change in mm/page_owner.c to safely skip buddy pages during iteration, preventing potential out-of-bounds PFN accesses when reading /sys/kernel/debug/page_owner or /proc/pagetypeinfo. It also fixes memcg data reading in page owner.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"PAGE_OWNER"
],
"FocusSymbols": [
"read_page_owner",
"pagetypeinfo_showmixedcount_print"
],
"Reasoning": "The patch introduces a functional change in mm/page_owner.c to safely skip buddy pages during iteration, preventing potential out-of-bounds PFN accesses when reading /sys/kernel/debug/page_owner or /proc/pagetypeinfo. It also fixes memcg data reading in page owner.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|