1876320d7SLorenzo Stoakes // SPDX-License-Identifier: GPL-2.0-or-later 2876320d7SLorenzo Stoakes 3876320d7SLorenzo Stoakes #define _GNU_SOURCE 4876320d7SLorenzo Stoakes #include "../kselftest_harness.h" 5876320d7SLorenzo Stoakes #include <asm-generic/mman.h> /* Force the import of the tools version. */ 6876320d7SLorenzo Stoakes #include <assert.h> 7876320d7SLorenzo Stoakes #include <errno.h> 8876320d7SLorenzo Stoakes #include <fcntl.h> 9876320d7SLorenzo Stoakes #include <linux/userfaultfd.h> 10876320d7SLorenzo Stoakes #include <setjmp.h> 11876320d7SLorenzo Stoakes #include <signal.h> 12876320d7SLorenzo Stoakes #include <stdbool.h> 13876320d7SLorenzo Stoakes #include <stdio.h> 14876320d7SLorenzo Stoakes #include <stdlib.h> 15876320d7SLorenzo Stoakes #include <string.h> 16876320d7SLorenzo Stoakes #include <sys/ioctl.h> 17876320d7SLorenzo Stoakes #include <sys/mman.h> 18876320d7SLorenzo Stoakes #include <sys/syscall.h> 19876320d7SLorenzo Stoakes #include <sys/uio.h> 20876320d7SLorenzo Stoakes #include <unistd.h> 21876320d7SLorenzo Stoakes 22*62d648c4SLorenzo Stoakes #include "../pidfd/pidfd.h" 23*62d648c4SLorenzo Stoakes 24876320d7SLorenzo Stoakes /* 25876320d7SLorenzo Stoakes * Ignore the checkpatch warning, as per the C99 standard, section 7.14.1.1: 26876320d7SLorenzo Stoakes * 27876320d7SLorenzo Stoakes * "If the signal occurs other than as the result of calling the abort or raise 28876320d7SLorenzo Stoakes * function, the behavior is undefined if the signal handler refers to any 29876320d7SLorenzo Stoakes * object with static storage duration other than by assigning a value to an 30876320d7SLorenzo Stoakes * object declared as volatile sig_atomic_t" 31876320d7SLorenzo Stoakes */ 32876320d7SLorenzo Stoakes static volatile sig_atomic_t signal_jump_set; 33876320d7SLorenzo Stoakes static sigjmp_buf signal_jmp_buf; 34876320d7SLorenzo Stoakes 35876320d7SLorenzo Stoakes /* 36876320d7SLorenzo Stoakes * Ignore the checkpatch warning, we must read from x but don't want to do 37876320d7SLorenzo Stoakes * anything with it in order to trigger a read page fault. We therefore must use 38876320d7SLorenzo Stoakes * volatile to stop the compiler from optimising this away. 39876320d7SLorenzo Stoakes */ 40876320d7SLorenzo Stoakes #define FORCE_READ(x) (*(volatile typeof(x) *)x) 41876320d7SLorenzo Stoakes 42876320d7SLorenzo Stoakes static int userfaultfd(int flags) 43876320d7SLorenzo Stoakes { 44876320d7SLorenzo Stoakes return syscall(SYS_userfaultfd, flags); 45876320d7SLorenzo Stoakes } 46876320d7SLorenzo Stoakes 47876320d7SLorenzo Stoakes static void handle_fatal(int c) 48876320d7SLorenzo Stoakes { 49876320d7SLorenzo Stoakes if (!signal_jump_set) 50876320d7SLorenzo Stoakes return; 51876320d7SLorenzo Stoakes 52876320d7SLorenzo Stoakes siglongjmp(signal_jmp_buf, c); 53876320d7SLorenzo Stoakes } 54876320d7SLorenzo Stoakes 5519b65ffaSLorenzo Stoakes static ssize_t sys_process_madvise(int pidfd, const struct iovec *iovec, 5619b65ffaSLorenzo Stoakes size_t n, int advice, unsigned int flags) 5719b65ffaSLorenzo Stoakes { 5819b65ffaSLorenzo Stoakes return syscall(__NR_process_madvise, pidfd, iovec, n, advice, flags); 5919b65ffaSLorenzo Stoakes } 6019b65ffaSLorenzo Stoakes 61876320d7SLorenzo Stoakes /* 62876320d7SLorenzo Stoakes * Enable our signal catcher and try to read/write the specified buffer. The 63876320d7SLorenzo Stoakes * return value indicates whether the read/write succeeds without a fatal 64876320d7SLorenzo Stoakes * signal. 65876320d7SLorenzo Stoakes */ 66876320d7SLorenzo Stoakes static bool try_access_buf(char *ptr, bool write) 67876320d7SLorenzo Stoakes { 68876320d7SLorenzo Stoakes bool failed; 69876320d7SLorenzo Stoakes 70876320d7SLorenzo Stoakes /* Tell signal handler to jump back here on fatal signal. */ 71876320d7SLorenzo Stoakes signal_jump_set = true; 72876320d7SLorenzo Stoakes /* If a fatal signal arose, we will jump back here and failed is set. */ 73876320d7SLorenzo Stoakes failed = sigsetjmp(signal_jmp_buf, 0) != 0; 74876320d7SLorenzo Stoakes 75876320d7SLorenzo Stoakes if (!failed) { 76876320d7SLorenzo Stoakes if (write) 77876320d7SLorenzo Stoakes *ptr = 'x'; 78876320d7SLorenzo Stoakes else 79876320d7SLorenzo Stoakes FORCE_READ(ptr); 80876320d7SLorenzo Stoakes } 81876320d7SLorenzo Stoakes 82876320d7SLorenzo Stoakes signal_jump_set = false; 83876320d7SLorenzo Stoakes return !failed; 84876320d7SLorenzo Stoakes } 85876320d7SLorenzo Stoakes 86876320d7SLorenzo Stoakes /* Try and read from a buffer, return true if no fatal signal. */ 87876320d7SLorenzo Stoakes static bool try_read_buf(char *ptr) 88876320d7SLorenzo Stoakes { 89876320d7SLorenzo Stoakes return try_access_buf(ptr, false); 90876320d7SLorenzo Stoakes } 91876320d7SLorenzo Stoakes 92876320d7SLorenzo Stoakes /* Try and write to a buffer, return true if no fatal signal. */ 93876320d7SLorenzo Stoakes static bool try_write_buf(char *ptr) 94876320d7SLorenzo Stoakes { 95876320d7SLorenzo Stoakes return try_access_buf(ptr, true); 96876320d7SLorenzo Stoakes } 97876320d7SLorenzo Stoakes 98876320d7SLorenzo Stoakes /* 99876320d7SLorenzo Stoakes * Try and BOTH read from AND write to a buffer, return true if BOTH operations 100876320d7SLorenzo Stoakes * succeed. 101876320d7SLorenzo Stoakes */ 102876320d7SLorenzo Stoakes static bool try_read_write_buf(char *ptr) 103876320d7SLorenzo Stoakes { 104876320d7SLorenzo Stoakes return try_read_buf(ptr) && try_write_buf(ptr); 105876320d7SLorenzo Stoakes } 106876320d7SLorenzo Stoakes 107876320d7SLorenzo Stoakes FIXTURE(guard_pages) 108876320d7SLorenzo Stoakes { 109876320d7SLorenzo Stoakes unsigned long page_size; 110876320d7SLorenzo Stoakes }; 111876320d7SLorenzo Stoakes 112876320d7SLorenzo Stoakes FIXTURE_SETUP(guard_pages) 113876320d7SLorenzo Stoakes { 114876320d7SLorenzo Stoakes struct sigaction act = { 115876320d7SLorenzo Stoakes .sa_handler = &handle_fatal, 116876320d7SLorenzo Stoakes .sa_flags = SA_NODEFER, 117876320d7SLorenzo Stoakes }; 118876320d7SLorenzo Stoakes 119876320d7SLorenzo Stoakes sigemptyset(&act.sa_mask); 120876320d7SLorenzo Stoakes if (sigaction(SIGSEGV, &act, NULL)) 121876320d7SLorenzo Stoakes ksft_exit_fail_perror("sigaction"); 122876320d7SLorenzo Stoakes 123876320d7SLorenzo Stoakes self->page_size = (unsigned long)sysconf(_SC_PAGESIZE); 124876320d7SLorenzo Stoakes }; 125876320d7SLorenzo Stoakes 126876320d7SLorenzo Stoakes FIXTURE_TEARDOWN(guard_pages) 127876320d7SLorenzo Stoakes { 128876320d7SLorenzo Stoakes struct sigaction act = { 129876320d7SLorenzo Stoakes .sa_handler = SIG_DFL, 130876320d7SLorenzo Stoakes .sa_flags = SA_NODEFER, 131876320d7SLorenzo Stoakes }; 132876320d7SLorenzo Stoakes 133876320d7SLorenzo Stoakes sigemptyset(&act.sa_mask); 134876320d7SLorenzo Stoakes sigaction(SIGSEGV, &act, NULL); 135876320d7SLorenzo Stoakes } 136876320d7SLorenzo Stoakes 137876320d7SLorenzo Stoakes TEST_F(guard_pages, basic) 138876320d7SLorenzo Stoakes { 139876320d7SLorenzo Stoakes const unsigned long NUM_PAGES = 10; 140876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 141876320d7SLorenzo Stoakes char *ptr; 142876320d7SLorenzo Stoakes int i; 143876320d7SLorenzo Stoakes 144876320d7SLorenzo Stoakes ptr = mmap(NULL, NUM_PAGES * page_size, PROT_READ | PROT_WRITE, 145876320d7SLorenzo Stoakes MAP_PRIVATE | MAP_ANON, -1, 0); 146876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 147876320d7SLorenzo Stoakes 148876320d7SLorenzo Stoakes /* Trivially assert we can touch the first page. */ 149876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr)); 150876320d7SLorenzo Stoakes 151876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); 152876320d7SLorenzo Stoakes 153876320d7SLorenzo Stoakes /* Establish that 1st page SIGSEGV's. */ 154876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 155876320d7SLorenzo Stoakes 156876320d7SLorenzo Stoakes /* Ensure we can touch everything else.*/ 157876320d7SLorenzo Stoakes for (i = 1; i < NUM_PAGES; i++) { 158876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 159876320d7SLorenzo Stoakes 160876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 161876320d7SLorenzo Stoakes } 162876320d7SLorenzo Stoakes 163876320d7SLorenzo Stoakes /* Establish a guard page at the end of the mapping. */ 164876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[(NUM_PAGES - 1) * page_size], page_size, 165876320d7SLorenzo Stoakes MADV_GUARD_INSTALL), 0); 166876320d7SLorenzo Stoakes 167876320d7SLorenzo Stoakes /* Check that both guard pages result in SIGSEGV. */ 168876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 169876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[(NUM_PAGES - 1) * page_size])); 170876320d7SLorenzo Stoakes 171876320d7SLorenzo Stoakes /* Remove the first guard page. */ 172876320d7SLorenzo Stoakes ASSERT_FALSE(madvise(ptr, page_size, MADV_GUARD_REMOVE)); 173876320d7SLorenzo Stoakes 174876320d7SLorenzo Stoakes /* Make sure we can touch it. */ 175876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr)); 176876320d7SLorenzo Stoakes 177876320d7SLorenzo Stoakes /* Remove the last guard page. */ 178876320d7SLorenzo Stoakes ASSERT_FALSE(madvise(&ptr[(NUM_PAGES - 1) * page_size], page_size, 179876320d7SLorenzo Stoakes MADV_GUARD_REMOVE)); 180876320d7SLorenzo Stoakes 181876320d7SLorenzo Stoakes /* Make sure we can touch it. */ 182876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(&ptr[(NUM_PAGES - 1) * page_size])); 183876320d7SLorenzo Stoakes 184876320d7SLorenzo Stoakes /* 185876320d7SLorenzo Stoakes * Test setting a _range_ of pages, namely the first 3. The first of 186876320d7SLorenzo Stoakes * these be faulted in, so this also tests that we can install guard 187876320d7SLorenzo Stoakes * pages over backed pages. 188876320d7SLorenzo Stoakes */ 189876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 3 * page_size, MADV_GUARD_INSTALL), 0); 190876320d7SLorenzo Stoakes 191876320d7SLorenzo Stoakes /* Make sure they are all guard pages. */ 192876320d7SLorenzo Stoakes for (i = 0; i < 3; i++) { 193876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 194876320d7SLorenzo Stoakes 195876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 196876320d7SLorenzo Stoakes } 197876320d7SLorenzo Stoakes 198876320d7SLorenzo Stoakes /* Make sure the rest are not. */ 199876320d7SLorenzo Stoakes for (i = 3; i < NUM_PAGES; i++) { 200876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 201876320d7SLorenzo Stoakes 202876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 203876320d7SLorenzo Stoakes } 204876320d7SLorenzo Stoakes 205876320d7SLorenzo Stoakes /* Remove guard pages. */ 206876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, NUM_PAGES * page_size, MADV_GUARD_REMOVE), 0); 207876320d7SLorenzo Stoakes 208876320d7SLorenzo Stoakes /* Now make sure we can touch everything. */ 209876320d7SLorenzo Stoakes for (i = 0; i < NUM_PAGES; i++) { 210876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 211876320d7SLorenzo Stoakes 212876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 213876320d7SLorenzo Stoakes } 214876320d7SLorenzo Stoakes 215876320d7SLorenzo Stoakes /* 216876320d7SLorenzo Stoakes * Now remove all guard pages, make sure we don't remove existing 217876320d7SLorenzo Stoakes * entries. 218876320d7SLorenzo Stoakes */ 219876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, NUM_PAGES * page_size, MADV_GUARD_REMOVE), 0); 220876320d7SLorenzo Stoakes 221876320d7SLorenzo Stoakes for (i = 0; i < NUM_PAGES * page_size; i += page_size) { 222876320d7SLorenzo Stoakes char chr = ptr[i]; 223876320d7SLorenzo Stoakes 224876320d7SLorenzo Stoakes ASSERT_EQ(chr, 'x'); 225876320d7SLorenzo Stoakes } 226876320d7SLorenzo Stoakes 227876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, NUM_PAGES * page_size), 0); 228876320d7SLorenzo Stoakes } 229876320d7SLorenzo Stoakes 230876320d7SLorenzo Stoakes /* Assert that operations applied across multiple VMAs work as expected. */ 231876320d7SLorenzo Stoakes TEST_F(guard_pages, multi_vma) 232876320d7SLorenzo Stoakes { 233876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 234876320d7SLorenzo Stoakes char *ptr_region, *ptr, *ptr1, *ptr2, *ptr3; 235876320d7SLorenzo Stoakes int i; 236876320d7SLorenzo Stoakes 237876320d7SLorenzo Stoakes /* Reserve a 100 page region over which we can install VMAs. */ 238876320d7SLorenzo Stoakes ptr_region = mmap(NULL, 100 * page_size, PROT_NONE, 239876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 240876320d7SLorenzo Stoakes ASSERT_NE(ptr_region, MAP_FAILED); 241876320d7SLorenzo Stoakes 242876320d7SLorenzo Stoakes /* Place a VMA of 10 pages size at the start of the region. */ 243876320d7SLorenzo Stoakes ptr1 = mmap(ptr_region, 10 * page_size, PROT_READ | PROT_WRITE, 244876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 245876320d7SLorenzo Stoakes ASSERT_NE(ptr1, MAP_FAILED); 246876320d7SLorenzo Stoakes 247876320d7SLorenzo Stoakes /* Place a VMA of 5 pages size 50 pages into the region. */ 248876320d7SLorenzo Stoakes ptr2 = mmap(&ptr_region[50 * page_size], 5 * page_size, 249876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE, 250876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 251876320d7SLorenzo Stoakes ASSERT_NE(ptr2, MAP_FAILED); 252876320d7SLorenzo Stoakes 253876320d7SLorenzo Stoakes /* Place a VMA of 20 pages size at the end of the region. */ 254876320d7SLorenzo Stoakes ptr3 = mmap(&ptr_region[80 * page_size], 20 * page_size, 255876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE, 256876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 257876320d7SLorenzo Stoakes ASSERT_NE(ptr3, MAP_FAILED); 258876320d7SLorenzo Stoakes 259876320d7SLorenzo Stoakes /* Unmap gaps. */ 260876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr_region[10 * page_size], 40 * page_size), 0); 261876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr_region[55 * page_size], 25 * page_size), 0); 262876320d7SLorenzo Stoakes 263876320d7SLorenzo Stoakes /* 264876320d7SLorenzo Stoakes * We end up with VMAs like this: 265876320d7SLorenzo Stoakes * 266876320d7SLorenzo Stoakes * 0 10 .. 50 55 .. 80 100 267876320d7SLorenzo Stoakes * [---] [---] [---] 268876320d7SLorenzo Stoakes */ 269876320d7SLorenzo Stoakes 270876320d7SLorenzo Stoakes /* 271876320d7SLorenzo Stoakes * Now mark the whole range as guard pages and make sure all VMAs are as 272876320d7SLorenzo Stoakes * such. 273876320d7SLorenzo Stoakes */ 274876320d7SLorenzo Stoakes 275876320d7SLorenzo Stoakes /* 276876320d7SLorenzo Stoakes * madvise() is certifiable and lets you perform operations over gaps, 277876320d7SLorenzo Stoakes * everything works, but it indicates an error and errno is set to 278876320d7SLorenzo Stoakes * -ENOMEM. Also if anything runs out of memory it is set to 279876320d7SLorenzo Stoakes * -ENOMEM. You are meant to guess which is which. 280876320d7SLorenzo Stoakes */ 281876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr_region, 100 * page_size, MADV_GUARD_INSTALL), -1); 282876320d7SLorenzo Stoakes ASSERT_EQ(errno, ENOMEM); 283876320d7SLorenzo Stoakes 284876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 285876320d7SLorenzo Stoakes char *curr = &ptr1[i * page_size]; 286876320d7SLorenzo Stoakes 287876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 288876320d7SLorenzo Stoakes } 289876320d7SLorenzo Stoakes 290876320d7SLorenzo Stoakes for (i = 0; i < 5; i++) { 291876320d7SLorenzo Stoakes char *curr = &ptr2[i * page_size]; 292876320d7SLorenzo Stoakes 293876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 294876320d7SLorenzo Stoakes } 295876320d7SLorenzo Stoakes 296876320d7SLorenzo Stoakes for (i = 0; i < 20; i++) { 297876320d7SLorenzo Stoakes char *curr = &ptr3[i * page_size]; 298876320d7SLorenzo Stoakes 299876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 300876320d7SLorenzo Stoakes } 301876320d7SLorenzo Stoakes 302876320d7SLorenzo Stoakes /* Now remove guar pages over range and assert the opposite. */ 303876320d7SLorenzo Stoakes 304876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr_region, 100 * page_size, MADV_GUARD_REMOVE), -1); 305876320d7SLorenzo Stoakes ASSERT_EQ(errno, ENOMEM); 306876320d7SLorenzo Stoakes 307876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 308876320d7SLorenzo Stoakes char *curr = &ptr1[i * page_size]; 309876320d7SLorenzo Stoakes 310876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 311876320d7SLorenzo Stoakes } 312876320d7SLorenzo Stoakes 313876320d7SLorenzo Stoakes for (i = 0; i < 5; i++) { 314876320d7SLorenzo Stoakes char *curr = &ptr2[i * page_size]; 315876320d7SLorenzo Stoakes 316876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 317876320d7SLorenzo Stoakes } 318876320d7SLorenzo Stoakes 319876320d7SLorenzo Stoakes for (i = 0; i < 20; i++) { 320876320d7SLorenzo Stoakes char *curr = &ptr3[i * page_size]; 321876320d7SLorenzo Stoakes 322876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 323876320d7SLorenzo Stoakes } 324876320d7SLorenzo Stoakes 325876320d7SLorenzo Stoakes /* Now map incompatible VMAs in the gaps. */ 326876320d7SLorenzo Stoakes ptr = mmap(&ptr_region[10 * page_size], 40 * page_size, 327876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE | PROT_EXEC, 328876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 329876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 330876320d7SLorenzo Stoakes ptr = mmap(&ptr_region[55 * page_size], 25 * page_size, 331876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE | PROT_EXEC, 332876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 333876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 334876320d7SLorenzo Stoakes 335876320d7SLorenzo Stoakes /* 336876320d7SLorenzo Stoakes * We end up with VMAs like this: 337876320d7SLorenzo Stoakes * 338876320d7SLorenzo Stoakes * 0 10 .. 50 55 .. 80 100 339876320d7SLorenzo Stoakes * [---][xxxx][---][xxxx][---] 340876320d7SLorenzo Stoakes * 341876320d7SLorenzo Stoakes * Where 'x' signifies VMAs that cannot be merged with those adjacent to 342876320d7SLorenzo Stoakes * them. 343876320d7SLorenzo Stoakes */ 344876320d7SLorenzo Stoakes 345876320d7SLorenzo Stoakes /* Multiple VMAs adjacent to one another should result in no error. */ 346876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr_region, 100 * page_size, MADV_GUARD_INSTALL), 0); 347876320d7SLorenzo Stoakes for (i = 0; i < 100; i++) { 348876320d7SLorenzo Stoakes char *curr = &ptr_region[i * page_size]; 349876320d7SLorenzo Stoakes 350876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 351876320d7SLorenzo Stoakes } 352876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr_region, 100 * page_size, MADV_GUARD_REMOVE), 0); 353876320d7SLorenzo Stoakes for (i = 0; i < 100; i++) { 354876320d7SLorenzo Stoakes char *curr = &ptr_region[i * page_size]; 355876320d7SLorenzo Stoakes 356876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 357876320d7SLorenzo Stoakes } 358876320d7SLorenzo Stoakes 359876320d7SLorenzo Stoakes /* Cleanup. */ 360876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr_region, 100 * page_size), 0); 361876320d7SLorenzo Stoakes } 362876320d7SLorenzo Stoakes 363876320d7SLorenzo Stoakes /* 364876320d7SLorenzo Stoakes * Assert that batched operations performed using process_madvise() work as 365876320d7SLorenzo Stoakes * expected. 366876320d7SLorenzo Stoakes */ 367876320d7SLorenzo Stoakes TEST_F(guard_pages, process_madvise) 368876320d7SLorenzo Stoakes { 369876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 370876320d7SLorenzo Stoakes char *ptr_region, *ptr1, *ptr2, *ptr3; 371876320d7SLorenzo Stoakes ssize_t count; 372876320d7SLorenzo Stoakes struct iovec vec[6]; 373876320d7SLorenzo Stoakes 374876320d7SLorenzo Stoakes /* Reserve region to map over. */ 375876320d7SLorenzo Stoakes ptr_region = mmap(NULL, 100 * page_size, PROT_NONE, 376876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 377876320d7SLorenzo Stoakes ASSERT_NE(ptr_region, MAP_FAILED); 378876320d7SLorenzo Stoakes 379876320d7SLorenzo Stoakes /* 380876320d7SLorenzo Stoakes * 10 pages offset 1 page into reserve region. We MAP_POPULATE so we 381876320d7SLorenzo Stoakes * overwrite existing entries and test this code path against 382876320d7SLorenzo Stoakes * overwriting existing entries. 383876320d7SLorenzo Stoakes */ 384876320d7SLorenzo Stoakes ptr1 = mmap(&ptr_region[page_size], 10 * page_size, 385876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE, 386876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE | MAP_POPULATE, -1, 0); 387876320d7SLorenzo Stoakes ASSERT_NE(ptr1, MAP_FAILED); 388876320d7SLorenzo Stoakes /* We want guard markers at start/end of each VMA. */ 389876320d7SLorenzo Stoakes vec[0].iov_base = ptr1; 390876320d7SLorenzo Stoakes vec[0].iov_len = page_size; 391876320d7SLorenzo Stoakes vec[1].iov_base = &ptr1[9 * page_size]; 392876320d7SLorenzo Stoakes vec[1].iov_len = page_size; 393876320d7SLorenzo Stoakes 394876320d7SLorenzo Stoakes /* 5 pages offset 50 pages into reserve region. */ 395876320d7SLorenzo Stoakes ptr2 = mmap(&ptr_region[50 * page_size], 5 * page_size, 396876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE, 397876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 398876320d7SLorenzo Stoakes ASSERT_NE(ptr2, MAP_FAILED); 399876320d7SLorenzo Stoakes vec[2].iov_base = ptr2; 400876320d7SLorenzo Stoakes vec[2].iov_len = page_size; 401876320d7SLorenzo Stoakes vec[3].iov_base = &ptr2[4 * page_size]; 402876320d7SLorenzo Stoakes vec[3].iov_len = page_size; 403876320d7SLorenzo Stoakes 404876320d7SLorenzo Stoakes /* 20 pages offset 79 pages into reserve region. */ 405876320d7SLorenzo Stoakes ptr3 = mmap(&ptr_region[79 * page_size], 20 * page_size, 406876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE, 407876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 408876320d7SLorenzo Stoakes ASSERT_NE(ptr3, MAP_FAILED); 409876320d7SLorenzo Stoakes vec[4].iov_base = ptr3; 410876320d7SLorenzo Stoakes vec[4].iov_len = page_size; 411876320d7SLorenzo Stoakes vec[5].iov_base = &ptr3[19 * page_size]; 412876320d7SLorenzo Stoakes vec[5].iov_len = page_size; 413876320d7SLorenzo Stoakes 414876320d7SLorenzo Stoakes /* Free surrounding VMAs. */ 415876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr_region, page_size), 0); 416876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr_region[11 * page_size], 39 * page_size), 0); 417876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr_region[55 * page_size], 24 * page_size), 0); 418876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr_region[99 * page_size], page_size), 0); 419876320d7SLorenzo Stoakes 420876320d7SLorenzo Stoakes /* Now guard in one step. */ 421*62d648c4SLorenzo Stoakes count = sys_process_madvise(PIDFD_SELF, vec, 6, MADV_GUARD_INSTALL, 0); 422876320d7SLorenzo Stoakes 423876320d7SLorenzo Stoakes /* OK we don't have permission to do this, skip. */ 424876320d7SLorenzo Stoakes if (count == -1 && errno == EPERM) 425876320d7SLorenzo Stoakes ksft_exit_skip("No process_madvise() permissions, try running as root.\n"); 426876320d7SLorenzo Stoakes 427876320d7SLorenzo Stoakes /* Returns the number of bytes advised. */ 428876320d7SLorenzo Stoakes ASSERT_EQ(count, 6 * page_size); 429876320d7SLorenzo Stoakes 430876320d7SLorenzo Stoakes /* Now make sure the guarding was applied. */ 431876320d7SLorenzo Stoakes 432876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr1)); 433876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr1[9 * page_size])); 434876320d7SLorenzo Stoakes 435876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr2)); 436876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr2[4 * page_size])); 437876320d7SLorenzo Stoakes 438876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr3)); 439876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr3[19 * page_size])); 440876320d7SLorenzo Stoakes 441876320d7SLorenzo Stoakes /* Now do the same with unguard... */ 442*62d648c4SLorenzo Stoakes count = sys_process_madvise(PIDFD_SELF, vec, 6, MADV_GUARD_REMOVE, 0); 443876320d7SLorenzo Stoakes 444876320d7SLorenzo Stoakes /* ...and everything should now succeed. */ 445876320d7SLorenzo Stoakes 446876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr1)); 447876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(&ptr1[9 * page_size])); 448876320d7SLorenzo Stoakes 449876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr2)); 450876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(&ptr2[4 * page_size])); 451876320d7SLorenzo Stoakes 452876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr3)); 453876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(&ptr3[19 * page_size])); 454876320d7SLorenzo Stoakes 455876320d7SLorenzo Stoakes /* Cleanup. */ 456876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr1, 10 * page_size), 0); 457876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr2, 5 * page_size), 0); 458876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr3, 20 * page_size), 0); 459876320d7SLorenzo Stoakes } 460876320d7SLorenzo Stoakes 461876320d7SLorenzo Stoakes /* Assert that unmapping ranges does not leave guard markers behind. */ 462876320d7SLorenzo Stoakes TEST_F(guard_pages, munmap) 463876320d7SLorenzo Stoakes { 464876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 465876320d7SLorenzo Stoakes char *ptr, *ptr_new1, *ptr_new2; 466876320d7SLorenzo Stoakes 467876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 468876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 469876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 470876320d7SLorenzo Stoakes 471876320d7SLorenzo Stoakes /* Guard first and last pages. */ 472876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); 473876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[9 * page_size], page_size, MADV_GUARD_INSTALL), 0); 474876320d7SLorenzo Stoakes 475876320d7SLorenzo Stoakes /* Assert that they are guarded. */ 476876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 477876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[9 * page_size])); 478876320d7SLorenzo Stoakes 479876320d7SLorenzo Stoakes /* Unmap them. */ 480876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, page_size), 0); 481876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr[9 * page_size], page_size), 0); 482876320d7SLorenzo Stoakes 483876320d7SLorenzo Stoakes /* Map over them.*/ 484876320d7SLorenzo Stoakes ptr_new1 = mmap(ptr, page_size, PROT_READ | PROT_WRITE, 485876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 486876320d7SLorenzo Stoakes ASSERT_NE(ptr_new1, MAP_FAILED); 487876320d7SLorenzo Stoakes ptr_new2 = mmap(&ptr[9 * page_size], page_size, PROT_READ | PROT_WRITE, 488876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 489876320d7SLorenzo Stoakes ASSERT_NE(ptr_new2, MAP_FAILED); 490876320d7SLorenzo Stoakes 491876320d7SLorenzo Stoakes /* Assert that they are now not guarded. */ 492876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr_new1)); 493876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(ptr_new2)); 494876320d7SLorenzo Stoakes 495876320d7SLorenzo Stoakes /* Cleanup. */ 496876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 497876320d7SLorenzo Stoakes } 498876320d7SLorenzo Stoakes 499876320d7SLorenzo Stoakes /* Assert that mprotect() operations have no bearing on guard markers. */ 500876320d7SLorenzo Stoakes TEST_F(guard_pages, mprotect) 501876320d7SLorenzo Stoakes { 502876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 503876320d7SLorenzo Stoakes char *ptr; 504876320d7SLorenzo Stoakes int i; 505876320d7SLorenzo Stoakes 506876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 507876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 508876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 509876320d7SLorenzo Stoakes 510876320d7SLorenzo Stoakes /* Guard the middle of the range. */ 511876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[5 * page_size], 2 * page_size, 512876320d7SLorenzo Stoakes MADV_GUARD_INSTALL), 0); 513876320d7SLorenzo Stoakes 514876320d7SLorenzo Stoakes /* Assert that it is indeed guarded. */ 515876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[5 * page_size])); 516876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[6 * page_size])); 517876320d7SLorenzo Stoakes 518876320d7SLorenzo Stoakes /* Now make these pages read-only. */ 519876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(&ptr[5 * page_size], 2 * page_size, PROT_READ), 0); 520876320d7SLorenzo Stoakes 521876320d7SLorenzo Stoakes /* Make sure the range is still guarded. */ 522876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_buf(&ptr[5 * page_size])); 523876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_buf(&ptr[6 * page_size])); 524876320d7SLorenzo Stoakes 525876320d7SLorenzo Stoakes /* Make sure we can guard again without issue.*/ 526876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[5 * page_size], 2 * page_size, 527876320d7SLorenzo Stoakes MADV_GUARD_INSTALL), 0); 528876320d7SLorenzo Stoakes 529876320d7SLorenzo Stoakes /* Make sure the range is, yet again, still guarded. */ 530876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_buf(&ptr[5 * page_size])); 531876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_buf(&ptr[6 * page_size])); 532876320d7SLorenzo Stoakes 533876320d7SLorenzo Stoakes /* Now unguard the whole range. */ 534876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); 535876320d7SLorenzo Stoakes 536876320d7SLorenzo Stoakes /* Make sure the whole range is readable. */ 537876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 538876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 539876320d7SLorenzo Stoakes 540876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_buf(curr)); 541876320d7SLorenzo Stoakes } 542876320d7SLorenzo Stoakes 543876320d7SLorenzo Stoakes /* Cleanup. */ 544876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 545876320d7SLorenzo Stoakes } 546876320d7SLorenzo Stoakes 547876320d7SLorenzo Stoakes /* Split and merge VMAs and make sure guard pages still behave. */ 548876320d7SLorenzo Stoakes TEST_F(guard_pages, split_merge) 549876320d7SLorenzo Stoakes { 550876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 551876320d7SLorenzo Stoakes char *ptr, *ptr_new; 552876320d7SLorenzo Stoakes int i; 553876320d7SLorenzo Stoakes 554876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 555876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 556876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 557876320d7SLorenzo Stoakes 558876320d7SLorenzo Stoakes /* Guard the whole range. */ 559876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 560876320d7SLorenzo Stoakes 561876320d7SLorenzo Stoakes /* Make sure the whole range is guarded. */ 562876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 563876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 564876320d7SLorenzo Stoakes 565876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 566876320d7SLorenzo Stoakes } 567876320d7SLorenzo Stoakes 568876320d7SLorenzo Stoakes /* Now unmap some pages in the range so we split. */ 569876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr[2 * page_size], page_size), 0); 570876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr[5 * page_size], page_size), 0); 571876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr[8 * page_size], page_size), 0); 572876320d7SLorenzo Stoakes 573876320d7SLorenzo Stoakes /* Make sure the remaining ranges are guarded post-split. */ 574876320d7SLorenzo Stoakes for (i = 0; i < 2; i++) { 575876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 576876320d7SLorenzo Stoakes 577876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 578876320d7SLorenzo Stoakes } 579876320d7SLorenzo Stoakes for (i = 2; i < 5; i++) { 580876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 581876320d7SLorenzo Stoakes 582876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 583876320d7SLorenzo Stoakes } 584876320d7SLorenzo Stoakes for (i = 6; i < 8; i++) { 585876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 586876320d7SLorenzo Stoakes 587876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 588876320d7SLorenzo Stoakes } 589876320d7SLorenzo Stoakes for (i = 9; i < 10; i++) { 590876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 591876320d7SLorenzo Stoakes 592876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 593876320d7SLorenzo Stoakes } 594876320d7SLorenzo Stoakes 595876320d7SLorenzo Stoakes /* Now map them again - the unmap will have cleared the guards. */ 596876320d7SLorenzo Stoakes ptr_new = mmap(&ptr[2 * page_size], page_size, PROT_READ | PROT_WRITE, 597876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 598876320d7SLorenzo Stoakes ASSERT_NE(ptr_new, MAP_FAILED); 599876320d7SLorenzo Stoakes ptr_new = mmap(&ptr[5 * page_size], page_size, PROT_READ | PROT_WRITE, 600876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 601876320d7SLorenzo Stoakes ASSERT_NE(ptr_new, MAP_FAILED); 602876320d7SLorenzo Stoakes ptr_new = mmap(&ptr[8 * page_size], page_size, PROT_READ | PROT_WRITE, 603876320d7SLorenzo Stoakes MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0); 604876320d7SLorenzo Stoakes ASSERT_NE(ptr_new, MAP_FAILED); 605876320d7SLorenzo Stoakes 606876320d7SLorenzo Stoakes /* Now make sure guard pages are established. */ 607876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 608876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 609876320d7SLorenzo Stoakes bool result = try_read_write_buf(curr); 610876320d7SLorenzo Stoakes bool expect_true = i == 2 || i == 5 || i == 8; 611876320d7SLorenzo Stoakes 612876320d7SLorenzo Stoakes ASSERT_TRUE(expect_true ? result : !result); 613876320d7SLorenzo Stoakes } 614876320d7SLorenzo Stoakes 615876320d7SLorenzo Stoakes /* Now guard everything again. */ 616876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 617876320d7SLorenzo Stoakes 618876320d7SLorenzo Stoakes /* Make sure the whole range is guarded. */ 619876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 620876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 621876320d7SLorenzo Stoakes 622876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 623876320d7SLorenzo Stoakes } 624876320d7SLorenzo Stoakes 625876320d7SLorenzo Stoakes /* Now split the range into three. */ 626876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0); 627876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(&ptr[7 * page_size], 3 * page_size, PROT_READ), 0); 628876320d7SLorenzo Stoakes 629876320d7SLorenzo Stoakes /* Make sure the whole range is guarded for read. */ 630876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 631876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 632876320d7SLorenzo Stoakes 633876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_buf(curr)); 634876320d7SLorenzo Stoakes } 635876320d7SLorenzo Stoakes 636876320d7SLorenzo Stoakes /* Now reset protection bits so we merge the whole thing. */ 637876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ | PROT_WRITE), 0); 638876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(&ptr[7 * page_size], 3 * page_size, 639876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE), 0); 640876320d7SLorenzo Stoakes 641876320d7SLorenzo Stoakes /* Make sure the whole range is still guarded. */ 642876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 643876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 644876320d7SLorenzo Stoakes 645876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 646876320d7SLorenzo Stoakes } 647876320d7SLorenzo Stoakes 648876320d7SLorenzo Stoakes /* Split range into 3 again... */ 649876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ), 0); 650876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(&ptr[7 * page_size], 3 * page_size, PROT_READ), 0); 651876320d7SLorenzo Stoakes 652876320d7SLorenzo Stoakes /* ...and unguard the whole range. */ 653876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); 654876320d7SLorenzo Stoakes 655876320d7SLorenzo Stoakes /* Make sure the whole range is remedied for read. */ 656876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 657876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 658876320d7SLorenzo Stoakes 659876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_buf(curr)); 660876320d7SLorenzo Stoakes } 661876320d7SLorenzo Stoakes 662876320d7SLorenzo Stoakes /* Merge them again. */ 663876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(ptr, 3 * page_size, PROT_READ | PROT_WRITE), 0); 664876320d7SLorenzo Stoakes ASSERT_EQ(mprotect(&ptr[7 * page_size], 3 * page_size, 665876320d7SLorenzo Stoakes PROT_READ | PROT_WRITE), 0); 666876320d7SLorenzo Stoakes 667876320d7SLorenzo Stoakes /* Now ensure the merged range is remedied for read/write. */ 668876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 669876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 670876320d7SLorenzo Stoakes 671876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 672876320d7SLorenzo Stoakes } 673876320d7SLorenzo Stoakes 674876320d7SLorenzo Stoakes /* Cleanup. */ 675876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 676876320d7SLorenzo Stoakes } 677876320d7SLorenzo Stoakes 678876320d7SLorenzo Stoakes /* Assert that MADV_DONTNEED does not remove guard markers. */ 679876320d7SLorenzo Stoakes TEST_F(guard_pages, dontneed) 680876320d7SLorenzo Stoakes { 681876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 682876320d7SLorenzo Stoakes char *ptr; 683876320d7SLorenzo Stoakes int i; 684876320d7SLorenzo Stoakes 685876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 686876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 687876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 688876320d7SLorenzo Stoakes 689876320d7SLorenzo Stoakes /* Back the whole range. */ 690876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 691876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 692876320d7SLorenzo Stoakes 693876320d7SLorenzo Stoakes *curr = 'y'; 694876320d7SLorenzo Stoakes } 695876320d7SLorenzo Stoakes 696876320d7SLorenzo Stoakes /* Guard every other page. */ 697876320d7SLorenzo Stoakes for (i = 0; i < 10; i += 2) { 698876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 699876320d7SLorenzo Stoakes int res = madvise(curr, page_size, MADV_GUARD_INSTALL); 700876320d7SLorenzo Stoakes 701876320d7SLorenzo Stoakes ASSERT_EQ(res, 0); 702876320d7SLorenzo Stoakes } 703876320d7SLorenzo Stoakes 704876320d7SLorenzo Stoakes /* Indicate that we don't need any of the range. */ 705876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_DONTNEED), 0); 706876320d7SLorenzo Stoakes 707876320d7SLorenzo Stoakes /* Check to ensure guard markers are still in place. */ 708876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 709876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 710876320d7SLorenzo Stoakes bool result = try_read_buf(curr); 711876320d7SLorenzo Stoakes 712876320d7SLorenzo Stoakes if (i % 2 == 0) { 713876320d7SLorenzo Stoakes ASSERT_FALSE(result); 714876320d7SLorenzo Stoakes } else { 715876320d7SLorenzo Stoakes ASSERT_TRUE(result); 716876320d7SLorenzo Stoakes /* Make sure we really did get reset to zero page. */ 717876320d7SLorenzo Stoakes ASSERT_EQ(*curr, '\0'); 718876320d7SLorenzo Stoakes } 719876320d7SLorenzo Stoakes 720876320d7SLorenzo Stoakes /* Now write... */ 721876320d7SLorenzo Stoakes result = try_write_buf(&ptr[i * page_size]); 722876320d7SLorenzo Stoakes 723876320d7SLorenzo Stoakes /* ...and make sure same result. */ 724876320d7SLorenzo Stoakes ASSERT_TRUE(i % 2 != 0 ? result : !result); 725876320d7SLorenzo Stoakes } 726876320d7SLorenzo Stoakes 727876320d7SLorenzo Stoakes /* Cleanup. */ 728876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 729876320d7SLorenzo Stoakes } 730876320d7SLorenzo Stoakes 731876320d7SLorenzo Stoakes /* Assert that mlock()'ed pages work correctly with guard markers. */ 732876320d7SLorenzo Stoakes TEST_F(guard_pages, mlock) 733876320d7SLorenzo Stoakes { 734876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 735876320d7SLorenzo Stoakes char *ptr; 736876320d7SLorenzo Stoakes int i; 737876320d7SLorenzo Stoakes 738876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 739876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 740876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 741876320d7SLorenzo Stoakes 742876320d7SLorenzo Stoakes /* Populate. */ 743876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 744876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 745876320d7SLorenzo Stoakes 746876320d7SLorenzo Stoakes *curr = 'y'; 747876320d7SLorenzo Stoakes } 748876320d7SLorenzo Stoakes 749876320d7SLorenzo Stoakes /* Lock. */ 750876320d7SLorenzo Stoakes ASSERT_EQ(mlock(ptr, 10 * page_size), 0); 751876320d7SLorenzo Stoakes 752876320d7SLorenzo Stoakes /* Now try to guard, should fail with EINVAL. */ 753876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), -1); 754876320d7SLorenzo Stoakes ASSERT_EQ(errno, EINVAL); 755876320d7SLorenzo Stoakes 756876320d7SLorenzo Stoakes /* OK unlock. */ 757876320d7SLorenzo Stoakes ASSERT_EQ(munlock(ptr, 10 * page_size), 0); 758876320d7SLorenzo Stoakes 759876320d7SLorenzo Stoakes /* Guard first half of range, should now succeed. */ 760876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 5 * page_size, MADV_GUARD_INSTALL), 0); 761876320d7SLorenzo Stoakes 762876320d7SLorenzo Stoakes /* Make sure guard works. */ 763876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 764876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 765876320d7SLorenzo Stoakes bool result = try_read_write_buf(curr); 766876320d7SLorenzo Stoakes 767876320d7SLorenzo Stoakes if (i < 5) { 768876320d7SLorenzo Stoakes ASSERT_FALSE(result); 769876320d7SLorenzo Stoakes } else { 770876320d7SLorenzo Stoakes ASSERT_TRUE(result); 771876320d7SLorenzo Stoakes ASSERT_EQ(*curr, 'x'); 772876320d7SLorenzo Stoakes } 773876320d7SLorenzo Stoakes } 774876320d7SLorenzo Stoakes 775876320d7SLorenzo Stoakes /* 776876320d7SLorenzo Stoakes * Now lock the latter part of the range. We can't lock the guard pages, 777876320d7SLorenzo Stoakes * as this would result in the pages being populated and the guarding 778876320d7SLorenzo Stoakes * would cause this to error out. 779876320d7SLorenzo Stoakes */ 780876320d7SLorenzo Stoakes ASSERT_EQ(mlock(&ptr[5 * page_size], 5 * page_size), 0); 781876320d7SLorenzo Stoakes 782876320d7SLorenzo Stoakes /* 783876320d7SLorenzo Stoakes * Now remove guard pages, we permit mlock()'d ranges to have guard 784876320d7SLorenzo Stoakes * pages removed as it is a non-destructive operation. 785876320d7SLorenzo Stoakes */ 786876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); 787876320d7SLorenzo Stoakes 788876320d7SLorenzo Stoakes /* Now check that no guard pages remain. */ 789876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 790876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 791876320d7SLorenzo Stoakes 792876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 793876320d7SLorenzo Stoakes } 794876320d7SLorenzo Stoakes 795876320d7SLorenzo Stoakes /* Cleanup. */ 796876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 797876320d7SLorenzo Stoakes } 798876320d7SLorenzo Stoakes 799876320d7SLorenzo Stoakes /* 800876320d7SLorenzo Stoakes * Assert that moving, extending and shrinking memory via mremap() retains 801876320d7SLorenzo Stoakes * guard markers where possible. 802876320d7SLorenzo Stoakes * 803876320d7SLorenzo Stoakes * - Moving a mapping alone should retain markers as they are. 804876320d7SLorenzo Stoakes */ 805876320d7SLorenzo Stoakes TEST_F(guard_pages, mremap_move) 806876320d7SLorenzo Stoakes { 807876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 808876320d7SLorenzo Stoakes char *ptr, *ptr_new; 809876320d7SLorenzo Stoakes 810876320d7SLorenzo Stoakes /* Map 5 pages. */ 811876320d7SLorenzo Stoakes ptr = mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE, 812876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 813876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 814876320d7SLorenzo Stoakes 815876320d7SLorenzo Stoakes /* Place guard markers at both ends of the 5 page span. */ 816876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); 817876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[4 * page_size], page_size, MADV_GUARD_INSTALL), 0); 818876320d7SLorenzo Stoakes 819876320d7SLorenzo Stoakes /* Make sure the guard pages are in effect. */ 820876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 821876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[4 * page_size])); 822876320d7SLorenzo Stoakes 823876320d7SLorenzo Stoakes /* Map a new region we will move this range into. Doing this ensures 824876320d7SLorenzo Stoakes * that we have reserved a range to map into. 825876320d7SLorenzo Stoakes */ 826876320d7SLorenzo Stoakes ptr_new = mmap(NULL, 5 * page_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, 827876320d7SLorenzo Stoakes -1, 0); 828876320d7SLorenzo Stoakes ASSERT_NE(ptr_new, MAP_FAILED); 829876320d7SLorenzo Stoakes 830876320d7SLorenzo Stoakes ASSERT_EQ(mremap(ptr, 5 * page_size, 5 * page_size, 831876320d7SLorenzo Stoakes MREMAP_MAYMOVE | MREMAP_FIXED, ptr_new), ptr_new); 832876320d7SLorenzo Stoakes 833876320d7SLorenzo Stoakes /* Make sure the guard markers are retained. */ 834876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr_new)); 835876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr_new[4 * page_size])); 836876320d7SLorenzo Stoakes 837876320d7SLorenzo Stoakes /* 838876320d7SLorenzo Stoakes * Clean up - we only need reference the new pointer as we overwrote the 839876320d7SLorenzo Stoakes * PROT_NONE range and moved the existing one. 840876320d7SLorenzo Stoakes */ 841876320d7SLorenzo Stoakes munmap(ptr_new, 5 * page_size); 842876320d7SLorenzo Stoakes } 843876320d7SLorenzo Stoakes 844876320d7SLorenzo Stoakes /* 845876320d7SLorenzo Stoakes * Assert that moving, extending and shrinking memory via mremap() retains 846876320d7SLorenzo Stoakes * guard markers where possible. 847876320d7SLorenzo Stoakes * 848876320d7SLorenzo Stoakes * Expanding should retain guard pages, only now in different position. The user 849876320d7SLorenzo Stoakes * will have to remove guard pages manually to fix up (they'd have to do the 850876320d7SLorenzo Stoakes * same if it were a PROT_NONE mapping). 851876320d7SLorenzo Stoakes */ 852876320d7SLorenzo Stoakes TEST_F(guard_pages, mremap_expand) 853876320d7SLorenzo Stoakes { 854876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 855876320d7SLorenzo Stoakes char *ptr, *ptr_new; 856876320d7SLorenzo Stoakes 857876320d7SLorenzo Stoakes /* Map 10 pages... */ 858876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 859876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 860876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 861876320d7SLorenzo Stoakes /* ...But unmap the last 5 so we can ensure we can expand into them. */ 862876320d7SLorenzo Stoakes ASSERT_EQ(munmap(&ptr[5 * page_size], 5 * page_size), 0); 863876320d7SLorenzo Stoakes 864876320d7SLorenzo Stoakes /* Place guard markers at both ends of the 5 page span. */ 865876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); 866876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[4 * page_size], page_size, MADV_GUARD_INSTALL), 0); 867876320d7SLorenzo Stoakes 868876320d7SLorenzo Stoakes /* Make sure the guarding is in effect. */ 869876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 870876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[4 * page_size])); 871876320d7SLorenzo Stoakes 872876320d7SLorenzo Stoakes /* Now expand to 10 pages. */ 873876320d7SLorenzo Stoakes ptr = mremap(ptr, 5 * page_size, 10 * page_size, 0); 874876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 875876320d7SLorenzo Stoakes 876876320d7SLorenzo Stoakes /* 877876320d7SLorenzo Stoakes * Make sure the guard markers are retained in their original positions. 878876320d7SLorenzo Stoakes */ 879876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 880876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[4 * page_size])); 881876320d7SLorenzo Stoakes 882876320d7SLorenzo Stoakes /* Reserve a region which we can move to and expand into. */ 883876320d7SLorenzo Stoakes ptr_new = mmap(NULL, 20 * page_size, PROT_NONE, 884876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 885876320d7SLorenzo Stoakes ASSERT_NE(ptr_new, MAP_FAILED); 886876320d7SLorenzo Stoakes 887876320d7SLorenzo Stoakes /* Now move and expand into it. */ 888876320d7SLorenzo Stoakes ptr = mremap(ptr, 10 * page_size, 20 * page_size, 889876320d7SLorenzo Stoakes MREMAP_MAYMOVE | MREMAP_FIXED, ptr_new); 890876320d7SLorenzo Stoakes ASSERT_EQ(ptr, ptr_new); 891876320d7SLorenzo Stoakes 892876320d7SLorenzo Stoakes /* 893876320d7SLorenzo Stoakes * Again, make sure the guard markers are retained in their original positions. 894876320d7SLorenzo Stoakes */ 895876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 896876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[4 * page_size])); 897876320d7SLorenzo Stoakes 898876320d7SLorenzo Stoakes /* 899876320d7SLorenzo Stoakes * A real user would have to remove guard markers, but would reasonably 900876320d7SLorenzo Stoakes * expect all characteristics of the mapping to be retained, including 901876320d7SLorenzo Stoakes * guard markers. 902876320d7SLorenzo Stoakes */ 903876320d7SLorenzo Stoakes 904876320d7SLorenzo Stoakes /* Cleanup. */ 905876320d7SLorenzo Stoakes munmap(ptr, 20 * page_size); 906876320d7SLorenzo Stoakes } 907876320d7SLorenzo Stoakes /* 908876320d7SLorenzo Stoakes * Assert that moving, extending and shrinking memory via mremap() retains 909876320d7SLorenzo Stoakes * guard markers where possible. 910876320d7SLorenzo Stoakes * 911876320d7SLorenzo Stoakes * Shrinking will result in markers that are shrunk over being removed. Again, 912876320d7SLorenzo Stoakes * if the user were using a PROT_NONE mapping they'd have to manually fix this 913876320d7SLorenzo Stoakes * up also so this is OK. 914876320d7SLorenzo Stoakes */ 915876320d7SLorenzo Stoakes TEST_F(guard_pages, mremap_shrink) 916876320d7SLorenzo Stoakes { 917876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 918876320d7SLorenzo Stoakes char *ptr; 919876320d7SLorenzo Stoakes int i; 920876320d7SLorenzo Stoakes 921876320d7SLorenzo Stoakes /* Map 5 pages. */ 922876320d7SLorenzo Stoakes ptr = mmap(NULL, 5 * page_size, PROT_READ | PROT_WRITE, 923876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 924876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 925876320d7SLorenzo Stoakes 926876320d7SLorenzo Stoakes /* Place guard markers at both ends of the 5 page span. */ 927876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, page_size, MADV_GUARD_INSTALL), 0); 928876320d7SLorenzo Stoakes ASSERT_EQ(madvise(&ptr[4 * page_size], page_size, MADV_GUARD_INSTALL), 0); 929876320d7SLorenzo Stoakes 930876320d7SLorenzo Stoakes /* Make sure the guarding is in effect. */ 931876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 932876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(&ptr[4 * page_size])); 933876320d7SLorenzo Stoakes 934876320d7SLorenzo Stoakes /* Now shrink to 3 pages. */ 935876320d7SLorenzo Stoakes ptr = mremap(ptr, 5 * page_size, 3 * page_size, MREMAP_MAYMOVE); 936876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 937876320d7SLorenzo Stoakes 938876320d7SLorenzo Stoakes /* We expect the guard marker at the start to be retained... */ 939876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 940876320d7SLorenzo Stoakes 941876320d7SLorenzo Stoakes /* ...But remaining pages will not have guard markers. */ 942876320d7SLorenzo Stoakes for (i = 1; i < 3; i++) { 943876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 944876320d7SLorenzo Stoakes 945876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 946876320d7SLorenzo Stoakes } 947876320d7SLorenzo Stoakes 948876320d7SLorenzo Stoakes /* 949876320d7SLorenzo Stoakes * As with expansion, a real user would have to remove guard pages and 950876320d7SLorenzo Stoakes * fixup. But you'd have to do similar manual things with PROT_NONE 951876320d7SLorenzo Stoakes * mappings too. 952876320d7SLorenzo Stoakes */ 953876320d7SLorenzo Stoakes 954876320d7SLorenzo Stoakes /* 955876320d7SLorenzo Stoakes * If we expand back to the original size, the end marker will, of 956876320d7SLorenzo Stoakes * course, no longer be present. 957876320d7SLorenzo Stoakes */ 958876320d7SLorenzo Stoakes ptr = mremap(ptr, 3 * page_size, 5 * page_size, 0); 959876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 960876320d7SLorenzo Stoakes 961876320d7SLorenzo Stoakes /* Again, we expect the guard marker at the start to be retained... */ 962876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(ptr)); 963876320d7SLorenzo Stoakes 964876320d7SLorenzo Stoakes /* ...But remaining pages will not have guard markers. */ 965876320d7SLorenzo Stoakes for (i = 1; i < 5; i++) { 966876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 967876320d7SLorenzo Stoakes 968876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 969876320d7SLorenzo Stoakes } 970876320d7SLorenzo Stoakes 971876320d7SLorenzo Stoakes /* Cleanup. */ 972876320d7SLorenzo Stoakes munmap(ptr, 5 * page_size); 973876320d7SLorenzo Stoakes } 974876320d7SLorenzo Stoakes 975876320d7SLorenzo Stoakes /* 976876320d7SLorenzo Stoakes * Assert that forking a process with VMAs that do not have VM_WIPEONFORK set 977876320d7SLorenzo Stoakes * retain guard pages. 978876320d7SLorenzo Stoakes */ 979876320d7SLorenzo Stoakes TEST_F(guard_pages, fork) 980876320d7SLorenzo Stoakes { 981876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 982876320d7SLorenzo Stoakes char *ptr; 983876320d7SLorenzo Stoakes pid_t pid; 984876320d7SLorenzo Stoakes int i; 985876320d7SLorenzo Stoakes 986876320d7SLorenzo Stoakes /* Map 10 pages. */ 987876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 988876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 989876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 990876320d7SLorenzo Stoakes 99119b65ffaSLorenzo Stoakes /* Establish guard pages in the first 5 pages. */ 992876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 5 * page_size, MADV_GUARD_INSTALL), 0); 993876320d7SLorenzo Stoakes 994876320d7SLorenzo Stoakes pid = fork(); 995876320d7SLorenzo Stoakes ASSERT_NE(pid, -1); 996876320d7SLorenzo Stoakes if (!pid) { 997876320d7SLorenzo Stoakes /* This is the child process now. */ 998876320d7SLorenzo Stoakes 999876320d7SLorenzo Stoakes /* Assert that the guarding is in effect. */ 1000876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1001876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1002876320d7SLorenzo Stoakes bool result = try_read_write_buf(curr); 1003876320d7SLorenzo Stoakes 1004876320d7SLorenzo Stoakes ASSERT_TRUE(i >= 5 ? result : !result); 1005876320d7SLorenzo Stoakes } 1006876320d7SLorenzo Stoakes 1007876320d7SLorenzo Stoakes /* Now unguard the range.*/ 1008876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); 1009876320d7SLorenzo Stoakes 1010876320d7SLorenzo Stoakes exit(0); 1011876320d7SLorenzo Stoakes } 1012876320d7SLorenzo Stoakes 1013876320d7SLorenzo Stoakes /* Parent process. */ 1014876320d7SLorenzo Stoakes 1015876320d7SLorenzo Stoakes /* Parent simply waits on child. */ 1016876320d7SLorenzo Stoakes waitpid(pid, NULL, 0); 1017876320d7SLorenzo Stoakes 1018876320d7SLorenzo Stoakes /* Child unguard does not impact parent page table state. */ 1019876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1020876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1021876320d7SLorenzo Stoakes bool result = try_read_write_buf(curr); 1022876320d7SLorenzo Stoakes 1023876320d7SLorenzo Stoakes ASSERT_TRUE(i >= 5 ? result : !result); 1024876320d7SLorenzo Stoakes } 1025876320d7SLorenzo Stoakes 1026876320d7SLorenzo Stoakes /* Cleanup. */ 1027876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1028876320d7SLorenzo Stoakes } 1029876320d7SLorenzo Stoakes 1030876320d7SLorenzo Stoakes /* 103119b65ffaSLorenzo Stoakes * Assert expected behaviour after we fork populated ranges of anonymous memory 103219b65ffaSLorenzo Stoakes * and then guard and unguard the range. 103319b65ffaSLorenzo Stoakes */ 103419b65ffaSLorenzo Stoakes TEST_F(guard_pages, fork_cow) 103519b65ffaSLorenzo Stoakes { 103619b65ffaSLorenzo Stoakes const unsigned long page_size = self->page_size; 103719b65ffaSLorenzo Stoakes char *ptr; 103819b65ffaSLorenzo Stoakes pid_t pid; 103919b65ffaSLorenzo Stoakes int i; 104019b65ffaSLorenzo Stoakes 104119b65ffaSLorenzo Stoakes /* Map 10 pages. */ 104219b65ffaSLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 104319b65ffaSLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 104419b65ffaSLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 104519b65ffaSLorenzo Stoakes 104619b65ffaSLorenzo Stoakes /* Populate range. */ 104719b65ffaSLorenzo Stoakes for (i = 0; i < 10 * page_size; i++) { 104819b65ffaSLorenzo Stoakes char chr = 'a' + (i % 26); 104919b65ffaSLorenzo Stoakes 105019b65ffaSLorenzo Stoakes ptr[i] = chr; 105119b65ffaSLorenzo Stoakes } 105219b65ffaSLorenzo Stoakes 105319b65ffaSLorenzo Stoakes pid = fork(); 105419b65ffaSLorenzo Stoakes ASSERT_NE(pid, -1); 105519b65ffaSLorenzo Stoakes if (!pid) { 105619b65ffaSLorenzo Stoakes /* This is the child process now. */ 105719b65ffaSLorenzo Stoakes 105819b65ffaSLorenzo Stoakes /* Ensure the range is as expected. */ 105919b65ffaSLorenzo Stoakes for (i = 0; i < 10 * page_size; i++) { 106019b65ffaSLorenzo Stoakes char expected = 'a' + (i % 26); 106119b65ffaSLorenzo Stoakes char actual = ptr[i]; 106219b65ffaSLorenzo Stoakes 106319b65ffaSLorenzo Stoakes ASSERT_EQ(actual, expected); 106419b65ffaSLorenzo Stoakes } 106519b65ffaSLorenzo Stoakes 106619b65ffaSLorenzo Stoakes /* Establish guard pages across the whole range. */ 106719b65ffaSLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 106819b65ffaSLorenzo Stoakes /* Remove it. */ 106919b65ffaSLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_REMOVE), 0); 107019b65ffaSLorenzo Stoakes 107119b65ffaSLorenzo Stoakes /* 107219b65ffaSLorenzo Stoakes * By removing the guard pages, the page tables will be 107319b65ffaSLorenzo Stoakes * cleared. Assert that we are looking at the zero page now. 107419b65ffaSLorenzo Stoakes */ 107519b65ffaSLorenzo Stoakes for (i = 0; i < 10 * page_size; i++) { 107619b65ffaSLorenzo Stoakes char actual = ptr[i]; 107719b65ffaSLorenzo Stoakes 107819b65ffaSLorenzo Stoakes ASSERT_EQ(actual, '\0'); 107919b65ffaSLorenzo Stoakes } 108019b65ffaSLorenzo Stoakes 108119b65ffaSLorenzo Stoakes exit(0); 108219b65ffaSLorenzo Stoakes } 108319b65ffaSLorenzo Stoakes 108419b65ffaSLorenzo Stoakes /* Parent process. */ 108519b65ffaSLorenzo Stoakes 108619b65ffaSLorenzo Stoakes /* Parent simply waits on child. */ 108719b65ffaSLorenzo Stoakes waitpid(pid, NULL, 0); 108819b65ffaSLorenzo Stoakes 108919b65ffaSLorenzo Stoakes /* Ensure the range is unchanged in parent anon range. */ 109019b65ffaSLorenzo Stoakes for (i = 0; i < 10 * page_size; i++) { 109119b65ffaSLorenzo Stoakes char expected = 'a' + (i % 26); 109219b65ffaSLorenzo Stoakes char actual = ptr[i]; 109319b65ffaSLorenzo Stoakes 109419b65ffaSLorenzo Stoakes ASSERT_EQ(actual, expected); 109519b65ffaSLorenzo Stoakes } 109619b65ffaSLorenzo Stoakes 109719b65ffaSLorenzo Stoakes /* Cleanup. */ 109819b65ffaSLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 109919b65ffaSLorenzo Stoakes } 110019b65ffaSLorenzo Stoakes 110119b65ffaSLorenzo Stoakes /* 1102876320d7SLorenzo Stoakes * Assert that forking a process with VMAs that do have VM_WIPEONFORK set 1103876320d7SLorenzo Stoakes * behave as expected. 1104876320d7SLorenzo Stoakes */ 1105876320d7SLorenzo Stoakes TEST_F(guard_pages, fork_wipeonfork) 1106876320d7SLorenzo Stoakes { 1107876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 1108876320d7SLorenzo Stoakes char *ptr; 1109876320d7SLorenzo Stoakes pid_t pid; 1110876320d7SLorenzo Stoakes int i; 1111876320d7SLorenzo Stoakes 1112876320d7SLorenzo Stoakes /* Map 10 pages. */ 1113876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 1114876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 1115876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 1116876320d7SLorenzo Stoakes 1117876320d7SLorenzo Stoakes /* Mark wipe on fork. */ 1118876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_WIPEONFORK), 0); 1119876320d7SLorenzo Stoakes 1120876320d7SLorenzo Stoakes /* Guard the first 5 pages. */ 1121876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 5 * page_size, MADV_GUARD_INSTALL), 0); 1122876320d7SLorenzo Stoakes 1123876320d7SLorenzo Stoakes pid = fork(); 1124876320d7SLorenzo Stoakes ASSERT_NE(pid, -1); 1125876320d7SLorenzo Stoakes if (!pid) { 1126876320d7SLorenzo Stoakes /* This is the child process now. */ 1127876320d7SLorenzo Stoakes 1128876320d7SLorenzo Stoakes /* Guard will have been wiped. */ 1129876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1130876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1131876320d7SLorenzo Stoakes 1132876320d7SLorenzo Stoakes ASSERT_TRUE(try_read_write_buf(curr)); 1133876320d7SLorenzo Stoakes } 1134876320d7SLorenzo Stoakes 1135876320d7SLorenzo Stoakes exit(0); 1136876320d7SLorenzo Stoakes } 1137876320d7SLorenzo Stoakes 1138876320d7SLorenzo Stoakes /* Parent process. */ 1139876320d7SLorenzo Stoakes 1140876320d7SLorenzo Stoakes waitpid(pid, NULL, 0); 1141876320d7SLorenzo Stoakes 1142876320d7SLorenzo Stoakes /* Guard markers should be in effect.*/ 1143876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1144876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1145876320d7SLorenzo Stoakes bool result = try_read_write_buf(curr); 1146876320d7SLorenzo Stoakes 1147876320d7SLorenzo Stoakes ASSERT_TRUE(i >= 5 ? result : !result); 1148876320d7SLorenzo Stoakes } 1149876320d7SLorenzo Stoakes 1150876320d7SLorenzo Stoakes /* Cleanup. */ 1151876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1152876320d7SLorenzo Stoakes } 1153876320d7SLorenzo Stoakes 1154876320d7SLorenzo Stoakes /* Ensure that MADV_FREE retains guard entries as expected. */ 1155876320d7SLorenzo Stoakes TEST_F(guard_pages, lazyfree) 1156876320d7SLorenzo Stoakes { 1157876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 1158876320d7SLorenzo Stoakes char *ptr; 1159876320d7SLorenzo Stoakes int i; 1160876320d7SLorenzo Stoakes 1161876320d7SLorenzo Stoakes /* Map 10 pages. */ 1162876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 1163876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 1164876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 1165876320d7SLorenzo Stoakes 1166876320d7SLorenzo Stoakes /* Guard range. */ 1167876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 1168876320d7SLorenzo Stoakes 1169876320d7SLorenzo Stoakes /* Ensure guarded. */ 1170876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1171876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1172876320d7SLorenzo Stoakes 1173876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1174876320d7SLorenzo Stoakes } 1175876320d7SLorenzo Stoakes 1176876320d7SLorenzo Stoakes /* Lazyfree range. */ 1177876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_FREE), 0); 1178876320d7SLorenzo Stoakes 1179876320d7SLorenzo Stoakes /* This should leave the guard markers in place. */ 1180876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1181876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1182876320d7SLorenzo Stoakes 1183876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1184876320d7SLorenzo Stoakes } 1185876320d7SLorenzo Stoakes 1186876320d7SLorenzo Stoakes /* Cleanup. */ 1187876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1188876320d7SLorenzo Stoakes } 1189876320d7SLorenzo Stoakes 1190876320d7SLorenzo Stoakes /* Ensure that MADV_POPULATE_READ, MADV_POPULATE_WRITE behave as expected. */ 1191876320d7SLorenzo Stoakes TEST_F(guard_pages, populate) 1192876320d7SLorenzo Stoakes { 1193876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 1194876320d7SLorenzo Stoakes char *ptr; 1195876320d7SLorenzo Stoakes 1196876320d7SLorenzo Stoakes /* Map 10 pages. */ 1197876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 1198876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 1199876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 1200876320d7SLorenzo Stoakes 1201876320d7SLorenzo Stoakes /* Guard range. */ 1202876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 1203876320d7SLorenzo Stoakes 1204876320d7SLorenzo Stoakes /* Populate read should error out... */ 1205876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_POPULATE_READ), -1); 1206876320d7SLorenzo Stoakes ASSERT_EQ(errno, EFAULT); 1207876320d7SLorenzo Stoakes 1208876320d7SLorenzo Stoakes /* ...as should populate write. */ 1209876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_POPULATE_WRITE), -1); 1210876320d7SLorenzo Stoakes ASSERT_EQ(errno, EFAULT); 1211876320d7SLorenzo Stoakes 1212876320d7SLorenzo Stoakes /* Cleanup. */ 1213876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1214876320d7SLorenzo Stoakes } 1215876320d7SLorenzo Stoakes 1216876320d7SLorenzo Stoakes /* Ensure that MADV_COLD, MADV_PAGEOUT do not remove guard markers. */ 1217876320d7SLorenzo Stoakes TEST_F(guard_pages, cold_pageout) 1218876320d7SLorenzo Stoakes { 1219876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 1220876320d7SLorenzo Stoakes char *ptr; 1221876320d7SLorenzo Stoakes int i; 1222876320d7SLorenzo Stoakes 1223876320d7SLorenzo Stoakes /* Map 10 pages. */ 1224876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 1225876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 1226876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 1227876320d7SLorenzo Stoakes 1228876320d7SLorenzo Stoakes /* Guard range. */ 1229876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 1230876320d7SLorenzo Stoakes 1231876320d7SLorenzo Stoakes /* Ensured guarded. */ 1232876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1233876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1234876320d7SLorenzo Stoakes 1235876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1236876320d7SLorenzo Stoakes } 1237876320d7SLorenzo Stoakes 1238876320d7SLorenzo Stoakes /* Now mark cold. This should have no impact on guard markers. */ 1239876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_COLD), 0); 1240876320d7SLorenzo Stoakes 1241876320d7SLorenzo Stoakes /* Should remain guarded. */ 1242876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1243876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1244876320d7SLorenzo Stoakes 1245876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1246876320d7SLorenzo Stoakes } 1247876320d7SLorenzo Stoakes 1248876320d7SLorenzo Stoakes /* OK, now page out. This should equally, have no effect on markers. */ 1249876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_PAGEOUT), 0); 1250876320d7SLorenzo Stoakes 1251876320d7SLorenzo Stoakes /* Should remain guarded. */ 1252876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1253876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1254876320d7SLorenzo Stoakes 1255876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1256876320d7SLorenzo Stoakes } 1257876320d7SLorenzo Stoakes 1258876320d7SLorenzo Stoakes /* Cleanup. */ 1259876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1260876320d7SLorenzo Stoakes } 1261876320d7SLorenzo Stoakes 1262876320d7SLorenzo Stoakes /* Ensure that guard pages do not break userfaultd. */ 1263876320d7SLorenzo Stoakes TEST_F(guard_pages, uffd) 1264876320d7SLorenzo Stoakes { 1265876320d7SLorenzo Stoakes const unsigned long page_size = self->page_size; 1266876320d7SLorenzo Stoakes int uffd; 1267876320d7SLorenzo Stoakes char *ptr; 1268876320d7SLorenzo Stoakes int i; 1269876320d7SLorenzo Stoakes struct uffdio_api api = { 1270876320d7SLorenzo Stoakes .api = UFFD_API, 1271876320d7SLorenzo Stoakes .features = 0, 1272876320d7SLorenzo Stoakes }; 1273876320d7SLorenzo Stoakes struct uffdio_register reg; 1274876320d7SLorenzo Stoakes struct uffdio_range range; 1275876320d7SLorenzo Stoakes 1276876320d7SLorenzo Stoakes /* Set up uffd. */ 1277876320d7SLorenzo Stoakes uffd = userfaultfd(0); 1278876320d7SLorenzo Stoakes if (uffd == -1 && errno == EPERM) 1279876320d7SLorenzo Stoakes ksft_exit_skip("No userfaultfd permissions, try running as root.\n"); 1280876320d7SLorenzo Stoakes ASSERT_NE(uffd, -1); 1281876320d7SLorenzo Stoakes 1282876320d7SLorenzo Stoakes ASSERT_EQ(ioctl(uffd, UFFDIO_API, &api), 0); 1283876320d7SLorenzo Stoakes 1284876320d7SLorenzo Stoakes /* Map 10 pages. */ 1285876320d7SLorenzo Stoakes ptr = mmap(NULL, 10 * page_size, PROT_READ | PROT_WRITE, 1286876320d7SLorenzo Stoakes MAP_ANON | MAP_PRIVATE, -1, 0); 1287876320d7SLorenzo Stoakes ASSERT_NE(ptr, MAP_FAILED); 1288876320d7SLorenzo Stoakes 1289876320d7SLorenzo Stoakes /* Register the range with uffd. */ 1290876320d7SLorenzo Stoakes range.start = (unsigned long)ptr; 1291876320d7SLorenzo Stoakes range.len = 10 * page_size; 1292876320d7SLorenzo Stoakes reg.range = range; 1293876320d7SLorenzo Stoakes reg.mode = UFFDIO_REGISTER_MODE_MISSING; 1294876320d7SLorenzo Stoakes ASSERT_EQ(ioctl(uffd, UFFDIO_REGISTER, ®), 0); 1295876320d7SLorenzo Stoakes 1296876320d7SLorenzo Stoakes /* Guard the range. This should not trigger the uffd. */ 1297876320d7SLorenzo Stoakes ASSERT_EQ(madvise(ptr, 10 * page_size, MADV_GUARD_INSTALL), 0); 1298876320d7SLorenzo Stoakes 1299876320d7SLorenzo Stoakes /* The guarding should behave as usual with no uffd intervention. */ 1300876320d7SLorenzo Stoakes for (i = 0; i < 10; i++) { 1301876320d7SLorenzo Stoakes char *curr = &ptr[i * page_size]; 1302876320d7SLorenzo Stoakes 1303876320d7SLorenzo Stoakes ASSERT_FALSE(try_read_write_buf(curr)); 1304876320d7SLorenzo Stoakes } 1305876320d7SLorenzo Stoakes 1306876320d7SLorenzo Stoakes /* Cleanup. */ 1307876320d7SLorenzo Stoakes ASSERT_EQ(ioctl(uffd, UFFDIO_UNREGISTER, &range), 0); 1308876320d7SLorenzo Stoakes close(uffd); 1309876320d7SLorenzo Stoakes ASSERT_EQ(munmap(ptr, 10 * page_size), 0); 1310876320d7SLorenzo Stoakes } 1311876320d7SLorenzo Stoakes 1312876320d7SLorenzo Stoakes TEST_HARNESS_MAIN 1313