-
-
Save 48ca/11d1e466deee032cb35aa8c2280f93b0 to your computer and use it in GitHub Desktop.
HugeTLB contiguous PTE page fault loop reproducer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: GPL-2.0 | |
/* | |
* Reproducer for contiguous PTE breakage. | |
* | |
* This test assumes 4KiB base pages, and that 64KiB hugepages are implemented | |
* with contiguous PTEs. | |
* | |
* aarch64-linux-gnu-gcc -Wall hugetlb-cont.c -o hugetlb-cont | |
* echo 1 > /sys/kernel/mm/hugepages/hugepages-64kB/nr_hugepages | |
*/ | |
#define _GNU_SOURCE | |
#include <fcntl.h> | |
#include <sys/syscall.h> | |
#include <sys/ioctl.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <string.h> | |
#include <linux/magic.h> | |
#include <sys/mman.h> | |
#include <sys/statfs.h> | |
#include <errno.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#define PAGE_SIZE 4096 | |
#define PAGE_MASK ~(PAGE_SIZE - 1) | |
#define PREFIX " ... " | |
#define ERROR_PREFIX " !!! " | |
int run_repro(int fd, size_t len) | |
{ | |
char *map; | |
map = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |
if (map == MAP_FAILED) { | |
perror(ERROR_PREFIX "mmap for primary mapping failed"); | |
return -1; | |
} | |
/* | |
* Fault in page. | |
* vma flags = VM_SHARED | VM_READ | VM_WRITE | |
* vma pgprot = PTE_RDONLY | PTE_WRITE | |
* pte pgprot = PTE_DIRTY | PTE_WRITE (dirtied by hugetlb) | |
*/ | |
*(volatile char *)map; | |
/* | |
* vma flags = VM_SHARED | |
* vma pgprot = PTE_RDONLY | |
* pte pgprot = PTE_DIRTY | PTE_RDONLY (sw-dirty bit is not touched) | |
*/ | |
if (mprotect(map, len, PROT_NONE)) { | |
perror("mprotect RW failed"); | |
goto out; | |
} | |
/* | |
* vma flags = VM_SHARED | VM_READ | VM_WRITE | |
* vma pgprot = PTE_RDONLY | PTE_WRITE | |
* pte pgprot = PTE_DIRTY | PTE_WRITE | PTE_RDONLY (PTE assumed clean) | |
*/ | |
if (mprotect(map, len, PROT_READ | PROT_WRITE)) { | |
perror("mprotect RW failed"); | |
goto out; | |
} | |
printf("attempting read: %p\n", map); | |
*(volatile char *)map; | |
/* stuck on the following write if the bug is present */ | |
printf("attempting write: %p\n", map); | |
*(volatile char *)map = 0; | |
printf("success\n"); | |
out: | |
munmap(map, len); | |
return 0; | |
} | |
#ifndef MFD_HUGE_64KB | |
#define MFD_HUGE_64KB 0x40000000 | |
#endif | |
int main(void) | |
{ | |
int fd; | |
struct statfs file_stat; | |
size_t hugepagesize; | |
size_t len; | |
int ret = 0; | |
fd = memfd_create("hugetlb_tmp", MFD_HUGETLB | MFD_HUGE_64KB); | |
if (fd < 0) { | |
perror(ERROR_PREFIX "could not open hugetlbfs file"); | |
return -1; | |
} | |
memset(&file_stat, 0, sizeof(file_stat)); | |
if (fstatfs(fd, &file_stat)) { | |
perror(ERROR_PREFIX "fstatfs failed"); | |
goto close; | |
} | |
if (file_stat.f_type != HUGETLBFS_MAGIC) { | |
printf(ERROR_PREFIX "not hugetlbfs file\n"); | |
goto close; | |
} | |
hugepagesize = file_stat.f_bsize; | |
len = hugepagesize; | |
if (ftruncate(fd, len) < 0) { | |
perror(ERROR_PREFIX "ftruncate failed"); | |
goto close; | |
} | |
run_repro(fd, len); | |
if (ftruncate(fd, 0) < 0) | |
perror(ERROR_PREFIX "ftruncate back to 0 failed"); | |
close: | |
close(fd); | |
return ret; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment