#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #define PAGES_PER_GIG 512 #define HUGE_PAGE_SIZE (2UL << 20) #define GIG_MASK ~(PAGES_PER_GIG * HUGE_PAGE_SIZE - 1) void fault_in_write(char *mapping, size_t len) { volatile char *mapping_v = mapping; for (size_t i = 0; i < len; i += 4096) mapping_v[i] = 1; } int main() { int fd = memfd_create("test", MFD_HUGETLB); size_t len = 2 * PAGES_PER_GIG * HUGE_PAGE_SIZE; char *mapping; char *mapping_to_use; int uffd; int pid; struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; struct uffdio_writeprotect uffdio_writeprotect; if (ftruncate(fd, len)) { perror("ftruncate failed"); return -1; } mapping = mmap(NULL, len, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0); if (mapping == MAP_FAILED) { perror("mmap failed"); return -1; } mapping_to_use = mapping; if ((unsigned long)mapping & ~GIG_MASK) { mapping_to_use = (char *)((unsigned long)mapping & GIG_MASK) + (len/2); } pid = fork(); if (pid < 0) { perror("fork failed"); return -1; } fault_in_write(mapping, len); if (pid > 0) { int status; waitpid(pid, &status, 0); return WEXITSTATUS(status); } uffd = syscall(SYS_userfaultfd, O_CLOEXEC); uffdio_api.api = UFFD_API; uffdio_api.features = UFFD_FEATURE_SIGBUS; uffdio_api.ioctls = 0; if (ioctl(uffd, UFFDIO_API, &uffdio_api)) { perror("UFFDIO_API failed"); return -1; } uffdio_register.range.start = (unsigned long)mapping_to_use; uffdio_register.range.len = len/4; uffdio_register.mode = UFFDIO_REGISTER_MODE_WP; if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) { perror("UFFDIO_REGISTER failed"); return -1; } uffdio_writeprotect.range.start = (unsigned long)mapping_to_use; uffdio_writeprotect.range.len = len/4; uffdio_writeprotect.mode = UFFDIO_WRITEPROTECT_MODE_WP; if (ioctl(uffd, UFFDIO_WRITEPROTECT, &uffdio_writeprotect)) { perror("UFFDIO_WRITEPROTECT failed"); return -1; } /* If correct: should SIGBUS */ fault_in_write(mapping_to_use, 1); printf("BUG: no sigbus\n"); return 0; }