1f58a2dd8SAlexey Dobriyan #if defined __amd64__ || defined __i386__ 25bc73bb3SAlexey Dobriyan /* 35bc73bb3SAlexey Dobriyan * Copyright (c) 2022 Alexey Dobriyan <[email protected]> 45bc73bb3SAlexey Dobriyan * 55bc73bb3SAlexey Dobriyan * Permission to use, copy, modify, and distribute this software for any 65bc73bb3SAlexey Dobriyan * purpose with or without fee is hereby granted, provided that the above 75bc73bb3SAlexey Dobriyan * copyright notice and this permission notice appear in all copies. 85bc73bb3SAlexey Dobriyan * 95bc73bb3SAlexey Dobriyan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 105bc73bb3SAlexey Dobriyan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 115bc73bb3SAlexey Dobriyan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 125bc73bb3SAlexey Dobriyan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 135bc73bb3SAlexey Dobriyan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 145bc73bb3SAlexey Dobriyan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 155bc73bb3SAlexey Dobriyan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 165bc73bb3SAlexey Dobriyan */ 175bc73bb3SAlexey Dobriyan /* 185bc73bb3SAlexey Dobriyan * Create a process without mappings by unmapping everything at once and 195bc73bb3SAlexey Dobriyan * holding it with ptrace(2). See what happens to 205bc73bb3SAlexey Dobriyan * 215bc73bb3SAlexey Dobriyan * /proc/${pid}/maps 225bc73bb3SAlexey Dobriyan * /proc/${pid}/numa_maps 235bc73bb3SAlexey Dobriyan * /proc/${pid}/smaps 245bc73bb3SAlexey Dobriyan * /proc/${pid}/smaps_rollup 255bc73bb3SAlexey Dobriyan */ 26*bf5add39SSwarup Laxman Kotiaklapudi #undef _GNU_SOURCE 27*bf5add39SSwarup Laxman Kotiaklapudi #define _GNU_SOURCE 28*bf5add39SSwarup Laxman Kotiaklapudi 295bc73bb3SAlexey Dobriyan #undef NDEBUG 305bc73bb3SAlexey Dobriyan #include <assert.h> 315bc73bb3SAlexey Dobriyan #include <errno.h> 325316a017SAlexey Dobriyan #include <stdint.h> 335bc73bb3SAlexey Dobriyan #include <stdio.h> 345bc73bb3SAlexey Dobriyan #include <stdlib.h> 355bc73bb3SAlexey Dobriyan #include <string.h> 365bc73bb3SAlexey Dobriyan #include <fcntl.h> 375bc73bb3SAlexey Dobriyan #include <sys/mman.h> 385bc73bb3SAlexey Dobriyan #include <sys/ptrace.h> 395bc73bb3SAlexey Dobriyan #include <sys/resource.h> 40*bf5add39SSwarup Laxman Kotiaklapudi #include <sys/syscall.h> 415bc73bb3SAlexey Dobriyan #include <sys/types.h> 425bc73bb3SAlexey Dobriyan #include <sys/wait.h> 435bc73bb3SAlexey Dobriyan #include <unistd.h> 445bc73bb3SAlexey Dobriyan 454356b11eSAlexey Dobriyan #ifdef __amd64__ 464356b11eSAlexey Dobriyan #define TEST_VSYSCALL 474356b11eSAlexey Dobriyan #endif 484356b11eSAlexey Dobriyan 49*bf5add39SSwarup Laxman Kotiaklapudi #if defined __amd64__ 50*bf5add39SSwarup Laxman Kotiaklapudi #ifndef SYS_pkey_alloc 51*bf5add39SSwarup Laxman Kotiaklapudi #define SYS_pkey_alloc 330 52*bf5add39SSwarup Laxman Kotiaklapudi #endif 53*bf5add39SSwarup Laxman Kotiaklapudi #ifndef SYS_pkey_free 54*bf5add39SSwarup Laxman Kotiaklapudi #define SYS_pkey_free 331 55*bf5add39SSwarup Laxman Kotiaklapudi #endif 56*bf5add39SSwarup Laxman Kotiaklapudi #elif defined __i386__ 57*bf5add39SSwarup Laxman Kotiaklapudi #ifndef SYS_pkey_alloc 58*bf5add39SSwarup Laxman Kotiaklapudi #define SYS_pkey_alloc 381 59*bf5add39SSwarup Laxman Kotiaklapudi #endif 60*bf5add39SSwarup Laxman Kotiaklapudi #ifndef SYS_pkey_free 61*bf5add39SSwarup Laxman Kotiaklapudi #define SYS_pkey_free 382 62*bf5add39SSwarup Laxman Kotiaklapudi #endif 63*bf5add39SSwarup Laxman Kotiaklapudi #else 64*bf5add39SSwarup Laxman Kotiaklapudi #error "SYS_pkey_alloc" 65*bf5add39SSwarup Laxman Kotiaklapudi #endif 66*bf5add39SSwarup Laxman Kotiaklapudi 67*bf5add39SSwarup Laxman Kotiaklapudi static int g_protection_key_support; 68*bf5add39SSwarup Laxman Kotiaklapudi 69*bf5add39SSwarup Laxman Kotiaklapudi static int protection_key_support(void) 70*bf5add39SSwarup Laxman Kotiaklapudi { 71*bf5add39SSwarup Laxman Kotiaklapudi long rv = syscall(SYS_pkey_alloc, 0, 0); 72*bf5add39SSwarup Laxman Kotiaklapudi if (rv > 0) { 73*bf5add39SSwarup Laxman Kotiaklapudi syscall(SYS_pkey_free, (int)rv); 74*bf5add39SSwarup Laxman Kotiaklapudi return 1; 75*bf5add39SSwarup Laxman Kotiaklapudi } else if (rv == -1 && errno == ENOSYS) { 76*bf5add39SSwarup Laxman Kotiaklapudi return 0; 77*bf5add39SSwarup Laxman Kotiaklapudi } else if (rv == -1 && errno == EINVAL) { 78*bf5add39SSwarup Laxman Kotiaklapudi // ospke=n 79*bf5add39SSwarup Laxman Kotiaklapudi return 0; 80*bf5add39SSwarup Laxman Kotiaklapudi } else { 81*bf5add39SSwarup Laxman Kotiaklapudi fprintf(stderr, "%s: error: rv %ld, errno %d\n", __func__, rv, errno); 82*bf5add39SSwarup Laxman Kotiaklapudi exit(EXIT_FAILURE); 83*bf5add39SSwarup Laxman Kotiaklapudi } 84*bf5add39SSwarup Laxman Kotiaklapudi } 85*bf5add39SSwarup Laxman Kotiaklapudi 865bc73bb3SAlexey Dobriyan /* 875bc73bb3SAlexey Dobriyan * 0: vsyscall VMA doesn't exist vsyscall=none 885bc73bb3SAlexey Dobriyan * 1: vsyscall VMA is --xp vsyscall=xonly 895bc73bb3SAlexey Dobriyan * 2: vsyscall VMA is r-xp vsyscall=emulate 905bc73bb3SAlexey Dobriyan */ 915316a017SAlexey Dobriyan static volatile int g_vsyscall; 925bc73bb3SAlexey Dobriyan static const char *g_proc_pid_maps_vsyscall; 935bc73bb3SAlexey Dobriyan static const char *g_proc_pid_smaps_vsyscall; 945bc73bb3SAlexey Dobriyan 955bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_0[] = ""; 965bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_1[] = 975bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"; 985bc73bb3SAlexey Dobriyan static const char proc_pid_maps_vsyscall_2[] = 995bc73bb3SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"; 1005bc73bb3SAlexey Dobriyan 1015bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_0[] = ""; 1025bc73bb3SAlexey Dobriyan 1035bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_1[] = 10420e34aa7SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n" 1055bc73bb3SAlexey Dobriyan "Size: 4 kB\n" 1065bc73bb3SAlexey Dobriyan "KernelPageSize: 4 kB\n" 1075bc73bb3SAlexey Dobriyan "MMUPageSize: 4 kB\n" 1085bc73bb3SAlexey Dobriyan "Rss: 0 kB\n" 1095bc73bb3SAlexey Dobriyan "Pss: 0 kB\n" 1105bc73bb3SAlexey Dobriyan "Pss_Dirty: 0 kB\n" 1115bc73bb3SAlexey Dobriyan "Shared_Clean: 0 kB\n" 1125bc73bb3SAlexey Dobriyan "Shared_Dirty: 0 kB\n" 1135bc73bb3SAlexey Dobriyan "Private_Clean: 0 kB\n" 1145bc73bb3SAlexey Dobriyan "Private_Dirty: 0 kB\n" 1155bc73bb3SAlexey Dobriyan "Referenced: 0 kB\n" 1165bc73bb3SAlexey Dobriyan "Anonymous: 0 kB\n" 11720e34aa7SAlexey Dobriyan "KSM: 0 kB\n" 1185bc73bb3SAlexey Dobriyan "LazyFree: 0 kB\n" 1195bc73bb3SAlexey Dobriyan "AnonHugePages: 0 kB\n" 1205bc73bb3SAlexey Dobriyan "ShmemPmdMapped: 0 kB\n" 1215bc73bb3SAlexey Dobriyan "FilePmdMapped: 0 kB\n" 1225bc73bb3SAlexey Dobriyan "Shared_Hugetlb: 0 kB\n" 1235bc73bb3SAlexey Dobriyan "Private_Hugetlb: 0 kB\n" 1245bc73bb3SAlexey Dobriyan "Swap: 0 kB\n" 1255bc73bb3SAlexey Dobriyan "SwapPss: 0 kB\n" 1265bc73bb3SAlexey Dobriyan "Locked: 0 kB\n" 1275bc73bb3SAlexey Dobriyan "THPeligible: 0\n" 1285bc73bb3SAlexey Dobriyan ; 1295bc73bb3SAlexey Dobriyan 1305bc73bb3SAlexey Dobriyan static const char proc_pid_smaps_vsyscall_2[] = 13120e34aa7SAlexey Dobriyan "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n" 1325bc73bb3SAlexey Dobriyan "Size: 4 kB\n" 1335bc73bb3SAlexey Dobriyan "KernelPageSize: 4 kB\n" 1345bc73bb3SAlexey Dobriyan "MMUPageSize: 4 kB\n" 1355bc73bb3SAlexey Dobriyan "Rss: 0 kB\n" 1365bc73bb3SAlexey Dobriyan "Pss: 0 kB\n" 1375bc73bb3SAlexey Dobriyan "Pss_Dirty: 0 kB\n" 1385bc73bb3SAlexey Dobriyan "Shared_Clean: 0 kB\n" 1395bc73bb3SAlexey Dobriyan "Shared_Dirty: 0 kB\n" 1405bc73bb3SAlexey Dobriyan "Private_Clean: 0 kB\n" 1415bc73bb3SAlexey Dobriyan "Private_Dirty: 0 kB\n" 1425bc73bb3SAlexey Dobriyan "Referenced: 0 kB\n" 1435bc73bb3SAlexey Dobriyan "Anonymous: 0 kB\n" 14420e34aa7SAlexey Dobriyan "KSM: 0 kB\n" 1455bc73bb3SAlexey Dobriyan "LazyFree: 0 kB\n" 1465bc73bb3SAlexey Dobriyan "AnonHugePages: 0 kB\n" 1475bc73bb3SAlexey Dobriyan "ShmemPmdMapped: 0 kB\n" 1485bc73bb3SAlexey Dobriyan "FilePmdMapped: 0 kB\n" 1495bc73bb3SAlexey Dobriyan "Shared_Hugetlb: 0 kB\n" 1505bc73bb3SAlexey Dobriyan "Private_Hugetlb: 0 kB\n" 1515bc73bb3SAlexey Dobriyan "Swap: 0 kB\n" 1525bc73bb3SAlexey Dobriyan "SwapPss: 0 kB\n" 1535bc73bb3SAlexey Dobriyan "Locked: 0 kB\n" 1545bc73bb3SAlexey Dobriyan "THPeligible: 0\n" 1555bc73bb3SAlexey Dobriyan ; 1565bc73bb3SAlexey Dobriyan 1575bc73bb3SAlexey Dobriyan static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___) 1585bc73bb3SAlexey Dobriyan { 1595bc73bb3SAlexey Dobriyan _exit(EXIT_FAILURE); 1605bc73bb3SAlexey Dobriyan } 1615bc73bb3SAlexey Dobriyan 1624356b11eSAlexey Dobriyan #ifdef TEST_VSYSCALL 1635bc73bb3SAlexey Dobriyan static void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___) 1645bc73bb3SAlexey Dobriyan { 1655bc73bb3SAlexey Dobriyan _exit(g_vsyscall); 1665bc73bb3SAlexey Dobriyan } 1675bc73bb3SAlexey Dobriyan 1685bc73bb3SAlexey Dobriyan /* 1695bc73bb3SAlexey Dobriyan * vsyscall page can't be unmapped, probe it directly. 1705bc73bb3SAlexey Dobriyan */ 1715bc73bb3SAlexey Dobriyan static void vsyscall(void) 1725bc73bb3SAlexey Dobriyan { 1735bc73bb3SAlexey Dobriyan pid_t pid; 1745bc73bb3SAlexey Dobriyan int wstatus; 1755bc73bb3SAlexey Dobriyan 1765bc73bb3SAlexey Dobriyan pid = fork(); 1775bc73bb3SAlexey Dobriyan if (pid < 0) { 1785bc73bb3SAlexey Dobriyan fprintf(stderr, "fork, errno %d\n", errno); 1795bc73bb3SAlexey Dobriyan exit(1); 1805bc73bb3SAlexey Dobriyan } 1815bc73bb3SAlexey Dobriyan if (pid == 0) { 1825bc73bb3SAlexey Dobriyan setrlimit(RLIMIT_CORE, &(struct rlimit){}); 1835bc73bb3SAlexey Dobriyan 1845bc73bb3SAlexey Dobriyan /* Hide "segfault at ffffffffff600000" messages. */ 1855bc73bb3SAlexey Dobriyan struct sigaction act = {}; 1865bc73bb3SAlexey Dobriyan act.sa_flags = SA_SIGINFO; 1875bc73bb3SAlexey Dobriyan act.sa_sigaction = sigaction_SIGSEGV_vsyscall; 1885bc73bb3SAlexey Dobriyan sigaction(SIGSEGV, &act, NULL); 1895bc73bb3SAlexey Dobriyan 1905bc73bb3SAlexey Dobriyan g_vsyscall = 0; 1915bc73bb3SAlexey Dobriyan /* gettimeofday(NULL, NULL); */ 1925316a017SAlexey Dobriyan uint64_t rax = 0xffffffffff600000; 1935bc73bb3SAlexey Dobriyan asm volatile ( 1945316a017SAlexey Dobriyan "call *%[rax]" 1955316a017SAlexey Dobriyan : [rax] "+a" (rax) 1965316a017SAlexey Dobriyan : "D" (NULL), "S" (NULL) 1975316a017SAlexey Dobriyan : "rcx", "r11" 1985bc73bb3SAlexey Dobriyan ); 1995bc73bb3SAlexey Dobriyan 2005bc73bb3SAlexey Dobriyan g_vsyscall = 1; 2015bc73bb3SAlexey Dobriyan *(volatile int *)0xffffffffff600000UL; 2025bc73bb3SAlexey Dobriyan 2035bc73bb3SAlexey Dobriyan g_vsyscall = 2; 2045bc73bb3SAlexey Dobriyan exit(g_vsyscall); 2055bc73bb3SAlexey Dobriyan } 2065bc73bb3SAlexey Dobriyan waitpid(pid, &wstatus, 0); 2075bc73bb3SAlexey Dobriyan if (WIFEXITED(wstatus)) { 2085bc73bb3SAlexey Dobriyan g_vsyscall = WEXITSTATUS(wstatus); 2095bc73bb3SAlexey Dobriyan } else { 2105bc73bb3SAlexey Dobriyan fprintf(stderr, "error: vsyscall wstatus %08x\n", wstatus); 2115bc73bb3SAlexey Dobriyan exit(1); 2125bc73bb3SAlexey Dobriyan } 2135bc73bb3SAlexey Dobriyan } 2144356b11eSAlexey Dobriyan #endif 2155bc73bb3SAlexey Dobriyan 2165bc73bb3SAlexey Dobriyan static int test_proc_pid_maps(pid_t pid) 2175bc73bb3SAlexey Dobriyan { 2185bc73bb3SAlexey Dobriyan char buf[4096]; 2195bc73bb3SAlexey Dobriyan snprintf(buf, sizeof(buf), "/proc/%u/maps", pid); 2205bc73bb3SAlexey Dobriyan int fd = open(buf, O_RDONLY); 2215bc73bb3SAlexey Dobriyan if (fd == -1) { 2225bc73bb3SAlexey Dobriyan perror("open /proc/${pid}/maps"); 2235bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 2245bc73bb3SAlexey Dobriyan } else { 2255bc73bb3SAlexey Dobriyan ssize_t rv = read(fd, buf, sizeof(buf)); 2265bc73bb3SAlexey Dobriyan close(fd); 2275bc73bb3SAlexey Dobriyan if (g_vsyscall == 0) { 2285bc73bb3SAlexey Dobriyan assert(rv == 0); 2295bc73bb3SAlexey Dobriyan } else { 2305bc73bb3SAlexey Dobriyan size_t len = strlen(g_proc_pid_maps_vsyscall); 2315bc73bb3SAlexey Dobriyan assert(rv == len); 2325bc73bb3SAlexey Dobriyan assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0); 2335bc73bb3SAlexey Dobriyan } 2345bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 2355bc73bb3SAlexey Dobriyan } 2365bc73bb3SAlexey Dobriyan } 2375bc73bb3SAlexey Dobriyan 2385bc73bb3SAlexey Dobriyan static int test_proc_pid_numa_maps(pid_t pid) 2395bc73bb3SAlexey Dobriyan { 2405bc73bb3SAlexey Dobriyan char buf[4096]; 2415bc73bb3SAlexey Dobriyan snprintf(buf, sizeof(buf), "/proc/%u/numa_maps", pid); 2425bc73bb3SAlexey Dobriyan int fd = open(buf, O_RDONLY); 2435bc73bb3SAlexey Dobriyan if (fd == -1) { 2445bc73bb3SAlexey Dobriyan if (errno == ENOENT) { 2455bc73bb3SAlexey Dobriyan /* 2465bc73bb3SAlexey Dobriyan * /proc/${pid}/numa_maps is under CONFIG_NUMA, 2475bc73bb3SAlexey Dobriyan * it doesn't necessarily exist. 2485bc73bb3SAlexey Dobriyan */ 2495bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 2505bc73bb3SAlexey Dobriyan } 2515bc73bb3SAlexey Dobriyan perror("open /proc/${pid}/numa_maps"); 2525bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 2535bc73bb3SAlexey Dobriyan } else { 2545bc73bb3SAlexey Dobriyan ssize_t rv = read(fd, buf, sizeof(buf)); 2555bc73bb3SAlexey Dobriyan close(fd); 2565bc73bb3SAlexey Dobriyan assert(rv == 0); 2575bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 2585bc73bb3SAlexey Dobriyan } 2595bc73bb3SAlexey Dobriyan } 2605bc73bb3SAlexey Dobriyan 2615bc73bb3SAlexey Dobriyan static int test_proc_pid_smaps(pid_t pid) 2625bc73bb3SAlexey Dobriyan { 2635bc73bb3SAlexey Dobriyan char buf[4096]; 2645bc73bb3SAlexey Dobriyan snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid); 2655bc73bb3SAlexey Dobriyan int fd = open(buf, O_RDONLY); 2665bc73bb3SAlexey Dobriyan if (fd == -1) { 2675bc73bb3SAlexey Dobriyan if (errno == ENOENT) { 2685bc73bb3SAlexey Dobriyan /* 2695bc73bb3SAlexey Dobriyan * /proc/${pid}/smaps is under CONFIG_PROC_PAGE_MONITOR, 2705bc73bb3SAlexey Dobriyan * it doesn't necessarily exist. 2715bc73bb3SAlexey Dobriyan */ 2725bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 2735bc73bb3SAlexey Dobriyan } 2745bc73bb3SAlexey Dobriyan perror("open /proc/${pid}/smaps"); 2755bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 276*bf5add39SSwarup Laxman Kotiaklapudi } 2775bc73bb3SAlexey Dobriyan ssize_t rv = read(fd, buf, sizeof(buf)); 2785bc73bb3SAlexey Dobriyan close(fd); 279*bf5add39SSwarup Laxman Kotiaklapudi 280*bf5add39SSwarup Laxman Kotiaklapudi assert(0 <= rv); 281*bf5add39SSwarup Laxman Kotiaklapudi assert(rv <= sizeof(buf)); 282*bf5add39SSwarup Laxman Kotiaklapudi 2835bc73bb3SAlexey Dobriyan if (g_vsyscall == 0) { 2845bc73bb3SAlexey Dobriyan assert(rv == 0); 2855bc73bb3SAlexey Dobriyan } else { 28620e34aa7SAlexey Dobriyan size_t len = strlen(g_proc_pid_smaps_vsyscall); 2875bc73bb3SAlexey Dobriyan assert(rv > len); 28820e34aa7SAlexey Dobriyan assert(memcmp(buf, g_proc_pid_smaps_vsyscall, len) == 0); 289*bf5add39SSwarup Laxman Kotiaklapudi 290*bf5add39SSwarup Laxman Kotiaklapudi if (g_protection_key_support) { 291*bf5add39SSwarup Laxman Kotiaklapudi #define PROTECTION_KEY "ProtectionKey: 0\n" 292*bf5add39SSwarup Laxman Kotiaklapudi assert(memmem(buf, rv, PROTECTION_KEY, strlen(PROTECTION_KEY))); 2935bc73bb3SAlexey Dobriyan } 294*bf5add39SSwarup Laxman Kotiaklapudi } 295*bf5add39SSwarup Laxman Kotiaklapudi 2965bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 2975bc73bb3SAlexey Dobriyan } 2985bc73bb3SAlexey Dobriyan 2995bc73bb3SAlexey Dobriyan static const char g_smaps_rollup[] = 3005bc73bb3SAlexey Dobriyan "00000000-00000000 ---p 00000000 00:00 0 [rollup]\n" 3015bc73bb3SAlexey Dobriyan "Rss: 0 kB\n" 3025bc73bb3SAlexey Dobriyan "Pss: 0 kB\n" 3035bc73bb3SAlexey Dobriyan "Pss_Dirty: 0 kB\n" 3045bc73bb3SAlexey Dobriyan "Pss_Anon: 0 kB\n" 3055bc73bb3SAlexey Dobriyan "Pss_File: 0 kB\n" 3065bc73bb3SAlexey Dobriyan "Pss_Shmem: 0 kB\n" 3075bc73bb3SAlexey Dobriyan "Shared_Clean: 0 kB\n" 3085bc73bb3SAlexey Dobriyan "Shared_Dirty: 0 kB\n" 3095bc73bb3SAlexey Dobriyan "Private_Clean: 0 kB\n" 3105bc73bb3SAlexey Dobriyan "Private_Dirty: 0 kB\n" 3115bc73bb3SAlexey Dobriyan "Referenced: 0 kB\n" 3125bc73bb3SAlexey Dobriyan "Anonymous: 0 kB\n" 3139d1be94dSAlexey Dobriyan "KSM: 0 kB\n" 3145bc73bb3SAlexey Dobriyan "LazyFree: 0 kB\n" 3155bc73bb3SAlexey Dobriyan "AnonHugePages: 0 kB\n" 3165bc73bb3SAlexey Dobriyan "ShmemPmdMapped: 0 kB\n" 3175bc73bb3SAlexey Dobriyan "FilePmdMapped: 0 kB\n" 3185bc73bb3SAlexey Dobriyan "Shared_Hugetlb: 0 kB\n" 3195bc73bb3SAlexey Dobriyan "Private_Hugetlb: 0 kB\n" 3205bc73bb3SAlexey Dobriyan "Swap: 0 kB\n" 3215bc73bb3SAlexey Dobriyan "SwapPss: 0 kB\n" 3225bc73bb3SAlexey Dobriyan "Locked: 0 kB\n" 3235bc73bb3SAlexey Dobriyan ; 3245bc73bb3SAlexey Dobriyan 3255bc73bb3SAlexey Dobriyan static int test_proc_pid_smaps_rollup(pid_t pid) 3265bc73bb3SAlexey Dobriyan { 3275bc73bb3SAlexey Dobriyan char buf[4096]; 3285bc73bb3SAlexey Dobriyan snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid); 3295bc73bb3SAlexey Dobriyan int fd = open(buf, O_RDONLY); 3305bc73bb3SAlexey Dobriyan if (fd == -1) { 3315bc73bb3SAlexey Dobriyan if (errno == ENOENT) { 3325bc73bb3SAlexey Dobriyan /* 3335bc73bb3SAlexey Dobriyan * /proc/${pid}/smaps_rollup is under CONFIG_PROC_PAGE_MONITOR, 3345bc73bb3SAlexey Dobriyan * it doesn't necessarily exist. 3355bc73bb3SAlexey Dobriyan */ 3365bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 3375bc73bb3SAlexey Dobriyan } 3385bc73bb3SAlexey Dobriyan perror("open /proc/${pid}/smaps_rollup"); 3395bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 3405bc73bb3SAlexey Dobriyan } else { 3415bc73bb3SAlexey Dobriyan ssize_t rv = read(fd, buf, sizeof(buf)); 3425bc73bb3SAlexey Dobriyan close(fd); 3435bc73bb3SAlexey Dobriyan assert(rv == sizeof(g_smaps_rollup) - 1); 3445bc73bb3SAlexey Dobriyan assert(memcmp(buf, g_smaps_rollup, sizeof(g_smaps_rollup) - 1) == 0); 3455bc73bb3SAlexey Dobriyan return EXIT_SUCCESS; 3465bc73bb3SAlexey Dobriyan } 3475bc73bb3SAlexey Dobriyan } 3485bc73bb3SAlexey Dobriyan 3496e79b375SSwarup Laxman Kotiaklapudi static const char *parse_u64(const char *p, const char *const end, uint64_t *rv) 3506e79b375SSwarup Laxman Kotiaklapudi { 3516e79b375SSwarup Laxman Kotiaklapudi *rv = 0; 3526e79b375SSwarup Laxman Kotiaklapudi for (; p != end; p += 1) { 3536e79b375SSwarup Laxman Kotiaklapudi if ('0' <= *p && *p <= '9') { 3546e79b375SSwarup Laxman Kotiaklapudi assert(!__builtin_mul_overflow(*rv, 10, rv)); 3556e79b375SSwarup Laxman Kotiaklapudi assert(!__builtin_add_overflow(*rv, *p - '0', rv)); 3566e79b375SSwarup Laxman Kotiaklapudi } else { 3576e79b375SSwarup Laxman Kotiaklapudi break; 3586e79b375SSwarup Laxman Kotiaklapudi } 3596e79b375SSwarup Laxman Kotiaklapudi } 3606e79b375SSwarup Laxman Kotiaklapudi assert(p != end); 3616e79b375SSwarup Laxman Kotiaklapudi return p; 3626e79b375SSwarup Laxman Kotiaklapudi } 3636e79b375SSwarup Laxman Kotiaklapudi 3646e79b375SSwarup Laxman Kotiaklapudi /* 3656e79b375SSwarup Laxman Kotiaklapudi * There seems to be 2 types of valid output: 3666e79b375SSwarup Laxman Kotiaklapudi * "0 A A B 0 0 0\n" for dynamic exeuctables, 3676e79b375SSwarup Laxman Kotiaklapudi * "0 0 0 B 0 0 0\n" for static executables. 3686e79b375SSwarup Laxman Kotiaklapudi */ 3696e79b375SSwarup Laxman Kotiaklapudi static int test_proc_pid_statm(pid_t pid) 3706e79b375SSwarup Laxman Kotiaklapudi { 3716e79b375SSwarup Laxman Kotiaklapudi char buf[4096]; 3726e79b375SSwarup Laxman Kotiaklapudi snprintf(buf, sizeof(buf), "/proc/%u/statm", pid); 3736e79b375SSwarup Laxman Kotiaklapudi int fd = open(buf, O_RDONLY); 3746e79b375SSwarup Laxman Kotiaklapudi if (fd == -1) { 3756e79b375SSwarup Laxman Kotiaklapudi perror("open /proc/${pid}/statm"); 3766e79b375SSwarup Laxman Kotiaklapudi return EXIT_FAILURE; 3776e79b375SSwarup Laxman Kotiaklapudi } 3786e79b375SSwarup Laxman Kotiaklapudi 3796e79b375SSwarup Laxman Kotiaklapudi ssize_t rv = read(fd, buf, sizeof(buf)); 3806e79b375SSwarup Laxman Kotiaklapudi close(fd); 3816e79b375SSwarup Laxman Kotiaklapudi 3826e79b375SSwarup Laxman Kotiaklapudi assert(rv >= 0); 3836e79b375SSwarup Laxman Kotiaklapudi assert(rv <= sizeof(buf)); 3846e79b375SSwarup Laxman Kotiaklapudi if (0) { 3856e79b375SSwarup Laxman Kotiaklapudi write(1, buf, rv); 3866e79b375SSwarup Laxman Kotiaklapudi } 3876e79b375SSwarup Laxman Kotiaklapudi 3886e79b375SSwarup Laxman Kotiaklapudi const char *p = buf; 3896e79b375SSwarup Laxman Kotiaklapudi const char *const end = p + rv; 3906e79b375SSwarup Laxman Kotiaklapudi 3916e79b375SSwarup Laxman Kotiaklapudi /* size */ 3926e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == '0'); 3936e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 3946e79b375SSwarup Laxman Kotiaklapudi 3956e79b375SSwarup Laxman Kotiaklapudi uint64_t resident; 3966e79b375SSwarup Laxman Kotiaklapudi p = parse_u64(p, end, &resident); 3976e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 3986e79b375SSwarup Laxman Kotiaklapudi 3996e79b375SSwarup Laxman Kotiaklapudi uint64_t shared; 4006e79b375SSwarup Laxman Kotiaklapudi p = parse_u64(p, end, &shared); 4016e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 4026e79b375SSwarup Laxman Kotiaklapudi 4036e79b375SSwarup Laxman Kotiaklapudi uint64_t text; 4046e79b375SSwarup Laxman Kotiaklapudi p = parse_u64(p, end, &text); 4056e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 4066e79b375SSwarup Laxman Kotiaklapudi 4076e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == '0'); 4086e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 4096e79b375SSwarup Laxman Kotiaklapudi 4106e79b375SSwarup Laxman Kotiaklapudi /* data */ 4116e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == '0'); 4126e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == ' '); 4136e79b375SSwarup Laxman Kotiaklapudi 4146e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == '0'); 4156e79b375SSwarup Laxman Kotiaklapudi assert(p != end && *p++ == '\n'); 4166e79b375SSwarup Laxman Kotiaklapudi 4176e79b375SSwarup Laxman Kotiaklapudi assert(p == end); 4186e79b375SSwarup Laxman Kotiaklapudi 4196e79b375SSwarup Laxman Kotiaklapudi /* 4206e79b375SSwarup Laxman Kotiaklapudi * "text" is "mm->end_code - mm->start_code" at execve(2) time. 4216e79b375SSwarup Laxman Kotiaklapudi * munmap() doesn't change it. It can be anything (just link 4226e79b375SSwarup Laxman Kotiaklapudi * statically). It can't be 0 because executing to this point 4236e79b375SSwarup Laxman Kotiaklapudi * implies at least 1 page of code. 4246e79b375SSwarup Laxman Kotiaklapudi */ 4256e79b375SSwarup Laxman Kotiaklapudi assert(text > 0); 4266e79b375SSwarup Laxman Kotiaklapudi 4276e79b375SSwarup Laxman Kotiaklapudi /* 4286e79b375SSwarup Laxman Kotiaklapudi * These two are always equal. Always 0 for statically linked 4296e79b375SSwarup Laxman Kotiaklapudi * executables and sometimes 0 for dynamically linked executables. 4306e79b375SSwarup Laxman Kotiaklapudi * There is no way to tell one from another without parsing ELF 4316e79b375SSwarup Laxman Kotiaklapudi * which is too much for this test. 4326e79b375SSwarup Laxman Kotiaklapudi */ 4336e79b375SSwarup Laxman Kotiaklapudi assert(resident == shared); 4346e79b375SSwarup Laxman Kotiaklapudi 4356e79b375SSwarup Laxman Kotiaklapudi return EXIT_SUCCESS; 4366e79b375SSwarup Laxman Kotiaklapudi } 4376e79b375SSwarup Laxman Kotiaklapudi 4385bc73bb3SAlexey Dobriyan int main(void) 4395bc73bb3SAlexey Dobriyan { 4405bc73bb3SAlexey Dobriyan int rv = EXIT_SUCCESS; 4415bc73bb3SAlexey Dobriyan 4424356b11eSAlexey Dobriyan #ifdef TEST_VSYSCALL 4435bc73bb3SAlexey Dobriyan vsyscall(); 4444356b11eSAlexey Dobriyan #endif 4455bc73bb3SAlexey Dobriyan 4465bc73bb3SAlexey Dobriyan switch (g_vsyscall) { 4475bc73bb3SAlexey Dobriyan case 0: 4485bc73bb3SAlexey Dobriyan g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_0; 4495bc73bb3SAlexey Dobriyan g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_0; 4505bc73bb3SAlexey Dobriyan break; 4515bc73bb3SAlexey Dobriyan case 1: 4525bc73bb3SAlexey Dobriyan g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_1; 4535bc73bb3SAlexey Dobriyan g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_1; 4545bc73bb3SAlexey Dobriyan break; 4555bc73bb3SAlexey Dobriyan case 2: 4565bc73bb3SAlexey Dobriyan g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_2; 4575bc73bb3SAlexey Dobriyan g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_2; 4585bc73bb3SAlexey Dobriyan break; 4595bc73bb3SAlexey Dobriyan default: 4605bc73bb3SAlexey Dobriyan abort(); 4615bc73bb3SAlexey Dobriyan } 4625bc73bb3SAlexey Dobriyan 463*bf5add39SSwarup Laxman Kotiaklapudi g_protection_key_support = protection_key_support(); 464*bf5add39SSwarup Laxman Kotiaklapudi 4655bc73bb3SAlexey Dobriyan pid_t pid = fork(); 4665bc73bb3SAlexey Dobriyan if (pid == -1) { 4675bc73bb3SAlexey Dobriyan perror("fork"); 4685bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 4695bc73bb3SAlexey Dobriyan } else if (pid == 0) { 4705bc73bb3SAlexey Dobriyan rv = ptrace(PTRACE_TRACEME, 0, NULL, NULL); 4715bc73bb3SAlexey Dobriyan if (rv != 0) { 4725bc73bb3SAlexey Dobriyan if (errno == EPERM) { 4735bc73bb3SAlexey Dobriyan fprintf(stderr, 4745bc73bb3SAlexey Dobriyan "Did you know? ptrace(PTRACE_TRACEME) doesn't work under strace.\n" 4755bc73bb3SAlexey Dobriyan ); 4765bc73bb3SAlexey Dobriyan kill(getppid(), SIGTERM); 4775bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 4785bc73bb3SAlexey Dobriyan } 4795bc73bb3SAlexey Dobriyan perror("ptrace PTRACE_TRACEME"); 4805bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 4815bc73bb3SAlexey Dobriyan } 4825bc73bb3SAlexey Dobriyan 4835bc73bb3SAlexey Dobriyan /* 4845bc73bb3SAlexey Dobriyan * Hide "segfault at ..." messages. Signal handler won't run. 4855bc73bb3SAlexey Dobriyan */ 4865bc73bb3SAlexey Dobriyan struct sigaction act = {}; 4875bc73bb3SAlexey Dobriyan act.sa_flags = SA_SIGINFO; 4885bc73bb3SAlexey Dobriyan act.sa_sigaction = sigaction_SIGSEGV; 4895bc73bb3SAlexey Dobriyan sigaction(SIGSEGV, &act, NULL); 4905bc73bb3SAlexey Dobriyan 4915bc73bb3SAlexey Dobriyan #ifdef __amd64__ 4925bc73bb3SAlexey Dobriyan munmap(NULL, ((size_t)1 << 47) - 4096); 4934356b11eSAlexey Dobriyan #elif defined __i386__ 4944356b11eSAlexey Dobriyan { 4954356b11eSAlexey Dobriyan size_t len; 4964356b11eSAlexey Dobriyan 4974356b11eSAlexey Dobriyan for (len = -4096;; len -= 4096) { 4984356b11eSAlexey Dobriyan munmap(NULL, len); 4994356b11eSAlexey Dobriyan } 5004356b11eSAlexey Dobriyan } 5015bc73bb3SAlexey Dobriyan #else 5025bc73bb3SAlexey Dobriyan #error "implement 'unmap everything'" 5035bc73bb3SAlexey Dobriyan #endif 5045bc73bb3SAlexey Dobriyan return EXIT_FAILURE; 5055bc73bb3SAlexey Dobriyan } else { 5065bc73bb3SAlexey Dobriyan /* 5075bc73bb3SAlexey Dobriyan * TODO find reliable way to signal parent that munmap(2) completed. 5085bc73bb3SAlexey Dobriyan * Child can't do it directly because it effectively doesn't exist 5095bc73bb3SAlexey Dobriyan * anymore. Looking at child's VM files isn't 100% reliable either: 5105bc73bb3SAlexey Dobriyan * due to a bug they may not become empty or empty-like. 5115bc73bb3SAlexey Dobriyan */ 5125bc73bb3SAlexey Dobriyan sleep(1); 5135bc73bb3SAlexey Dobriyan 5145bc73bb3SAlexey Dobriyan if (rv == EXIT_SUCCESS) { 5155bc73bb3SAlexey Dobriyan rv = test_proc_pid_maps(pid); 5165bc73bb3SAlexey Dobriyan } 5175bc73bb3SAlexey Dobriyan if (rv == EXIT_SUCCESS) { 5185bc73bb3SAlexey Dobriyan rv = test_proc_pid_numa_maps(pid); 5195bc73bb3SAlexey Dobriyan } 5205bc73bb3SAlexey Dobriyan if (rv == EXIT_SUCCESS) { 5215bc73bb3SAlexey Dobriyan rv = test_proc_pid_smaps(pid); 5225bc73bb3SAlexey Dobriyan } 5235bc73bb3SAlexey Dobriyan if (rv == EXIT_SUCCESS) { 5245bc73bb3SAlexey Dobriyan rv = test_proc_pid_smaps_rollup(pid); 5255bc73bb3SAlexey Dobriyan } 5266e79b375SSwarup Laxman Kotiaklapudi if (rv == EXIT_SUCCESS) { 5276e79b375SSwarup Laxman Kotiaklapudi rv = test_proc_pid_statm(pid); 5286e79b375SSwarup Laxman Kotiaklapudi } 5295bc73bb3SAlexey Dobriyan 5305bc73bb3SAlexey Dobriyan /* Cut the rope. */ 5315bc73bb3SAlexey Dobriyan int wstatus; 5325bc73bb3SAlexey Dobriyan waitpid(pid, &wstatus, 0); 5335bc73bb3SAlexey Dobriyan assert(WIFSTOPPED(wstatus)); 5345bc73bb3SAlexey Dobriyan assert(WSTOPSIG(wstatus) == SIGSEGV); 5355bc73bb3SAlexey Dobriyan } 5365bc73bb3SAlexey Dobriyan 5375bc73bb3SAlexey Dobriyan return rv; 5385bc73bb3SAlexey Dobriyan } 539f58a2dd8SAlexey Dobriyan #else 540f58a2dd8SAlexey Dobriyan int main(void) 541f58a2dd8SAlexey Dobriyan { 542f58a2dd8SAlexey Dobriyan return 4; 543f58a2dd8SAlexey Dobriyan } 544f58a2dd8SAlexey Dobriyan #endif 545