Re: [PATCH] arm64/mm: restrict transparent contpte mappings to valid PTEs

From: Zhao Gongyi

Date: Wed Oct 14 2026 - 02:17:16 EST


On 2026/10/13 18:13, Dev Jain wrote:
>
>
> Thanks, could you please give the code of the reproducer. That will
> save us some time.
>
>

Hi Dev,

Sure, sorry for not including it in the first mail.

The standalone reproducer I used is below. It assumes an arm64 kernel
with CONFIG_ARM64_CONTPTE=y and a CONT_PTE_SIZE anonymous mTHP order
available. When run as root, it tries to enable THP and the matching
mTHP order through sysfs. These sysfs writes are best-effort; if the
matching order is not available or cannot be enabled, the reproducer
may not hit the issue.

On a 4K-page kernel, CONT_PTE_SIZE is 64KB.

Compile and run:

gcc -O2 -Wall contpte_guard_repro.c -o contpte_guard_repro
./contpte_guard_repro 100

On my unfixed KASAN kernel with panic_on_warn=1, the first iteration hit
the bad swap entry / swap_put_entries_direct() warning. With this patch,
100 iterations completed without warnings.

Here is the reproducer:

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

#ifndef MADV_HUGEPAGE
#define MADV_HUGEPAGE 14
#endif
#ifndef MADV_POPULATE_WRITE
#define MADV_POPULATE_WRITE 23
#endif
#ifndef MADV_GUARD_INSTALL
#define MADV_GUARD_INSTALL 102
#endif
#ifndef MADV_GUARD_REMOVE
#define MADV_GUARD_REMOVE 103
#endif

static void write_file(const char *path, const char *val)
{
int fd = open(path, O_WRONLY | O_CLOEXEC);

if (fd < 0)
return;
(void)write(fd, val, strlen(val));
close(fd);
}

static void write_clear_refs(void)
{
int fd = open("/proc/self/clear_refs", O_WRONLY | O_CLOEXEC);

if (fd < 0) {
perror("open clear_refs");
exit(1);
}
if (write(fd, "1", 1) != 1) {
perror("write clear_refs");
exit(1);
}
close(fd);
}

static unsigned long contpte_size(unsigned long page_size)
{
if (page_size == 4096)
return 64 * 1024;
if (page_size == 16384 || page_size == 65536)
return 2 * 1024 * 1024;

fprintf(stderr, "unsupported page size %lu\n", page_size);
exit(1);
}

static void enable_mthp(unsigned long size)
{
char path[256];

write_file("/sys/kernel/mm/transparent_hugepage/enabled",
"always\n");
write_file("/sys/kernel/mm/transparent_hugepage/defrag", "always\n");

snprintf(path, sizeof(path),
"/sys/kernel/mm/transparent_hugepage/hugepages-%lukB/enabled",
size / 1024);
write_file(path, "always\n");
}

static void touch_range(char *p, unsigned long len, unsigned long
page_size)
{
for (unsigned long off = 0; off < len; off += page_size)
p[off] = (char)off;
}

int main(int argc, char **argv)
{
const int loops = argc > 1 ? atoi(argv[1]) : 1;
const unsigned long page_size = sysconf(_SC_PAGESIZE);
const unsigned long csize = contpte_size(page_size);
const unsigned long nr_pages = 2 * csize / page_size;
const unsigned long len = nr_pages * page_size;
const unsigned long map_len = len + csize;
const unsigned long guard_idx = csize / page_size - 3;

enable_mthp(csize);

for (int i = 0; i < loops; i++) {
char *map, *p;

map = mmap(NULL, map_len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map == MAP_FAILED) {
perror("mmap");
return 1;
}

p = (char *)(((unsigned long)map + csize - 1) & ~(csize - 1));

if (madvise(p, len, MADV_HUGEPAGE))
perror("madvise MADV_HUGEPAGE");
if (madvise(p, len, MADV_POPULATE_WRITE))
perror("madvise MADV_POPULATE_WRITE");

touch_range(p, len, page_size);

if (mprotect(p, len, PROT_NONE)) {
perror("mprotect PROT_NONE");
return 1;
}
if (madvise(p + guard_idx * page_size, page_size,
MADV_GUARD_INSTALL)) {
perror("madvise MADV_GUARD_INSTALL");
return 1;
}

write_clear_refs();

if (munmap(map, map_len)) {
perror("munmap");
return 1;
}
}

return 0;
}


Thanks,
Zhao