AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/14 23:56 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies only user-space selftests and test runner scripts under tools/testing/selftests/mm/. It does not touch any kernel code or affect the compiled kernel image (vmlinux).",
  "WorthFuzzing": false
}

1/1 2026/10/14 23:56 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 16bdbf96d1ba8f126596d177e62ab7a93261fc03\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Wed Oct 14 23:56:04 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile\nindex beacc0f873049..07d61110eb240 100644\n--- a/tools/testing/selftests/mm/Makefile\n+++ b/tools/testing/selftests/mm/Makefile\n@@ -69,6 +69,7 @@ TEST_GEN_FILES += hugetlb-shm\n TEST_GEN_FILES += hugetlb-soft-offline\n TEST_GEN_FILES += khugepaged\n TEST_GEN_FILES += madv_populate\n+TEST_GEN_FILES += madvise\n TEST_GEN_FILES += map_fixed_noreplace\n TEST_GEN_FILES += map_populate\n ifneq (,$(filter $(ARCH),arm64 riscv riscv64 x86 x86_64 loongarch32 loongarch64))\n@@ -156,6 +157,7 @@ TEST_PROGS += ksft_ksm.sh\n TEST_PROGS += ksft_ksm_numa.sh\n TEST_PROGS += ksft_madv_guard.sh\n TEST_PROGS += ksft_madv_populate.sh\n+TEST_PROGS += ksft_madvise.sh\n TEST_PROGS += ksft_memfd_secret.sh\n TEST_PROGS += ksft_memory_failure.sh\n TEST_PROGS += ksft_migration.sh\ndiff --git a/tools/testing/selftests/mm/ksft_madvise.sh b/tools/testing/selftests/mm/ksft_madvise.sh\nnew file mode 100755\nindex 0000000000000..29cbbca800f2c\n--- /dev/null\n+++ b/tools/testing/selftests/mm/ksft_madvise.sh\n@@ -0,0 +1,4 @@\n+#!/bin/sh -e\n+# SPDX-License-Identifier: GPL-2.0\n+\n+./run_vmtests.sh -t madvise\ndiff --git a/tools/testing/selftests/mm/madvise.c b/tools/testing/selftests/mm/madvise.c\nnew file mode 100644\nindex 0000000000000..d59c03ec8191e\n--- /dev/null\n+++ b/tools/testing/selftests/mm/madvise.c\n@@ -0,0 +1,846 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * MADV_COLD and MADV_PAGEOUT tests.\n+ */\n+#define _GNU_SOURCE\n+#include \u003cerrno.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003cgrp.h\u003e\n+#include \u003csched.h\u003e\n+#include \u003cstdbool.h\u003e\n+#include \u003cstdint.h\u003e\n+#include \u003cstring.h\u003e\n+#include \u003csys/ipc.h\u003e\n+#include \u003csys/mman.h\u003e\n+#include \u003csys/shm.h\u003e\n+#include \u003csys/stat.h\u003e\n+#include \u003cunistd.h\u003e\n+\n+#include \"kselftest_harness.h\"\n+#include \"vm_util.h\"\n+\n+#ifndef MADV_COLLAPSE\n+#define MADV_COLLAPSE 25\n+#endif\n+\n+/* Reclaim can fail transiently, so retry for up to a second. */\n+#define PAGEOUT_RETRIES 100\n+\n+static size_t page_size;\n+static size_t pmd_size;\n+\n+FIXTURE(madvise)\n+{\n+\tint pagemap_fd;\n+\t/* Opening it needs CAP_SYS_ADMIN, -1 otherwise. */\n+\tint kpageflags_fd;\n+};\n+\n+FIXTURE_SETUP(madvise)\n+{\n+\tpage_size = getpagesize();\n+\tpmd_size = read_pmd_pagesize();\n+\n+\tself-\u003epagemap_fd = open(\"/proc/self/pagemap\", O_RDONLY);\n+\tASSERT_GE(self-\u003epagemap_fd, 0);\n+\tself-\u003ekpageflags_fd = open(\"/proc/kpageflags\", O_RDONLY);\n+}\n+\n+FIXTURE_TEARDOWN(madvise)\n+{\n+\tif (self-\u003ekpageflags_fd \u003e= 0)\n+\t\tclose(self-\u003ekpageflags_fd);\n+\tclose(self-\u003epagemap_fd);\n+}\n+\n+/*\n+ * Reserve a PMD-aligned range, so that memory mapped over it with MAP_FIXED\n+ * can be backed by PMD-mapped THPs.\n+ */\n+static char *reserve_aligned(struct __test_metadata *_metadata, size_t size)\n+{\n+\tchar *reservation, *aligned;\n+\n+\treservation = mmap(NULL, size + pmd_size, PROT_NONE,\n+\t\t\t   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(reservation, MAP_FAILED);\n+\n+\taligned = (char *)(((uintptr_t)reservation + pmd_size - 1) \u0026\n+\t\t\t   ~(pmd_size - 1));\n+\tif (aligned != reservation)\n+\t\tASSERT_EQ(munmap(reservation, aligned - reservation), 0);\n+\tASSERT_EQ(munmap(aligned + size, reservation + pmd_size - aligned), 0);\n+\n+\treturn aligned;\n+}\n+\n+/* Map PMD-aligned anonymous memory filled with 1s. */\n+static char *map_aligned_pages(struct __test_metadata *_metadata, size_t size)\n+{\n+\tchar *mapping = reserve_aligned(_metadata, size);\n+\n+\tASSERT_EQ(mmap(mapping, size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),\n+\t\t  mapping);\n+\tmemset(mapping, 1, size);\n+\n+\treturn mapping;\n+}\n+\n+/*\n+ * Collapse a PMD-aligned range into PMD-mapped THPs, and confirm the result\n+ * with check_huge(). MADV_COLLAPSE may fail transiently with EAGAIN.\n+ */\n+static bool collapse(char *mapping, size_t size,\n+\t\t     bool (*check_huge)(void *, size_t, int, uint64_t))\n+{\n+\tint nr_hpages = size / pmd_size;\n+\tint retry;\n+\n+\tfor (retry = 0; retry \u003c 10; retry++) {\n+\t\tif (madvise(mapping, size, MADV_COLLAPSE)) {\n+\t\t\tif (errno != EAGAIN)\n+\t\t\t\treturn false;\n+\t\t} else if (check_huge(mapping, size, nr_hpages, pmd_size)) {\n+\t\t\treturn true;\n+\t\t}\n+\t\tusleep(10000);\n+\t}\n+\n+\treturn false;\n+}\n+\n+/* Map a PMD-mapped anonymous THP filled with 1s, or return NULL. */\n+static char *map_thp(struct __test_metadata *_metadata)\n+{\n+\tchar *mapping;\n+\n+\tif (!pmd_size)\n+\t\treturn NULL;\n+\tmapping = map_aligned_pages(_metadata, pmd_size);\n+\tif (collapse(mapping, pmd_size, check_huge_anon))\n+\t\treturn mapping;\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+\treturn NULL;\n+}\n+\n+/*\n+ * Changing the protection of one page PTE-maps a THP. When /proc/kpageflags\n+ * is readable, also check that the folio is still large.\n+ */\n+static void pte_map_thp(struct __test_metadata *_metadata, char *mapping,\n+\t\t\tint pagemap_fd, int kpageflags_fd)\n+{\n+\tunsigned long pfn;\n+\tuint64_t flags;\n+\n+\tASSERT_EQ(mprotect(mapping + page_size, page_size, PROT_READ), 0);\n+\tASSERT_EQ(mprotect(mapping + page_size, page_size,\n+\t\t\t   PROT_READ | PROT_WRITE), 0);\n+\tASSERT_TRUE(check_huge_anon(mapping, pmd_size, 0, pmd_size));\n+\tif (kpageflags_fd \u003c 0)\n+\t\treturn;\n+\n+\tpfn = pagemap_get_pfn(pagemap_fd, mapping);\n+\tASSERT_NE(pfn, -1UL);\n+\tASSERT_EQ(pageflags_get(pfn, kpageflags_fd, \u0026flags), 0);\n+\tASSERT_TRUE(flags \u0026 KPF_COMPOUND_HEAD);\n+}\n+\n+static char *map_aligned_file(struct __test_metadata *_metadata, int fd,\n+\t\t\t      size_t size, int flags)\n+{\n+\tchar *mapping = reserve_aligned(_metadata, size);\n+\n+\tASSERT_EQ(mmap(mapping, size, PROT_READ | PROT_WRITE, flags | MAP_FIXED,\n+\t\t       fd, 0), mapping);\n+\n+\treturn mapping;\n+}\n+\n+/* Create a PMD-sized shmem file filled with 1s. */\n+static int create_shmem_file(struct __test_metadata *_metadata)\n+{\n+\tchar *shared;\n+\tint fd;\n+\n+\tfd = memfd_create(\"madvise-pageout\", 0);\n+\tASSERT_GE(fd, 0);\n+\tASSERT_EQ(ftruncate(fd, pmd_size), 0);\n+\tshared = map_aligned_file(_metadata, fd, pmd_size, MAP_SHARED);\n+\tmemset(shared, 1, pmd_size);\n+\tASSERT_EQ(munmap(shared, pmd_size), 0);\n+\n+\treturn fd;\n+}\n+\n+static void assert_filled(struct __test_metadata *_metadata,\n+\t\t\t  const char *mapping, size_t size)\n+{\n+\tsize_t offset;\n+\n+\tfor (offset = 0; offset \u003c size; offset += page_size) {\n+\t\tASSERT_EQ(mapping[offset], 1)\n+\t\t\tTH_LOG(\"memory changed at offset %zu\", offset);\n+\t}\n+}\n+\n+/* Page out the range until its first page is swapped. */\n+static bool pageout_until_swapped(struct __test_metadata *_metadata,\n+\t\t\t\t  char *mapping, size_t size, int pagemap_fd)\n+{\n+\tint retry;\n+\n+\tfor (retry = 0; retry \u003c PAGEOUT_RETRIES; retry++) {\n+\t\tASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), 0);\n+\t\tif (pagemap_is_swapped(pagemap_fd, mapping))\n+\t\t\treturn true;\n+\t\tusleep(10000);\n+\t}\n+\n+\treturn false;\n+}\n+\n+/* Page out the range until its first page is no longer mapped. */\n+static bool pageout_until_evicted(struct __test_metadata *_metadata,\n+\t\t\t\t  char *mapping, size_t size, int pagemap_fd)\n+{\n+\tint retry;\n+\n+\tfor (retry = 0; retry \u003c PAGEOUT_RETRIES; retry++) {\n+\t\tASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), 0);\n+\t\tif (!pagemap_is_populated(pagemap_fd, mapping))\n+\t\t\treturn true;\n+\t\tusleep(10000);\n+\t}\n+\n+\treturn false;\n+}\n+\n+/*\n+ * Keep this task on one CPU, so that draining that CPU's LRU batches drains\n+ * the folios this task faulted in.\n+ */\n+static void pin_to_current_cpu(struct __test_metadata *_metadata)\n+{\n+\tint cpu = sched_getcpu();\n+\tcpu_set_t mask;\n+\n+\tASSERT_GE(cpu, 0);\n+\tCPU_ZERO(\u0026mask);\n+\tCPU_SET(cpu, \u0026mask);\n+\tASSERT_EQ(sched_setaffinity(0, sizeof(mask), \u0026mask), 0);\n+}\n+\n+static bool page_is_present(int pagemap_fd, char *addr)\n+{\n+\treturn pagemap_get_entry(pagemap_fd, addr) \u0026 PM_PRESENT;\n+}\n+\n+/* Map an unlinked, zero-filled temporary file with the given permissions. */\n+static char *map_temp_file(struct __test_metadata *_metadata, size_t size,\n+\t\t\t   int flags, mode_t mode)\n+{\n+\tchar template[] = \"/tmp/madvise-pageout-XXXXXX\";\n+\tchar *mapping;\n+\tint fd;\n+\n+\tfd = mkstemp(template);\n+\tASSERT_GE(fd, 0);\n+\tASSERT_EQ(unlink(template), 0);\n+\tASSERT_EQ(ftruncate(fd, size), 0);\n+\tASSERT_EQ(fchmod(fd, mode), 0);\n+\n+\tmapping = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tclose(fd);\n+\n+\treturn mapping;\n+}\n+\n+/* Become an unprivileged user without capabilities. */\n+static bool drop_privileges(void)\n+{\n+\treturn !setgroups(0, NULL) \u0026\u0026 !setgid(65534) \u0026\u0026 !setuid(65534);\n+}\n+\n+/*\n+ * Both hints are best effort, so these tests check what userspace can rely\n+ * on: which mappings are accepted, which pages must never be reclaimed, and\n+ * that neither hint changes memory contents or populates the range. A test\n+ * that needs a page to be reclaimed first checks that reclaim works at all,\n+ * and skips if it does not.\n+ */\n+\n+/* Neither hint may populate a hole between two populated pages. */\n+static void check_skips_pte_hole(struct __test_metadata *_metadata,\n+\t\t\t\t int pagemap_fd, int advice)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\tmapping[2 * page_size] = 1;\n+\tASSERT_FALSE(pagemap_is_populated(pagemap_fd, mapping + page_size));\n+\n+\tASSERT_EQ(madvise(mapping, 3 * page_size, advice), 0);\n+\tASSERT_FALSE(pagemap_is_populated(pagemap_fd, mapping + page_size));\n+\tASSERT_EQ(mapping[0], 1);\n+\tASSERT_EQ(mapping[2 * page_size], 1);\n+\n+\tASSERT_EQ(munmap(mapping, 3 * page_size), 0);\n+}\n+\n+TEST_F(madvise, cold_skips_pte_hole)\n+{\n+\tcheck_skips_pte_hole(_metadata, self-\u003epagemap_fd, MADV_COLD);\n+}\n+\n+TEST_F(madvise, pageout_skips_pte_hole)\n+{\n+\tcheck_skips_pte_hole(_metadata, self-\u003epagemap_fd, MADV_PAGEOUT);\n+}\n+\n+/* Neither hint may be applied to locked memory. */\n+TEST_F(madvise, locked_vma_is_rejected)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\tif (mlock(mapping, page_size))\n+\t\tSKIP(return, \"could not lock a page\");\n+\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_COLD), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/* Neither hint may be applied to hugetlb memory. */\n+TEST_F(madvise, hugetlb_vma_is_rejected)\n+{\n+\tsize_t size = default_huge_page_size();\n+\tchar *mapping;\n+\n+\tif (!size)\n+\t\tSKIP(return, \"hugetlb is not supported\");\n+\tmapping = mmap(NULL, size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);\n+\tif (mapping == MAP_FAILED)\n+\t\tSKIP(return, \"no hugetlb pages are available\");\n+\tmapping[0] = 1;\n+\n+\tASSERT_EQ(madvise(mapping, size, MADV_COLD), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, size), 0);\n+}\n+\n+/* A zero-length range is accepted. */\n+TEST_F(madvise, zero_length_is_accepted)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\n+\tASSERT_EQ(madvise(mapping, 0, MADV_COLD), 0);\n+\tASSERT_EQ(madvise(mapping, 0, MADV_PAGEOUT), 0);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/* The start of the range must be page-aligned. */\n+TEST_F(madvise, unaligned_start_is_rejected)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\n+\tASSERT_EQ(madvise(mapping + 1, page_size, MADV_COLD), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(madvise(mapping + 1, page_size, MADV_PAGEOUT), -1);\n+\tASSERT_EQ(errno, EINVAL);\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, 2 * page_size), 0);\n+}\n+\n+/*\n+ * An unmapped gap in the range makes either hint fail with ENOMEM, after\n+ * advising the mapped pages on both sides of the gap. Only MADV_PAGEOUT has\n+ * an effect on those pages that the test can observe.\n+ */\n+TEST_F(madvise, unmapped_gap_fails_after_advising)\n+{\n+\tchar *mapping, *control;\n+\tbool swapped = false;\n+\tint retry;\n+\n+\tpin_to_current_cpu(_metadata);\n+\n+\t/* Make sure MADV_PAGEOUT can swap at all before relying on it. */\n+\tcontrol = mmap(NULL, page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(control, MAP_FAILED);\n+\tcontrol[0] = 1;\n+\tif (!pageout_until_swapped(_metadata, control, page_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the page\");\n+\n+\tmapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\tmapping[2 * page_size] = 1;\n+\tASSERT_EQ(munmap(mapping + page_size, page_size), 0);\n+\n+\tASSERT_EQ(madvise(mapping, 3 * page_size, MADV_COLD), -1);\n+\tASSERT_EQ(errno, ENOMEM);\n+\tASSERT_EQ(mapping[0], 1);\n+\tASSERT_EQ(mapping[2 * page_size], 1);\n+\n+\tfor (retry = 0; retry \u003c PAGEOUT_RETRIES \u0026\u0026 !swapped; retry++) {\n+\t\tASSERT_EQ(madvise(mapping, 3 * page_size, MADV_PAGEOUT), -1);\n+\t\tASSERT_EQ(errno, ENOMEM);\n+\t\tswapped = pagemap_is_swapped(self-\u003epagemap_fd, mapping) \u0026\u0026\n+\t\t\t  pagemap_is_swapped(self-\u003epagemap_fd,\n+\t\t\t\t\t     mapping + 2 * page_size);\n+\t\tif (!swapped)\n+\t\t\tusleep(10000);\n+\t}\n+\tASSERT_TRUE(swapped)\n+\t\tTH_LOG(\"MADV_PAGEOUT did not page out both sides of the gap\");\n+\tASSERT_EQ(mapping[0], 1);\n+\tASSERT_EQ(mapping[2 * page_size], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+\tASSERT_EQ(munmap(mapping + 2 * page_size, page_size), 0);\n+\tASSERT_EQ(munmap(control, page_size), 0);\n+}\n+\n+/* Neither hint may fault a swapped page back in. */\n+static void check_skips_swapped_pte(struct __test_metadata *_metadata,\n+\t\t\t\t    int pagemap_fd, int advice)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\tif (!pageout_until_swapped(_metadata, mapping, page_size, pagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the page\");\n+\n+\tASSERT_EQ(madvise(mapping, page_size, advice), 0);\n+\tASSERT_TRUE(pagemap_is_swapped(pagemap_fd, mapping));\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+TEST_F(madvise, cold_skips_swapped_pte)\n+{\n+\tcheck_skips_swapped_pte(_metadata, self-\u003epagemap_fd, MADV_COLD);\n+}\n+\n+TEST_F(madvise, pageout_skips_swapped_pte)\n+{\n+\tcheck_skips_swapped_pte(_metadata, self-\u003epagemap_fd, MADV_PAGEOUT);\n+}\n+\n+/* MADV_COLD must not discard a dirty page of a shared file mapping. */\n+TEST_F(madvise, cold_preserves_dirty_shared_file_page)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0600);\n+\tmapping[0] = 1;\n+\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_COLD), 0);\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/*\n+ * MADV_PAGEOUT may write a dirty shared file page back or leave it resident,\n+ * but must not discard it.\n+ */\n+TEST_F(madvise, pageout_preserves_dirty_shared_file_page)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0600);\n+\tmapping[0] = 1;\n+\n+\tif (!pageout_until_evicted(_metadata, mapping, page_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not unmap the page\");\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/* MADV_PAGEOUT must not lose the contents of shared anonymous memory. */\n+TEST_F(madvise, pageout_preserves_shared_anon_page)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmapping[0] = 1;\n+\n+\tif (!pageout_until_evicted(_metadata, mapping, page_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not evict the page\");\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/*\n+ * SHM_LOCK makes shmem pages unevictable without setting VM_LOCKED on the\n+ * VMA, so MADV_PAGEOUT accepts the range but must not reclaim the page.\n+ */\n+TEST_F(madvise, pageout_skips_unevictable_page)\n+{\n+\tunsigned long pfn;\n+\tuint64_t flags;\n+\tchar *mapping;\n+\tint shmid;\n+\n+\tif (self-\u003ekpageflags_fd \u003c 0)\n+\t\tSKIP(return, \"requires root to read page flags\");\n+\tshmid = shmget(IPC_PRIVATE, page_size, IPC_CREAT | 0600);\n+\tASSERT_GE(shmid, 0);\n+\tif (shmctl(shmid, SHM_LOCK, NULL)) {\n+\t\tshmctl(shmid, IPC_RMID, NULL);\n+\t\tSKIP(return, \"could not lock a shmem segment\");\n+\t}\n+\tmapping = shmat(shmid, NULL, 0);\n+\t/* The segment is destroyed once it is detached. */\n+\tshmctl(shmid, IPC_RMID, NULL);\n+\tASSERT_NE(mapping, (void *)-1);\n+\tpin_to_current_cpu(_metadata);\n+\tmapping[0] = 1;\n+\n+\t/*\n+\t * MADV_COLD drains this CPU's LRU add batch, which makes the page\n+\t * unevictable.\n+\t */\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_COLD), 0);\n+\tpfn = pagemap_get_pfn(self-\u003epagemap_fd, mapping);\n+\tASSERT_NE(pfn, -1UL);\n+\tASSERT_EQ(pageflags_get(pfn, self-\u003ekpageflags_fd, \u0026flags), 0);\n+\tif (!(flags \u0026 KPF_UNEVICTABLE))\n+\t\tSKIP(return, \"could not make the page unevictable\");\n+\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), 0);\n+\tASSERT_TRUE(pagemap_is_populated(self-\u003epagemap_fd, mapping));\n+\tASSERT_EQ(mapping[0], 1);\n+\n+\tASSERT_EQ(shmdt(mapping), 0);\n+}\n+\n+/* MADV_PAGEOUT of a whole THP must swap it without corrupting it. */\n+TEST_F(madvise, pageout_swaps_thp)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_thp(_metadata);\n+\tif (!mapping)\n+\t\tSKIP(return, \"could not allocate a PMD-sized THP\");\n+\n+\tif (!pageout_until_swapped(_metadata, mapping, pmd_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the THP\");\n+\tassert_filled(_metadata, mapping, pmd_size);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+}\n+\n+/*\n+ * MADV_PAGEOUT must not reclaim pages outside the advised range. kswapd or\n+ * page migration could also unmap them, but neither should happen when the\n+ * selftests run by themselves.\n+ */\n+TEST_F(madvise, pageout_stays_within_range)\n+{\n+\tchar *mapping;\n+\n+\tmapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,\n+\t\t       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tmemset(mapping, 1, 3 * page_size);\n+\tif (!pageout_until_swapped(_metadata, mapping + page_size, page_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the page\");\n+\n+\tASSERT_TRUE(page_is_present(self-\u003epagemap_fd, mapping));\n+\tASSERT_TRUE(page_is_present(self-\u003epagemap_fd, mapping + 2 * page_size));\n+\tassert_filled(_metadata, mapping, 3 * page_size);\n+\n+\tASSERT_EQ(munmap(mapping, 3 * page_size), 0);\n+}\n+\n+/*\n+ * MADV_PAGEOUT must not reclaim the unadvised half of a THP: the THP must be\n+ * split before reclaim, or skipped. See pageout_stays_within_range about\n+ * other unmapping.\n+ */\n+static void pageout_half_thp(struct __test_metadata *_metadata, char *mapping,\n+\t\t\t     int pagemap_fd)\n+{\n+\tsize_t offset;\n+\n+\tif (!pageout_until_swapped(_metadata, mapping, pmd_size / 2,\n+\t\t\t\t   pagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the advised half\");\n+\n+\tfor (offset = pmd_size / 2; offset \u003c pmd_size; offset += page_size)\n+\t\tASSERT_TRUE(page_is_present(pagemap_fd, mapping + offset));\n+\tassert_filled(_metadata, mapping, pmd_size);\n+}\n+\n+TEST_F(madvise, pageout_stays_within_pmd_mapped_thp)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_thp(_metadata);\n+\tif (!mapping)\n+\t\tSKIP(return, \"could not allocate a PMD-sized THP\");\n+\n+\tpageout_half_thp(_metadata, mapping, self-\u003epagemap_fd);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+}\n+\n+TEST_F(madvise, pageout_stays_within_pte_mapped_thp)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_thp(_metadata);\n+\tif (!mapping)\n+\t\tSKIP(return, \"could not allocate a PMD-sized THP\");\n+\tpte_map_thp(_metadata, mapping, self-\u003epagemap_fd, self-\u003ekpageflags_fd);\n+\n+\tpageout_half_thp(_metadata, mapping, self-\u003epagemap_fd);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+}\n+\n+/* MADV_COLD on part of a THP may split it, but must not change it. */\n+TEST_F(madvise, cold_preserves_half_of_pmd_mapped_thp)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_thp(_metadata);\n+\tif (!mapping)\n+\t\tSKIP(return, \"could not allocate a PMD-sized THP\");\n+\n+\tASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_COLD), 0);\n+\tassert_filled(_metadata, mapping, pmd_size);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+}\n+\n+TEST_F(madvise, cold_preserves_half_of_pte_mapped_thp)\n+{\n+\tchar *mapping;\n+\n+\tmapping = map_thp(_metadata);\n+\tif (!mapping)\n+\t\tSKIP(return, \"could not allocate a PMD-sized THP\");\n+\tpte_map_thp(_metadata, mapping, self-\u003epagemap_fd, self-\u003ekpageflags_fd);\n+\n+\tASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_COLD), 0);\n+\tassert_filled(_metadata, mapping, pmd_size);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+}\n+\n+/*\n+ * The owner of a file may page out its clean page-cache pages. Run as an\n+ * unprivileged owner, so that capabilities cannot stand in for ownership.\n+ */\n+TEST_F(madvise, pageout_evicts_owned_file_page)\n+{\n+\tchar *mapping, *control;\n+\n+\tif (!geteuid() \u0026\u0026 !drop_privileges())\n+\t\tSKIP(return, \"could not drop privileges\");\n+\tpin_to_current_cpu(_metadata);\n+\n+\t/* Make sure a writable file page can be paged out at all. */\n+\tcontrol = map_temp_file(_metadata, page_size, MAP_PRIVATE, 0600);\n+\tASSERT_EQ(control[0], 0);\n+\tif (!pageout_until_evicted(_metadata, control, page_size,\n+\t\t\t\t   self-\u003epagemap_fd))\n+\t\tSKIP(return, \"MADV_PAGEOUT did not evict a file page\");\n+\n+\tmapping = map_temp_file(_metadata, page_size, MAP_PRIVATE, 0400);\n+\tASSERT_EQ(mapping[0], 0);\n+\tASSERT_TRUE(pageout_until_evicted(_metadata, mapping, page_size,\n+\t\t\t\t\t  self-\u003epagemap_fd));\n+\tASSERT_EQ(mapping[0], 0);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+\tASSERT_EQ(munmap(control, page_size), 0);\n+}\n+\n+/*\n+ * A caller that neither owns nor may write a file must not page out its\n+ * shared mappings. MADV_PAGEOUT succeeds but leaves the page resident.\n+ */\n+TEST_F(madvise, pageout_skips_unauthorized_shared_file)\n+{\n+\tchar *mapping;\n+\n+\tif (geteuid())\n+\t\tSKIP(return, \"requires root to change credentials\");\n+\tmapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0400);\n+\tif (!drop_privileges())\n+\t\tSKIP(return, \"could not drop privileges\");\n+\n+\tASSERT_EQ(mapping[0], 0);\n+\tASSERT_TRUE(pagemap_is_populated(self-\u003epagemap_fd, mapping));\n+\tASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), 0);\n+\tASSERT_TRUE(pagemap_is_populated(self-\u003epagemap_fd, mapping));\n+\tASSERT_EQ(mapping[0], 0);\n+\n+\tASSERT_EQ(munmap(mapping, page_size), 0);\n+}\n+\n+/*\n+ * A caller that neither owns nor may write a file may page out the anonymous\n+ * COW pages of a private mapping of it, but must leave the file pages\n+ * resident.\n+ */\n+TEST_F(madvise, pageout_private_file_reclaims_only_cow)\n+{\n+\tchar *mapping;\n+\tbool swapped;\n+\n+\tif (geteuid())\n+\t\tSKIP(return, \"requires root to change credentials\");\n+\tmapping = map_temp_file(_metadata, 2 * page_size, MAP_PRIVATE,\n+\t\t\t\t0400);\n+\tif (!drop_privileges())\n+\t\tSKIP(return, \"could not drop privileges\");\n+\n+\tASSERT_EQ(mapping[0], 0);\n+\tASSERT_EQ(mapping[page_size], 0);\n+\tmapping[0] = 1;\n+\n+\tASSERT_EQ(madvise(mapping, 2 * page_size, MADV_PAGEOUT), 0);\n+\tASSERT_TRUE(pagemap_is_populated(self-\u003epagemap_fd,\n+\t\t\t\t\t mapping + page_size));\n+\tswapped = pagemap_is_swapped(self-\u003epagemap_fd, mapping);\n+\tASSERT_EQ(mapping[0], 1);\n+\tASSERT_EQ(mapping[page_size], 0);\n+\tif (!swapped)\n+\t\tSKIP(return, \"MADV_PAGEOUT did not swap the COW page\");\n+\n+\tASSERT_EQ(munmap(mapping, 2 * page_size), 0);\n+}\n+\n+/*\n+ * An unauthorized caller must not page out a large file folio of a private\n+ * mapping, even when the advised range only partly covers it. See\n+ * pageout_stays_within_range about other unmapping.\n+ */\n+TEST_F(madvise, pageout_private_file_skips_large_file_folio)\n+{\n+\tchar *mapping;\n+\tsize_t offset;\n+\tint fd;\n+\n+\tif (!pmd_size)\n+\t\tSKIP(return, \"PMD-sized THPs are not supported\");\n+\tif (geteuid())\n+\t\tSKIP(return, \"requires root to change credentials\");\n+\tfd = create_shmem_file(_metadata);\n+\tmapping = map_aligned_file(_metadata, fd, pmd_size, MAP_PRIVATE);\n+\tif (!collapse(mapping, pmd_size, check_huge_file))\n+\t\tSKIP(return, \"could not allocate a PMD-sized shmem THP\");\n+\tASSERT_EQ(fchmod(fd, 0400), 0);\n+\tif (!drop_privileges())\n+\t\tSKIP(return, \"could not drop privileges\");\n+\n+\tASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_PAGEOUT), 0);\n+\tfor (offset = 0; offset \u003c pmd_size; offset += page_size)\n+\t\tASSERT_TRUE(page_is_present(self-\u003epagemap_fd,\n+\t\t\t\t\t    mapping + offset));\n+\tassert_filled(_metadata, mapping, pmd_size);\n+\n+\tASSERT_EQ(munmap(mapping, pmd_size), 0);\n+\tclose(fd);\n+}\n+\n+/*\n+ * An unauthorized caller must not page out a large file folio that a private\n+ * mapping maps with PTEs, even when the advised range covers only part of it.\n+ * See pageout_stays_within_range about other unmapping.\n+ */\n+TEST_F(madvise, pageout_private_file_skips_pte_mapped_file_folio)\n+{\n+\tconst size_t size = pmd_size / 2;\n+\tchar *mapping, *shared;\n+\tunsigned long pfn;\n+\tuint64_t flags;\n+\tsize_t offset;\n+\tint fd;\n+\n+\tif (!pmd_size)\n+\t\tSKIP(return, \"PMD-sized THPs are not supported\");\n+\tif (geteuid())\n+\t\tSKIP(return, \"requires root to change credentials\");\n+\tif (self-\u003ekpageflags_fd \u003c 0)\n+\t\tSKIP(return, \"requires root to read page flags\");\n+\tfd = create_shmem_file(_metadata);\n+\tshared = map_aligned_file(_metadata, fd, pmd_size, MAP_SHARED);\n+\tif (!collapse(shared, pmd_size, check_huge_file))\n+\t\tSKIP(return, \"could not allocate a PMD-sized shmem THP\");\n+\tASSERT_EQ(munmap(shared, pmd_size), 0);\n+\n+\t/* Mapping half of the folio leaves it mapped by PTEs. */\n+\tmapping = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);\n+\tASSERT_NE(mapping, MAP_FAILED);\n+\tassert_filled(_metadata, mapping, size);\n+\tpfn = pagemap_get_pfn(self-\u003epagemap_fd, mapping);\n+\tASSERT_NE(pfn, -1UL);\n+\tASSERT_EQ(pageflags_get(pfn, self-\u003ekpageflags_fd, \u0026flags), 0);\n+\tif (!(flags \u0026 KPF_COMPOUND_HEAD))\n+\t\tSKIP(return, \"the shmem THP was split\");\n+\tASSERT_EQ(fchmod(fd, 0400), 0);\n+\tif (!drop_privileges())\n+\t\tSKIP(return, \"could not drop privileges\");\n+\n+\tASSERT_EQ(madvise(mapping, size / 2, MADV_PAGEOUT), 0);\n+\tfor (offset = 0; offset \u003c size; offset += page_size)\n+\t\tASSERT_TRUE(page_is_present(self-\u003epagemap_fd,\n+\t\t\t\t\t    mapping + offset));\n+\tassert_filled(_metadata, mapping, size);\n+\n+\tASSERT_EQ(munmap(mapping, size), 0);\n+\tclose(fd);\n+}\n+\n+TEST_HARNESS_MAIN\ndiff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh\nindex a1b45a3dedaee..0e6a24659d127 100755\n--- a/tools/testing/selftests/mm/run_vmtests.sh\n+++ b/tools/testing/selftests/mm/run_vmtests.sh\n@@ -53,6 +53,8 @@ separated by spaces:\n \ttest madvise(2) MADV_GUARD_INSTALL and MADV_GUARD_REMOVE options\n - madv_populate\n \ttest memadvise(2) MADV_POPULATE_{READ,WRITE} options\n+- madvise\n+\ttest madvise(2) MADV_COLD and MADV_PAGEOUT\n - memfd_secret\n \ttest memfd_secret(2)\n - process_mrelease\n@@ -330,6 +332,9 @@ CATEGORY=\"madv_guard\" run_test ./guard-regions\n # MADV_POPULATE_READ and MADV_POPULATE_WRITE tests\n CATEGORY=\"madv_populate\" run_test ./madv_populate\n \n+# MADV_COLD and MADV_PAGEOUT tests\n+CATEGORY=\"madvise\" run_test ./madvise\n+\n # PROCESS_MADV test\n CATEGORY=\"process_madv\" run_test ./process_madv\n \ndiff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h\nindex b5d59729d4327..95898e045bacb 100644\n--- a/tools/testing/selftests/mm/vm_util.h\n+++ b/tools/testing/selftests/mm/vm_util.h\n@@ -24,6 +24,7 @@\n \n #define KPF_COMPOUND_HEAD             BIT_ULL(15)\n #define KPF_COMPOUND_TAIL             BIT_ULL(16)\n+#define KPF_UNEVICTABLE               BIT_ULL(18)\n #define KPF_HWPOISON                  BIT_ULL(19)\n #define KPF_THP                       BIT_ULL(22)\n /*\n"
}

2/1 2026/10/14 23:56 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only user-space selftests and test runner scripts under tools/testing/selftests/mm/. It does not touch any kernel code or affect the compiled kernel image (vmlinux).",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


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

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 16bdbf96d1ba8f126596d177e62ab7a93261fc03
Author: syz-cluster <triage@syzkaller.com>
Date:   Wed Oct 14 23:56:04 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile
index beacc0f873049..07d61110eb240 100644
--- a/tools/testing/selftests/mm/Makefile
+++ b/tools/testing/selftests/mm/Makefile
@@ -69,6 +69,7 @@ TEST_GEN_FILES += hugetlb-shm
 TEST_GEN_FILES += hugetlb-soft-offline
 TEST_GEN_FILES += khugepaged
 TEST_GEN_FILES += madv_populate
+TEST_GEN_FILES += madvise
 TEST_GEN_FILES += map_fixed_noreplace
 TEST_GEN_FILES += map_populate
 ifneq (,$(filter $(ARCH),arm64 riscv riscv64 x86 x86_64 loongarch32 loongarch64))
@@ -156,6 +157,7 @@ TEST_PROGS += ksft_ksm.sh
 TEST_PROGS += ksft_ksm_numa.sh
 TEST_PROGS += ksft_madv_guard.sh
 TEST_PROGS += ksft_madv_populate.sh
+TEST_PROGS += ksft_madvise.sh
 TEST_PROGS += ksft_memfd_secret.sh
 TEST_PROGS += ksft_memory_failure.sh
 TEST_PROGS += ksft_migration.sh
diff --git a/tools/testing/selftests/mm/ksft_madvise.sh b/tools/testing/selftests/mm/ksft_madvise.sh
new file mode 100755
index 0000000000000..29cbbca800f2c
--- /dev/null
+++ b/tools/testing/selftests/mm/ksft_madvise.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -e
+# SPDX-License-Identifier: GPL-2.0
+
+./run_vmtests.sh -t madvise
diff --git a/tools/testing/selftests/mm/madvise.c b/tools/testing/selftests/mm/madvise.c
new file mode 100644
index 0000000000000..d59c03ec8191e
--- /dev/null
+++ b/tools/testing/selftests/mm/madvise.c
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MADV_COLD and MADV_PAGEOUT tests.
+ */
+#define _GNU_SOURCE
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <sched.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/mman.h>
+#include <sys/shm.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "kselftest_harness.h"
+#include "vm_util.h"
+
+#ifndef MADV_COLLAPSE
+#define MADV_COLLAPSE 25
+#endif
+
+/* Reclaim can fail transiently, so retry for up to a second. */
+#define PAGEOUT_RETRIES 100
+
+static size_t page_size;
+static size_t pmd_size;
+
+FIXTURE(madvise)
+{
+	int pagemap_fd;
+	/* Opening it needs CAP_SYS_ADMIN, -1 otherwise. */
+	int kpageflags_fd;
+};
+
+FIXTURE_SETUP(madvise)
+{
+	page_size = getpagesize();
+	pmd_size = read_pmd_pagesize();
+
+	self->pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+	ASSERT_GE(self->pagemap_fd, 0);
+	self->kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
+}
+
+FIXTURE_TEARDOWN(madvise)
+{
+	if (self->kpageflags_fd >= 0)
+		close(self->kpageflags_fd);
+	close(self->pagemap_fd);
+}
+
+/*
+ * Reserve a PMD-aligned range, so that memory mapped over it with MAP_FIXED
+ * can be backed by PMD-mapped THPs.
+ */
+static char *reserve_aligned(struct __test_metadata *_metadata, size_t size)
+{
+	char *reservation, *aligned;
+
+	reservation = mmap(NULL, size + pmd_size, PROT_NONE,
+			   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(reservation, MAP_FAILED);
+
+	aligned = (char *)(((uintptr_t)reservation + pmd_size - 1) &
+			   ~(pmd_size - 1));
+	if (aligned != reservation)
+		ASSERT_EQ(munmap(reservation, aligned - reservation), 0);
+	ASSERT_EQ(munmap(aligned + size, reservation + pmd_size - aligned), 0);
+
+	return aligned;
+}
+
+/* Map PMD-aligned anonymous memory filled with 1s. */
+static char *map_aligned_pages(struct __test_metadata *_metadata, size_t size)
+{
+	char *mapping = reserve_aligned(_metadata, size);
+
+	ASSERT_EQ(mmap(mapping, size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
+		  mapping);
+	memset(mapping, 1, size);
+
+	return mapping;
+}
+
+/*
+ * Collapse a PMD-aligned range into PMD-mapped THPs, and confirm the result
+ * with check_huge(). MADV_COLLAPSE may fail transiently with EAGAIN.
+ */
+static bool collapse(char *mapping, size_t size,
+		     bool (*check_huge)(void *, size_t, int, uint64_t))
+{
+	int nr_hpages = size / pmd_size;
+	int retry;
+
+	for (retry = 0; retry < 10; retry++) {
+		if (madvise(mapping, size, MADV_COLLAPSE)) {
+			if (errno != EAGAIN)
+				return false;
+		} else if (check_huge(mapping, size, nr_hpages, pmd_size)) {
+			return true;
+		}
+		usleep(10000);
+	}
+
+	return false;
+}
+
+/* Map a PMD-mapped anonymous THP filled with 1s, or return NULL. */
+static char *map_thp(struct __test_metadata *_metadata)
+{
+	char *mapping;
+
+	if (!pmd_size)
+		return NULL;
+	mapping = map_aligned_pages(_metadata, pmd_size);
+	if (collapse(mapping, pmd_size, check_huge_anon))
+		return mapping;
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+	return NULL;
+}
+
+/*
+ * Changing the protection of one page PTE-maps a THP. When /proc/kpageflags
+ * is readable, also check that the folio is still large.
+ */
+static void pte_map_thp(struct __test_metadata *_metadata, char *mapping,
+			int pagemap_fd, int kpageflags_fd)
+{
+	unsigned long pfn;
+	uint64_t flags;
+
+	ASSERT_EQ(mprotect(mapping + page_size, page_size, PROT_READ), 0);
+	ASSERT_EQ(mprotect(mapping + page_size, page_size,
+			   PROT_READ | PROT_WRITE), 0);
+	ASSERT_TRUE(check_huge_anon(mapping, pmd_size, 0, pmd_size));
+	if (kpageflags_fd < 0)
+		return;
+
+	pfn = pagemap_get_pfn(pagemap_fd, mapping);
+	ASSERT_NE(pfn, -1UL);
+	ASSERT_EQ(pageflags_get(pfn, kpageflags_fd, &flags), 0);
+	ASSERT_TRUE(flags & KPF_COMPOUND_HEAD);
+}
+
+static char *map_aligned_file(struct __test_metadata *_metadata, int fd,
+			      size_t size, int flags)
+{
+	char *mapping = reserve_aligned(_metadata, size);
+
+	ASSERT_EQ(mmap(mapping, size, PROT_READ | PROT_WRITE, flags | MAP_FIXED,
+		       fd, 0), mapping);
+
+	return mapping;
+}
+
+/* Create a PMD-sized shmem file filled with 1s. */
+static int create_shmem_file(struct __test_metadata *_metadata)
+{
+	char *shared;
+	int fd;
+
+	fd = memfd_create("madvise-pageout", 0);
+	ASSERT_GE(fd, 0);
+	ASSERT_EQ(ftruncate(fd, pmd_size), 0);
+	shared = map_aligned_file(_metadata, fd, pmd_size, MAP_SHARED);
+	memset(shared, 1, pmd_size);
+	ASSERT_EQ(munmap(shared, pmd_size), 0);
+
+	return fd;
+}
+
+static void assert_filled(struct __test_metadata *_metadata,
+			  const char *mapping, size_t size)
+{
+	size_t offset;
+
+	for (offset = 0; offset < size; offset += page_size) {
+		ASSERT_EQ(mapping[offset], 1)
+			TH_LOG("memory changed at offset %zu", offset);
+	}
+}
+
+/* Page out the range until its first page is swapped. */
+static bool pageout_until_swapped(struct __test_metadata *_metadata,
+				  char *mapping, size_t size, int pagemap_fd)
+{
+	int retry;
+
+	for (retry = 0; retry < PAGEOUT_RETRIES; retry++) {
+		ASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), 0);
+		if (pagemap_is_swapped(pagemap_fd, mapping))
+			return true;
+		usleep(10000);
+	}
+
+	return false;
+}
+
+/* Page out the range until its first page is no longer mapped. */
+static bool pageout_until_evicted(struct __test_metadata *_metadata,
+				  char *mapping, size_t size, int pagemap_fd)
+{
+	int retry;
+
+	for (retry = 0; retry < PAGEOUT_RETRIES; retry++) {
+		ASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), 0);
+		if (!pagemap_is_populated(pagemap_fd, mapping))
+			return true;
+		usleep(10000);
+	}
+
+	return false;
+}
+
+/*
+ * Keep this task on one CPU, so that draining that CPU's LRU batches drains
+ * the folios this task faulted in.
+ */
+static void pin_to_current_cpu(struct __test_metadata *_metadata)
+{
+	int cpu = sched_getcpu();
+	cpu_set_t mask;
+
+	ASSERT_GE(cpu, 0);
+	CPU_ZERO(&mask);
+	CPU_SET(cpu, &mask);
+	ASSERT_EQ(sched_setaffinity(0, sizeof(mask), &mask), 0);
+}
+
+static bool page_is_present(int pagemap_fd, char *addr)
+{
+	return pagemap_get_entry(pagemap_fd, addr) & PM_PRESENT;
+}
+
+/* Map an unlinked, zero-filled temporary file with the given permissions. */
+static char *map_temp_file(struct __test_metadata *_metadata, size_t size,
+			   int flags, mode_t mode)
+{
+	char template[] = "/tmp/madvise-pageout-XXXXXX";
+	char *mapping;
+	int fd;
+
+	fd = mkstemp(template);
+	ASSERT_GE(fd, 0);
+	ASSERT_EQ(unlink(template), 0);
+	ASSERT_EQ(ftruncate(fd, size), 0);
+	ASSERT_EQ(fchmod(fd, mode), 0);
+
+	mapping = mmap(NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	close(fd);
+
+	return mapping;
+}
+
+/* Become an unprivileged user without capabilities. */
+static bool drop_privileges(void)
+{
+	return !setgroups(0, NULL) && !setgid(65534) && !setuid(65534);
+}
+
+/*
+ * Both hints are best effort, so these tests check what userspace can rely
+ * on: which mappings are accepted, which pages must never be reclaimed, and
+ * that neither hint changes memory contents or populates the range. A test
+ * that needs a page to be reclaimed first checks that reclaim works at all,
+ * and skips if it does not.
+ */
+
+/* Neither hint may populate a hole between two populated pages. */
+static void check_skips_pte_hole(struct __test_metadata *_metadata,
+				 int pagemap_fd, int advice)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+	mapping[2 * page_size] = 1;
+	ASSERT_FALSE(pagemap_is_populated(pagemap_fd, mapping + page_size));
+
+	ASSERT_EQ(madvise(mapping, 3 * page_size, advice), 0);
+	ASSERT_FALSE(pagemap_is_populated(pagemap_fd, mapping + page_size));
+	ASSERT_EQ(mapping[0], 1);
+	ASSERT_EQ(mapping[2 * page_size], 1);
+
+	ASSERT_EQ(munmap(mapping, 3 * page_size), 0);
+}
+
+TEST_F(madvise, cold_skips_pte_hole)
+{
+	check_skips_pte_hole(_metadata, self->pagemap_fd, MADV_COLD);
+}
+
+TEST_F(madvise, pageout_skips_pte_hole)
+{
+	check_skips_pte_hole(_metadata, self->pagemap_fd, MADV_PAGEOUT);
+}
+
+/* Neither hint may be applied to locked memory. */
+TEST_F(madvise, locked_vma_is_rejected)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+	if (mlock(mapping, page_size))
+		SKIP(return, "could not lock a page");
+
+	ASSERT_EQ(madvise(mapping, page_size, MADV_COLD), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/* Neither hint may be applied to hugetlb memory. */
+TEST_F(madvise, hugetlb_vma_is_rejected)
+{
+	size_t size = default_huge_page_size();
+	char *mapping;
+
+	if (!size)
+		SKIP(return, "hugetlb is not supported");
+	mapping = mmap(NULL, size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, 0);
+	if (mapping == MAP_FAILED)
+		SKIP(return, "no hugetlb pages are available");
+	mapping[0] = 1;
+
+	ASSERT_EQ(madvise(mapping, size, MADV_COLD), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(madvise(mapping, size, MADV_PAGEOUT), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, size), 0);
+}
+
+/* A zero-length range is accepted. */
+TEST_F(madvise, zero_length_is_accepted)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+
+	ASSERT_EQ(madvise(mapping, 0, MADV_COLD), 0);
+	ASSERT_EQ(madvise(mapping, 0, MADV_PAGEOUT), 0);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/* The start of the range must be page-aligned. */
+TEST_F(madvise, unaligned_start_is_rejected)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+
+	ASSERT_EQ(madvise(mapping + 1, page_size, MADV_COLD), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(madvise(mapping + 1, page_size, MADV_PAGEOUT), -1);
+	ASSERT_EQ(errno, EINVAL);
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, 2 * page_size), 0);
+}
+
+/*
+ * An unmapped gap in the range makes either hint fail with ENOMEM, after
+ * advising the mapped pages on both sides of the gap. Only MADV_PAGEOUT has
+ * an effect on those pages that the test can observe.
+ */
+TEST_F(madvise, unmapped_gap_fails_after_advising)
+{
+	char *mapping, *control;
+	bool swapped = false;
+	int retry;
+
+	pin_to_current_cpu(_metadata);
+
+	/* Make sure MADV_PAGEOUT can swap at all before relying on it. */
+	control = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(control, MAP_FAILED);
+	control[0] = 1;
+	if (!pageout_until_swapped(_metadata, control, page_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not swap the page");
+
+	mapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+	mapping[2 * page_size] = 1;
+	ASSERT_EQ(munmap(mapping + page_size, page_size), 0);
+
+	ASSERT_EQ(madvise(mapping, 3 * page_size, MADV_COLD), -1);
+	ASSERT_EQ(errno, ENOMEM);
+	ASSERT_EQ(mapping[0], 1);
+	ASSERT_EQ(mapping[2 * page_size], 1);
+
+	for (retry = 0; retry < PAGEOUT_RETRIES && !swapped; retry++) {
+		ASSERT_EQ(madvise(mapping, 3 * page_size, MADV_PAGEOUT), -1);
+		ASSERT_EQ(errno, ENOMEM);
+		swapped = pagemap_is_swapped(self->pagemap_fd, mapping) &&
+			  pagemap_is_swapped(self->pagemap_fd,
+					     mapping + 2 * page_size);
+		if (!swapped)
+			usleep(10000);
+	}
+	ASSERT_TRUE(swapped)
+		TH_LOG("MADV_PAGEOUT did not page out both sides of the gap");
+	ASSERT_EQ(mapping[0], 1);
+	ASSERT_EQ(mapping[2 * page_size], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+	ASSERT_EQ(munmap(mapping + 2 * page_size, page_size), 0);
+	ASSERT_EQ(munmap(control, page_size), 0);
+}
+
+/* Neither hint may fault a swapped page back in. */
+static void check_skips_swapped_pte(struct __test_metadata *_metadata,
+				    int pagemap_fd, int advice)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+	if (!pageout_until_swapped(_metadata, mapping, page_size, pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not swap the page");
+
+	ASSERT_EQ(madvise(mapping, page_size, advice), 0);
+	ASSERT_TRUE(pagemap_is_swapped(pagemap_fd, mapping));
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+TEST_F(madvise, cold_skips_swapped_pte)
+{
+	check_skips_swapped_pte(_metadata, self->pagemap_fd, MADV_COLD);
+}
+
+TEST_F(madvise, pageout_skips_swapped_pte)
+{
+	check_skips_swapped_pte(_metadata, self->pagemap_fd, MADV_PAGEOUT);
+}
+
+/* MADV_COLD must not discard a dirty page of a shared file mapping. */
+TEST_F(madvise, cold_preserves_dirty_shared_file_page)
+{
+	char *mapping;
+
+	mapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0600);
+	mapping[0] = 1;
+
+	ASSERT_EQ(madvise(mapping, page_size, MADV_COLD), 0);
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/*
+ * MADV_PAGEOUT may write a dirty shared file page back or leave it resident,
+ * but must not discard it.
+ */
+TEST_F(madvise, pageout_preserves_dirty_shared_file_page)
+{
+	char *mapping;
+
+	mapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0600);
+	mapping[0] = 1;
+
+	if (!pageout_until_evicted(_metadata, mapping, page_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not unmap the page");
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/* MADV_PAGEOUT must not lose the contents of shared anonymous memory. */
+TEST_F(madvise, pageout_preserves_shared_anon_page)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
+		       MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	mapping[0] = 1;
+
+	if (!pageout_until_evicted(_metadata, mapping, page_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not evict the page");
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/*
+ * SHM_LOCK makes shmem pages unevictable without setting VM_LOCKED on the
+ * VMA, so MADV_PAGEOUT accepts the range but must not reclaim the page.
+ */
+TEST_F(madvise, pageout_skips_unevictable_page)
+{
+	unsigned long pfn;
+	uint64_t flags;
+	char *mapping;
+	int shmid;
+
+	if (self->kpageflags_fd < 0)
+		SKIP(return, "requires root to read page flags");
+	shmid = shmget(IPC_PRIVATE, page_size, IPC_CREAT | 0600);
+	ASSERT_GE(shmid, 0);
+	if (shmctl(shmid, SHM_LOCK, NULL)) {
+		shmctl(shmid, IPC_RMID, NULL);
+		SKIP(return, "could not lock a shmem segment");
+	}
+	mapping = shmat(shmid, NULL, 0);
+	/* The segment is destroyed once it is detached. */
+	shmctl(shmid, IPC_RMID, NULL);
+	ASSERT_NE(mapping, (void *)-1);
+	pin_to_current_cpu(_metadata);
+	mapping[0] = 1;
+
+	/*
+	 * MADV_COLD drains this CPU's LRU add batch, which makes the page
+	 * unevictable.
+	 */
+	ASSERT_EQ(madvise(mapping, page_size, MADV_COLD), 0);
+	pfn = pagemap_get_pfn(self->pagemap_fd, mapping);
+	ASSERT_NE(pfn, -1UL);
+	ASSERT_EQ(pageflags_get(pfn, self->kpageflags_fd, &flags), 0);
+	if (!(flags & KPF_UNEVICTABLE))
+		SKIP(return, "could not make the page unevictable");
+
+	ASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), 0);
+	ASSERT_TRUE(pagemap_is_populated(self->pagemap_fd, mapping));
+	ASSERT_EQ(mapping[0], 1);
+
+	ASSERT_EQ(shmdt(mapping), 0);
+}
+
+/* MADV_PAGEOUT of a whole THP must swap it without corrupting it. */
+TEST_F(madvise, pageout_swaps_thp)
+{
+	char *mapping;
+
+	mapping = map_thp(_metadata);
+	if (!mapping)
+		SKIP(return, "could not allocate a PMD-sized THP");
+
+	if (!pageout_until_swapped(_metadata, mapping, pmd_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not swap the THP");
+	assert_filled(_metadata, mapping, pmd_size);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+}
+
+/*
+ * MADV_PAGEOUT must not reclaim pages outside the advised range. kswapd or
+ * page migration could also unmap them, but neither should happen when the
+ * selftests run by themselves.
+ */
+TEST_F(madvise, pageout_stays_within_range)
+{
+	char *mapping;
+
+	mapping = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
+		       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	memset(mapping, 1, 3 * page_size);
+	if (!pageout_until_swapped(_metadata, mapping + page_size, page_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not swap the page");
+
+	ASSERT_TRUE(page_is_present(self->pagemap_fd, mapping));
+	ASSERT_TRUE(page_is_present(self->pagemap_fd, mapping + 2 * page_size));
+	assert_filled(_metadata, mapping, 3 * page_size);
+
+	ASSERT_EQ(munmap(mapping, 3 * page_size), 0);
+}
+
+/*
+ * MADV_PAGEOUT must not reclaim the unadvised half of a THP: the THP must be
+ * split before reclaim, or skipped. See pageout_stays_within_range about
+ * other unmapping.
+ */
+static void pageout_half_thp(struct __test_metadata *_metadata, char *mapping,
+			     int pagemap_fd)
+{
+	size_t offset;
+
+	if (!pageout_until_swapped(_metadata, mapping, pmd_size / 2,
+				   pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not swap the advised half");
+
+	for (offset = pmd_size / 2; offset < pmd_size; offset += page_size)
+		ASSERT_TRUE(page_is_present(pagemap_fd, mapping + offset));
+	assert_filled(_metadata, mapping, pmd_size);
+}
+
+TEST_F(madvise, pageout_stays_within_pmd_mapped_thp)
+{
+	char *mapping;
+
+	mapping = map_thp(_metadata);
+	if (!mapping)
+		SKIP(return, "could not allocate a PMD-sized THP");
+
+	pageout_half_thp(_metadata, mapping, self->pagemap_fd);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+}
+
+TEST_F(madvise, pageout_stays_within_pte_mapped_thp)
+{
+	char *mapping;
+
+	mapping = map_thp(_metadata);
+	if (!mapping)
+		SKIP(return, "could not allocate a PMD-sized THP");
+	pte_map_thp(_metadata, mapping, self->pagemap_fd, self->kpageflags_fd);
+
+	pageout_half_thp(_metadata, mapping, self->pagemap_fd);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+}
+
+/* MADV_COLD on part of a THP may split it, but must not change it. */
+TEST_F(madvise, cold_preserves_half_of_pmd_mapped_thp)
+{
+	char *mapping;
+
+	mapping = map_thp(_metadata);
+	if (!mapping)
+		SKIP(return, "could not allocate a PMD-sized THP");
+
+	ASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_COLD), 0);
+	assert_filled(_metadata, mapping, pmd_size);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+}
+
+TEST_F(madvise, cold_preserves_half_of_pte_mapped_thp)
+{
+	char *mapping;
+
+	mapping = map_thp(_metadata);
+	if (!mapping)
+		SKIP(return, "could not allocate a PMD-sized THP");
+	pte_map_thp(_metadata, mapping, self->pagemap_fd, self->kpageflags_fd);
+
+	ASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_COLD), 0);
+	assert_filled(_metadata, mapping, pmd_size);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+}
+
+/*
+ * The owner of a file may page out its clean page-cache pages. Run as an
+ * unprivileged owner, so that capabilities cannot stand in for ownership.
+ */
+TEST_F(madvise, pageout_evicts_owned_file_page)
+{
+	char *mapping, *control;
+
+	if (!geteuid() && !drop_privileges())
+		SKIP(return, "could not drop privileges");
+	pin_to_current_cpu(_metadata);
+
+	/* Make sure a writable file page can be paged out at all. */
+	control = map_temp_file(_metadata, page_size, MAP_PRIVATE, 0600);
+	ASSERT_EQ(control[0], 0);
+	if (!pageout_until_evicted(_metadata, control, page_size,
+				   self->pagemap_fd))
+		SKIP(return, "MADV_PAGEOUT did not evict a file page");
+
+	mapping = map_temp_file(_metadata, page_size, MAP_PRIVATE, 0400);
+	ASSERT_EQ(mapping[0], 0);
+	ASSERT_TRUE(pageout_until_evicted(_metadata, mapping, page_size,
+					  self->pagemap_fd));
+	ASSERT_EQ(mapping[0], 0);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+	ASSERT_EQ(munmap(control, page_size), 0);
+}
+
+/*
+ * A caller that neither owns nor may write a file must not page out its
+ * shared mappings. MADV_PAGEOUT succeeds but leaves the page resident.
+ */
+TEST_F(madvise, pageout_skips_unauthorized_shared_file)
+{
+	char *mapping;
+
+	if (geteuid())
+		SKIP(return, "requires root to change credentials");
+	mapping = map_temp_file(_metadata, page_size, MAP_SHARED, 0400);
+	if (!drop_privileges())
+		SKIP(return, "could not drop privileges");
+
+	ASSERT_EQ(mapping[0], 0);
+	ASSERT_TRUE(pagemap_is_populated(self->pagemap_fd, mapping));
+	ASSERT_EQ(madvise(mapping, page_size, MADV_PAGEOUT), 0);
+	ASSERT_TRUE(pagemap_is_populated(self->pagemap_fd, mapping));
+	ASSERT_EQ(mapping[0], 0);
+
+	ASSERT_EQ(munmap(mapping, page_size), 0);
+}
+
+/*
+ * A caller that neither owns nor may write a file may page out the anonymous
+ * COW pages of a private mapping of it, but must leave the file pages
+ * resident.
+ */
+TEST_F(madvise, pageout_private_file_reclaims_only_cow)
+{
+	char *mapping;
+	bool swapped;
+
+	if (geteuid())
+		SKIP(return, "requires root to change credentials");
+	mapping = map_temp_file(_metadata, 2 * page_size, MAP_PRIVATE,
+				0400);
+	if (!drop_privileges())
+		SKIP(return, "could not drop privileges");
+
+	ASSERT_EQ(mapping[0], 0);
+	ASSERT_EQ(mapping[page_size], 0);
+	mapping[0] = 1;
+
+	ASSERT_EQ(madvise(mapping, 2 * page_size, MADV_PAGEOUT), 0);
+	ASSERT_TRUE(pagemap_is_populated(self->pagemap_fd,
+					 mapping + page_size));
+	swapped = pagemap_is_swapped(self->pagemap_fd, mapping);
+	ASSERT_EQ(mapping[0], 1);
+	ASSERT_EQ(mapping[page_size], 0);
+	if (!swapped)
+		SKIP(return, "MADV_PAGEOUT did not swap the COW page");
+
+	ASSERT_EQ(munmap(mapping, 2 * page_size), 0);
+}
+
+/*
+ * An unauthorized caller must not page out a large file folio of a private
+ * mapping, even when the advised range only partly covers it. See
+ * pageout_stays_within_range about other unmapping.
+ */
+TEST_F(madvise, pageout_private_file_skips_large_file_folio)
+{
+	char *mapping;
+	size_t offset;
+	int fd;
+
+	if (!pmd_size)
+		SKIP(return, "PMD-sized THPs are not supported");
+	if (geteuid())
+		SKIP(return, "requires root to change credentials");
+	fd = create_shmem_file(_metadata);
+	mapping = map_aligned_file(_metadata, fd, pmd_size, MAP_PRIVATE);
+	if (!collapse(mapping, pmd_size, check_huge_file))
+		SKIP(return, "could not allocate a PMD-sized shmem THP");
+	ASSERT_EQ(fchmod(fd, 0400), 0);
+	if (!drop_privileges())
+		SKIP(return, "could not drop privileges");
+
+	ASSERT_EQ(madvise(mapping, pmd_size / 2, MADV_PAGEOUT), 0);
+	for (offset = 0; offset < pmd_size; offset += page_size)
+		ASSERT_TRUE(page_is_present(self->pagemap_fd,
+					    mapping + offset));
+	assert_filled(_metadata, mapping, pmd_size);
+
+	ASSERT_EQ(munmap(mapping, pmd_size), 0);
+	close(fd);
+}
+
+/*
+ * An unauthorized caller must not page out a large file folio that a private
+ * mapping maps with PTEs, even when the advised range covers only part of it.
+ * See pageout_stays_within_range about other unmapping.
+ */
+TEST_F(madvise, pageout_private_file_skips_pte_mapped_file_folio)
+{
+	const size_t size = pmd_size / 2;
+	char *mapping, *shared;
+	unsigned long pfn;
+	uint64_t flags;
+	size_t offset;
+	int fd;
+
+	if (!pmd_size)
+		SKIP(return, "PMD-sized THPs are not supported");
+	if (geteuid())
+		SKIP(return, "requires root to change credentials");
+	if (self->kpageflags_fd < 0)
+		SKIP(return, "requires root to read page flags");
+	fd = create_shmem_file(_metadata);
+	shared = map_aligned_file(_metadata, fd, pmd_size, MAP_SHARED);
+	if (!collapse(shared, pmd_size, check_huge_file))
+		SKIP(return, "could not allocate a PMD-sized shmem THP");
+	ASSERT_EQ(munmap(shared, pmd_size), 0);
+
+	/* Mapping half of the folio leaves it mapped by PTEs. */
+	mapping = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+	ASSERT_NE(mapping, MAP_FAILED);
+	assert_filled(_metadata, mapping, size);
+	pfn = pagemap_get_pfn(self->pagemap_fd, mapping);
+	ASSERT_NE(pfn, -1UL);
+	ASSERT_EQ(pageflags_get(pfn, self->kpageflags_fd, &flags), 0);
+	if (!(flags & KPF_COMPOUND_HEAD))
+		SKIP(return, "the shmem THP was split");
+	ASSERT_EQ(fchmod(fd, 0400), 0);
+	if (!drop_privileges())
+		SKIP(return, "could not drop privileges");
+
+	ASSERT_EQ(madvise(mapping, size / 2, MADV_PAGEOUT), 0);
+	for (offset = 0; offset < size; offset += page_size)
+		ASSERT_TRUE(page_is_present(self->pagemap_fd,
+					    mapping + offset));
+	assert_filled(_metadata, mapping, size);
+
+	ASSERT_EQ(munmap(mapping, size), 0);
+	close(fd);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tools/testing/selftests/mm/run_vmtests.sh b/tools/testing/selftests/mm/run_vmtests.sh
index a1b45a3dedaee..0e6a24659d127 100755
--- a/tools/testing/selftests/mm/run_vmtests.sh
+++ b/tools/testing/selftests/mm/run_vmtests.sh
@@ -53,6 +53,8 @@ separated by spaces:
 	test madvise(2) MADV_GUARD_INSTALL and MADV_GUARD_REMOVE options
 - madv_populate
 	test memadvise(2) MADV_POPULATE_{READ,WRITE} options
+- madvise
+	test madvise(2) MADV_COLD and MADV_PAGEOUT
 - memfd_secret
 	test memfd_secret(2)
 - process_mrelease
@@ -330,6 +332,9 @@ CATEGORY="madv_guard" run_test ./guard-regions
 # MADV_POPULATE_READ and MADV_POPULATE_WRITE tests
 CATEGORY="madv_populate" run_test ./madv_populate
 
+# MADV_COLD and MADV_PAGEOUT tests
+CATEGORY="madvise" run_test ./madvise
+
 # PROCESS_MADV test
 CATEGORY="process_madv" run_test ./process_madv
 
diff --git a/tools/testing/selftests/mm/vm_util.h b/tools/testing/selftests/mm/vm_util.h
index b5d59729d4327..95898e045bacb 100644
--- a/tools/testing/selftests/mm/vm_util.h
+++ b/tools/testing/selftests/mm/vm_util.h
@@ -24,6 +24,7 @@
 
 #define KPF_COMPOUND_HEAD             BIT_ULL(15)
 #define KPF_COMPOUND_TAIL             BIT_ULL(16)
+#define KPF_UNEVICTABLE               BIT_ULL(18)
 #define KPF_HWPOISON                  BIT_ULL(19)
 #define KPF_THP                       BIT_ULL(22)
 /*

3/2 2026/10/14 23:56 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 13966
							output: 19
							thoughts: 864
						

Thoughts:
**Patch Evaluation: Selftests and WorthFuzzing**

Okay, let's break this down. My goal is to determine if this patch is "WorthFuzzing" or not. Based on the prompt, it looks like I need to look at this in the context of the defined negative criteria. The user-space selftests, samples, or scripts should have a `WorthFuzzing` of `false`.

First, let's look at the diff. The patch touches several files: `tools/testing/selftests/mm/Makefile`, `tools/testing/selftests/mm/ksft_madvise.sh`, `tools/testing/selftests/mm/madvise.c`, `tools/testing/selftests/mm/run_vmtests.sh`, and `tools/testing/selftests/mm/vm_util.h`. That seems to be the entire commit, since the diff is complete. All the modified files are in `tools/testing/selftests/mm/`, which clearly places them within the user-space self-test framework.

Given the instructions, this seems straightforward. But I'll double-check to be sure that `vm_util.h` isn't used elsewhere in the kernel, just in case. But it looks like it's all contained within the `tools/testing/selftests/` directory.

So, according to the negative criteria, these changes are non-kernel and user-space, therefore I'll set the `WorthFuzzing` flag to `false`.



4/2 2026/10/14 23:56 tool grepper
0m Args:
{
  "Expression": "vm_util\\.h"
}

Results:
{
  "Output": "\nFull output is too long, showing 200 out of 1001 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (231 files in total):\ntools/testing/selftests/kvm/access_tracking_perf_test.c:1\ntools/testing/selftests/kvm/arm64/aarch32_id_regs.c:1\ntools/testing/selftests/kvm/arm64/at.c:1\ntools/testing/selftests/kvm/arm64/debug-exceptions.c:1\ntools/testing/selftests/kvm/arm64/get-reg-list.c:1\ntools/testing/selftests/kvm/arm64/hello_el2.c:1\ntools/testing/selftests/kvm/arm64/hidden_features.c:1\ntools/testing/selftests/kvm/arm64/host_sve.c:1\ntools/testing/selftests/kvm/arm64/hypercalls.c:1\ntools/testing/selftests/kvm/arm64/idreg-idst.c:1\ntools/testing/selftests/kvm/arm64/kvm-uuid.c:1\ntools/testing/selftests/kvm/arm64/no-vgic.c:1\ntools/testing/selftests/kvm/arm64/page_fault_test.c:1\ntools/testing/selftests/kvm/arm64/psci_test.c:1\ntools/testing/selftests/kvm/arm64/sea_to_user.c:1\ntools/testing/selftests/kvm/arm64/set_id_regs.c:1\ntools/testing/selftests/kvm/arm64/stage2_block_transitions.c:1\ntools/testing/selftests/kvm/arm64/vcpu_width_config.c:1\ntools/testing/selftests/kvm/arm64/vgic_init.c:1\ntools/testing/selftests/kvm/arm64/vgic_irq.c:1\ntools/testing/selftests/kvm/arm64/vgic_its_save.c:1\ntools/testing/selftests/kvm/arm64/vgic_lpi_stress.c:1\ntools/testing/selftests/kvm/arm64/vgic_v5.c:1\ntools/testing/selftests/kvm/arm64/vpmu_counter_access.c:1\ntools/testing/selftests/kvm/coalesced_io_test.c:1\ntools/testing/selftests/kvm/demand_paging_test.c:1\ntools/testing/selftests/kvm/dirty_log_perf_test.c:1\ntools/testing/selftests/kvm/dirty_log_test.c:1\ntools/testing/selftests/kvm/get-reg-list.c:1\ntools/testing/selftests/kvm/guest_memfd_test.c:1\ntools/testing/selftests/kvm/guest_print_test.c:1\ntools/testing/selftests/kvm/hardware_disable_test.c:1\ntools/testing/selftests/kvm/include/arm64/processor.h:1\ntools/testing/selftests/kvm/include/arm64/ucall.h:1\ntools/testing/selftests/kvm/include/arm64/vgic.h:1\ntools/testing/selftests/kvm/include/guest_modes.h:1\ntools/testing/selftests/kvm/include/loongarch/ucall.h:1\ntools/testing/selftests/kvm/include/memstress.h:1\ntools/testing/selftests/kvm/include/riscv/processor.h:1\ntools/testing/selftests/kvm/include/s390/debug_print.h:1\ntools/testing/selftests/kvm/include/s390/ucall.h:1\ntools/testing/selftests/kvm/include/timer_test.h:1\ntools/testing/selftests/kvm/include/x86/processor.h:1\ntools/testing/selftests/kvm/include/x86/sev.h:2\ntools/testing/selftests/kvm/include/x86/smm.h:1\ntools/testing/selftests/kvm/include/x86/ucall.h:1\ntools/testing/selftests/kvm/irq_test.c:1\ntools/testing/selftests/kvm/irqfd_test.c:1\ntools/testing/selftests/kvm/kvm_binary_stats_test.c:1\ntools/testing/selftests/kvm/kvm_create_max_vcpus.c:1\n... and 181 more files\n\ntools/testing/selftests/kvm/access_tracking_perf_test.c-47-#include \"kvm_syscalls.h\"\ntools/testing/selftests/kvm/access_tracking_perf_test.c:48:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/access_tracking_perf_test.c-49-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/arm64/aarch32_id_regs.c-12-\ntools/testing/selftests/kvm/arm64/aarch32_id_regs.c:13:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/aarch32_id_regs.c-14-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/at.c-4- */\ntools/testing/selftests/kvm/arm64/at.c:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/at.c-6-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/debug-exceptions.c-2-#include \u003ctest_util.h\u003e\ntools/testing/selftests/kvm/arm64/debug-exceptions.c:3:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/debug-exceptions.c-4-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/arm64/get-reg-list.c-13-#include \u003cstdio.h\u003e\ntools/testing/selftests/kvm/arm64/get-reg-list.c:14:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/get-reg-list.c-15-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/arm64/hello_el2.c-6- */\ntools/testing/selftests/kvm/arm64/hello_el2.c:7:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/hello_el2.c-8-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/hidden_features.c-9- */\ntools/testing/selftests/kvm/arm64/hidden_features.c:10:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/hidden_features.c-11-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/host_sve.c-12-#include \u003casm/kvm.h\u003e\ntools/testing/selftests/kvm/arm64/host_sve.c:13:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/host_sve.c-14-\n--\ntools/testing/selftests/kvm/arm64/hypercalls.c-13-#include \u003casm/kvm.h\u003e\ntools/testing/selftests/kvm/arm64/hypercalls.c:14:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/hypercalls.c-15-\n--\ntools/testing/selftests/kvm/arm64/idreg-idst.c-8-#include \u003ctest_util.h\u003e\ntools/testing/selftests/kvm/arm64/idreg-idst.c:9:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/idreg-idst.c-10-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/arm64/kvm-uuid.c-7-#include \u003casm/kvm.h\u003e\ntools/testing/selftests/kvm/arm64/kvm-uuid.c:8:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/kvm-uuid.c-9-\n--\ntools/testing/selftests/kvm/arm64/no-vgic.c-8-#include \u003ctest_util.h\u003e\ntools/testing/selftests/kvm/arm64/no-vgic.c:9:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/no-vgic.c-10-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/arm64/page_fault_test.c-12-#include \u003ctest_util.h\u003e\ntools/testing/selftests/kvm/arm64/page_fault_test.c:13:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/page_fault_test.c-14-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/arm64/psci_test.c-17-\ntools/testing/selftests/kvm/arm64/psci_test.c:18:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/psci_test.c-19-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/sea_to_user.c-22-#include \"test_util.h\"\ntools/testing/selftests/kvm/arm64/sea_to_user.c:23:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/sea_to_user.c-24-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/set_id_regs.c-12-#include \u003cstdint.h\u003e\ntools/testing/selftests/kvm/arm64/set_id_regs.c:13:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/set_id_regs.c-14-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/stage2_block_transitions.c-38-\ntools/testing/selftests/kvm/arm64/stage2_block_transitions.c:39:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/stage2_block_transitions.c-40-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/vcpu_width_config.c-11-\ntools/testing/selftests/kvm/arm64/vcpu_width_config.c:12:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vcpu_width_config.c-13-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/vgic_init.c-14-#include \"test_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_init.c:15:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_init.c-16-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/vgic_irq.c-16-#include \"test_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_irq.c:17:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_irq.c-18-#include \"gic.h\"\n--\ntools/testing/selftests/kvm/arm64/vgic_its_save.c-23-\ntools/testing/selftests/kvm/arm64/vgic_its_save.c:24:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_its_save.c-25-#include \"gic.h\"\n--\ntools/testing/selftests/kvm/arm64/vgic_lpi_stress.c-12-\ntools/testing/selftests/kvm/arm64/vgic_lpi_stress.c:13:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_lpi_stress.c-14-#include \"gic.h\"\n--\ntools/testing/selftests/kvm/arm64/vgic_v5.c-10-#include \"test_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_v5.c:11:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/arm64/vgic_v5.c-12-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/arm64/vpmu_counter_access.c-14- */\ntools/testing/selftests/kvm/arm64/vpmu_counter_access.c:15:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/arm64/vpmu_counter_access.c-16-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/coalesced_io_test.c-9-\ntools/testing/selftests/kvm/coalesced_io_test.c:10:#include \u003ckvm_util.h\u003e\ntools/testing/selftests/kvm/coalesced_io_test.c-11-#include \u003cprocessor.h\u003e\n--\ntools/testing/selftests/kvm/demand_paging_test.c-16-\ntools/testing/selftests/kvm/demand_paging_test.c:17:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/demand_paging_test.c-18-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/dirty_log_perf_test.c-16-\ntools/testing/selftests/kvm/dirty_log_perf_test.c:17:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/dirty_log_perf_test.c-18-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/dirty_log_test.c-18-\ntools/testing/selftests/kvm/dirty_log_test.c:19:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/dirty_log_test.c-20-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/get-reg-list.c-26-#include \u003csys/wait.h\u003e\ntools/testing/selftests/kvm/get-reg-list.c:27:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/get-reg-list.c-28-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/guest_memfd_test.c-20-#include \"kvm_syscalls.h\"\ntools/testing/selftests/kvm/guest_memfd_test.c:21:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/guest_memfd_test.c-22-#include \"numaif.h\"\n--\ntools/testing/selftests/kvm/guest_print_test.c-13-#include \"test_util.h\"\ntools/testing/selftests/kvm/guest_print_test.c:14:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/guest_print_test.c-15-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/hardware_disable_test.c-16-#include \"kvm_syscalls.h\"\ntools/testing/selftests/kvm/hardware_disable_test.c:17:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/hardware_disable_test.c-18-#include \"ucall_common.h\"\n--\ntools/testing/selftests/kvm/include/arm64/processor.h-9-\ntools/testing/selftests/kvm/include/arm64/processor.h:10:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/arm64/processor.h-11-#include \"ucall_common.h\"\n--\ntools/testing/selftests/kvm/include/arm64/ucall.h-4-\ntools/testing/selftests/kvm/include/arm64/ucall.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/arm64/ucall.h-6-\n--\ntools/testing/selftests/kvm/include/arm64/vgic.h-10-\ntools/testing/selftests/kvm/include/arm64/vgic.h:11:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/arm64/vgic.h-12-\n--\ntools/testing/selftests/kvm/include/guest_modes.h-4- */\ntools/testing/selftests/kvm/include/guest_modes.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/guest_modes.h-6-\n--\ntools/testing/selftests/kvm/include/loongarch/ucall.h-4-\ntools/testing/selftests/kvm/include/loongarch/ucall.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/loongarch/ucall.h-6-\n--\ntools/testing/selftests/kvm/include/memstress.h-12-\ntools/testing/selftests/kvm/include/memstress.h:13:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/memstress.h-14-\n--\ntools/testing/selftests/kvm/include/riscv/processor.h-12-#include \u003casm/vdso/processor.h\u003e\ntools/testing/selftests/kvm/include/riscv/processor.h:13:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/riscv/processor.h-14-\n--\ntools/testing/selftests/kvm/include/s390/debug_print.h-14-#include \"asm/ptrace.h\"\ntools/testing/selftests/kvm/include/s390/debug_print.h:15:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/s390/debug_print.h-16-#include \"sie.h\"\n--\ntools/testing/selftests/kvm/include/s390/ucall.h-4-\ntools/testing/selftests/kvm/include/s390/ucall.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/s390/ucall.h-6-\n--\ntools/testing/selftests/kvm/include/timer_test.h-10-\ntools/testing/selftests/kvm/include/timer_test.h:11:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/timer_test.h-12-\n--\ntools/testing/selftests/kvm/include/x86/processor.h-18-\ntools/testing/selftests/kvm/include/x86/processor.h:19:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/x86/processor.h-20-#include \"ucall_common.h\"\n--\ntools/testing/selftests/kvm/include/x86/sev.h-13-\ntools/testing/selftests/kvm/include/x86/sev.h:14:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/x86/sev.h:15:#include \"svm_util.h\"\ntools/testing/selftests/kvm/include/x86/sev.h-16-#include \"processor.h\"\n--\ntools/testing/selftests/kvm/include/x86/smm.h-4-\ntools/testing/selftests/kvm/include/x86/smm.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/x86/smm.h-6-\n--\ntools/testing/selftests/kvm/include/x86/ucall.h-4-\ntools/testing/selftests/kvm/include/x86/ucall.h:5:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/include/x86/ucall.h-6-\n--\ntools/testing/selftests/kvm/irq_test.c-1-// SPDX-License-Identifier: GPL-2.0\ntools/testing/selftests/kvm/irq_test.c:2:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/irq_test.c-3-#include \"test_util.h\"\n--\ntools/testing/selftests/kvm/irqfd_test.c-10-\ntools/testing/selftests/kvm/irqfd_test.c:11:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/irqfd_test.c-12-\n--\ntools/testing/selftests/kvm/kvm_binary_stats_test.c-16-\ntools/testing/selftests/kvm/kvm_binary_stats_test.c:17:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/kvm_binary_stats_test.c-18-#include \"asm/kvm.h\"\n--\ntools/testing/selftests/kvm/kvm_create_max_vcpus.c-15-\ntools/testing/selftests/kvm/kvm_create_max_vcpus.c:16:#include \"kvm_util.h\"\ntools/testing/selftests/kvm/kvm_create_max_vcpus.c-17-#include \"asm/kvm.h\"\n\n"
}

5/2 2026/10/14 23:56 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 21243
							output: 33
							thoughts: 20
						

Thoughts:
Here's my take on those thoughts:

**Analyzing Memory Management Self-Tests**

Okay, time to dig into the memory management self-tests. My initial thought is to navigate to the `tools/testing/selftests/mm/` directory. That's the logical place to start poking around. And, of course, the next step in my expert assessment is to bring in the big guns. I think using a tool like `grepper` is going to be incredibly efficient for quickly searching within those tests. It should let me zero in on key areas and understand the structure of these self-tests much faster than simply browsing the files. I'm aiming for targeted analysis here, so `grepper` is the perfect instrument for the task.



6/2 2026/10/14 23:56 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "vm_util\\.h",
  "PathPrefixes": [
    "mm/",
    "include/"
  ]
}

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

7/2 2026/10/14 23:56 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 21467
							output: 74
							thoughts: 581
						

Thoughts:
**Analysis of Patch for Fuzzing Eligibility**

Okay, let's break this down. My initial assessment is that this patch isn't suitable for fuzzing. I need to justify this decision, and the criteria are clear: `WorthFuzzing=false` only if *all* modified code falls into specific negative categories. I need to make sure this check is exhaustive.

The documentation in SECTION 2 is my guide. The rule is that I should return `WorthFuzzing=false` if the changes are limited to certain non-kernel categories. Specifically, I'm focusing on "Non-kernel and non-executable changes" and, importantly, the subset that's relevant here: "User-space directories, self-tests, samples, or scripts... that do not affect the compiled kernel image (vmlinux) or kernel modules."

Now, let's look at the modified files: `tools/testing/selftests/mm/Makefile`, `tools/testing/selftests/mm/ksft_madvise.sh`, `tools/testing/selftests/mm/madvise.c`, `tools/testing/selftests/mm/run_vmtests.sh`, and `tools/testing/selftests/mm/vm_util.h`. All of these files are located within `tools/testing/selftests/mm/`. This clearly falls into the category of "user-space directories for self-tests and scripts." Crucially, *none* of these modifications impact the compiled kernel image, `vmlinux`, or any kernel modules. This is the key.

Therefore, because all the changes are isolated within this self-test directory and are strictly user-space, the negative criteria are met. I'm confident in setting:

-   `WorthFuzzing: false`
-   `FocusSymbols: []` (empty because we're not fuzzing)
-   `EnableConfigs: []` (also empty for the same reason)

My reasoning is that the patch *only* touches user-space selftests under `tools/testing/selftests/mm/` and *does not* modify any kernel code, nor does it affect the compiled kernel image.

I'm double-checking the instruction to confirm: "Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories... User-space directories, self-tests, samples, or scripts... that do not affect the compiled kernel image (vmlinux) or kernel modules." It all matches.

I'm ready to call `set-results`.



8/2 2026/10/14 23:56 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only user-space selftests and test runner scripts under tools/testing/selftests/mm/. It does not touch any kernel code or affect the compiled kernel image (vmlinux).",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch modifies only user-space selftests and test runner scripts under tools/testing/selftests/mm/. It does not touch any kernel code or affect the compiled kernel image (vmlinux).",
  "WorthFuzzing": false
}

9/1 2026/10/14 23:56 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)