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