Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542#if defined __amd64__ || defined __i386__/* * Copyright (c) 2022 Alexey Dobriyan <adobriyan@gmail.com> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *//* * Create a process without mappings by unmapping everything at once and * holding it with ptrace(2). See what happens to * * /proc/${pid}/maps * /proc/${pid}/numa_maps * /proc/${pid}/smaps * /proc/${pid}/smaps_rollup */#undef _GNU_SOURCE#define _GNU_SOURCE
#undef NDEBUG#include <assert.h>#include <errno.h>#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <sys/mman.h>#include <sys/ptrace.h>#include <sys/resource.h>#include <sys/syscall.h>#include <sys/types.h>#include <sys/wait.h>#include <unistd.h>
#ifdef __amd64__#define TEST_VSYSCALL#endif
#if defined __amd64__ #ifndef SYS_pkey_alloc #define SYS_pkey_alloc 330 #endif #ifndef SYS_pkey_free #define SYS_pkey_free 331 #endif#elif defined __i386__ #ifndef SYS_pkey_alloc #define SYS_pkey_alloc 381 #endif #ifndef SYS_pkey_free #define SYS_pkey_free 382 #endif#else #error "SYS_pkey_alloc"#endif
static int g_protection_key_support;
static int protection_key_support(void){ long rv = syscall(SYS_pkey_alloc, 0, 0); if (rv > 0) { syscall(SYS_pkey_free, (int)rv); return 1; } else if (rv == -1 && errno == ENOSYS) { return 0; } else if (rv == -1 && errno == EINVAL) { // ospke=n return 0; } else { fprintf(stderr, "%s: error: rv %ld, errno %d\n", __func__, rv, errno); exit(EXIT_FAILURE); }}
/* * 0: vsyscall VMA doesn't exist vsyscall=none * 1: vsyscall VMA is --xp vsyscall=xonly * 2: vsyscall VMA is r-xp vsyscall=emulate */static volatile int g_vsyscall;static const char *g_proc_pid_maps_vsyscall;static const char *g_proc_pid_smaps_vsyscall;
static const char proc_pid_maps_vsyscall_0[] = "";static const char proc_pid_maps_vsyscall_1[] ="ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n";static const char proc_pid_maps_vsyscall_2[] ="ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n";
static const char proc_pid_smaps_vsyscall_0[] = "";
static const char proc_pid_smaps_vsyscall_1[] ="ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n""Size: 4 kB\n""KernelPageSize: 4 kB\n""MMUPageSize: 4 kB\n""Rss: 0 kB\n""Pss: 0 kB\n""Pss_Dirty: 0 kB\n""Shared_Clean: 0 kB\n""Shared_Dirty: 0 kB\n""Private_Clean: 0 kB\n""Private_Dirty: 0 kB\n""Referenced: 0 kB\n""Anonymous: 0 kB\n""KSM: 0 kB\n""LazyFree: 0 kB\n""AnonHugePages: 0 kB\n""ShmemPmdMapped: 0 kB\n""FilePmdMapped: 0 kB\n""Shared_Hugetlb: 0 kB\n""Private_Hugetlb: 0 kB\n""Swap: 0 kB\n""SwapPss: 0 kB\n""Locked: 0 kB\n""THPeligible: 0\n";
static const char proc_pid_smaps_vsyscall_2[] ="ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n""Size: 4 kB\n""KernelPageSize: 4 kB\n""MMUPageSize: 4 kB\n""Rss: 0 kB\n""Pss: 0 kB\n""Pss_Dirty: 0 kB\n""Shared_Clean: 0 kB\n""Shared_Dirty: 0 kB\n""Private_Clean: 0 kB\n""Private_Dirty: 0 kB\n""Referenced: 0 kB\n""Anonymous: 0 kB\n""KSM: 0 kB\n""LazyFree: 0 kB\n""AnonHugePages: 0 kB\n""ShmemPmdMapped: 0 kB\n""FilePmdMapped: 0 kB\n""Shared_Hugetlb: 0 kB\n""Private_Hugetlb: 0 kB\n""Swap: 0 kB\n""SwapPss: 0 kB\n""Locked: 0 kB\n""THPeligible: 0\n";
static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___){ _exit(EXIT_FAILURE);}
#ifdef TEST_VSYSCALLstatic void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___){ _exit(g_vsyscall);}
/* * vsyscall page can't be unmapped, probe it directly. */static void vsyscall(void){ pid_t pid; int wstatus;
pid = fork(); if (pid < 0) { fprintf(stderr, "fork, errno %d\n", errno); exit(1); } if (pid == 0) { setrlimit(RLIMIT_CORE, &(struct rlimit){});
/* Hide "segfault at ffffffffff600000" messages. */ struct sigaction act = {}; act.sa_flags = SA_SIGINFO; act.sa_sigaction = sigaction_SIGSEGV_vsyscall; sigaction(SIGSEGV, &act, NULL);
g_vsyscall = 0; /* gettimeofday(NULL, NULL); */ uint64_t rax = 0xffffffffff600000; asm volatile ( "call *%[rax]" : [rax] "+a" (rax) : "D" (NULL), "S" (NULL) : "rcx", "r11" );
g_vsyscall = 1; *(volatile int *)0xffffffffff600000UL;
g_vsyscall = 2; exit(g_vsyscall); } waitpid(pid, &wstatus, 0); if (WIFEXITED(wstatus)) { g_vsyscall = WEXITSTATUS(wstatus); } else { fprintf(stderr, "error: vsyscall wstatus %08x\n", wstatus); exit(1); }}#endif
static int test_proc_pid_maps(pid_t pid){ char buf[4096]; snprintf(buf, sizeof(buf), "/proc/%u/maps", pid); int fd = open(buf, O_RDONLY); if (fd == -1) { perror("open /proc/${pid}/maps"); return EXIT_FAILURE; } else { ssize_t rv = read(fd, buf, sizeof(buf)); close(fd); if (g_vsyscall == 0) { assert(rv == 0); } else { size_t len = strlen(g_proc_pid_maps_vsyscall); assert(rv == len); assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0); } return EXIT_SUCCESS; }}
static int test_proc_pid_numa_maps(pid_t pid){ char buf[4096]; snprintf(buf, sizeof(buf), "/proc/%u/numa_maps", pid); int fd = open(buf, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* * /proc/${pid}/numa_maps is under CONFIG_NUMA, * it doesn't necessarily exist. */ return EXIT_SUCCESS; } perror("open /proc/${pid}/numa_maps"); return EXIT_FAILURE; } else { ssize_t rv = read(fd, buf, sizeof(buf)); close(fd); assert(rv == 0); return EXIT_SUCCESS; }}
static int test_proc_pid_smaps(pid_t pid){ char buf[4096]; snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid); int fd = open(buf, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* * /proc/${pid}/smaps is under CONFIG_PROC_PAGE_MONITOR, * it doesn't necessarily exist. */ return EXIT_SUCCESS; } perror("open /proc/${pid}/smaps"); return EXIT_FAILURE; } ssize_t rv = read(fd, buf, sizeof(buf)); close(fd);
assert(0 <= rv); assert(rv <= sizeof(buf));
if (g_vsyscall == 0) { assert(rv == 0); } else { size_t len = strlen(g_proc_pid_smaps_vsyscall); assert(rv > len); assert(memcmp(buf, g_proc_pid_smaps_vsyscall, len) == 0);
if (g_protection_key_support) {#define PROTECTION_KEY "ProtectionKey: 0\n" assert(memmem(buf, rv, PROTECTION_KEY, strlen(PROTECTION_KEY))); } }
return EXIT_SUCCESS;}
static const char g_smaps_rollup[] ="00000000-00000000 ---p 00000000 00:00 0 [rollup]\n""Rss: 0 kB\n""Pss: 0 kB\n""Pss_Dirty: 0 kB\n""Pss_Anon: 0 kB\n""Pss_File: 0 kB\n""Pss_Shmem: 0 kB\n""Shared_Clean: 0 kB\n""Shared_Dirty: 0 kB\n""Private_Clean: 0 kB\n""Private_Dirty: 0 kB\n""Referenced: 0 kB\n""Anonymous: 0 kB\n""KSM: 0 kB\n""LazyFree: 0 kB\n""AnonHugePages: 0 kB\n""ShmemPmdMapped: 0 kB\n""FilePmdMapped: 0 kB\n""Shared_Hugetlb: 0 kB\n""Private_Hugetlb: 0 kB\n""Swap: 0 kB\n""SwapPss: 0 kB\n""Locked: 0 kB\n";
static int test_proc_pid_smaps_rollup(pid_t pid){ char buf[4096]; snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid); int fd = open(buf, O_RDONLY); if (fd == -1) { if (errno == ENOENT) { /* * /proc/${pid}/smaps_rollup is under CONFIG_PROC_PAGE_MONITOR, * it doesn't necessarily exist. */ return EXIT_SUCCESS; } perror("open /proc/${pid}/smaps_rollup"); return EXIT_FAILURE; } else { ssize_t rv = read(fd, buf, sizeof(buf)); close(fd); assert(rv == sizeof(g_smaps_rollup) - 1); assert(memcmp(buf, g_smaps_rollup, sizeof(g_smaps_rollup) - 1) == 0); return EXIT_SUCCESS; }}
static const char *parse_u64(const char *p, const char *const end, uint64_t *rv){ *rv = 0; for (; p != end; p += 1) { if ('0' <= *p && *p <= '9') { assert(!__builtin_mul_overflow(*rv, 10, rv)); assert(!__builtin_add_overflow(*rv, *p - '0', rv)); } else { break; } } assert(p != end); return p;}
/* * There seems to be 2 types of valid output: * "0 A A B 0 0 0\n" for dynamic exeuctables, * "0 0 0 B 0 0 0\n" for static executables. */static int test_proc_pid_statm(pid_t pid){ char buf[4096]; snprintf(buf, sizeof(buf), "/proc/%u/statm", pid); int fd = open(buf, O_RDONLY); if (fd == -1) { perror("open /proc/${pid}/statm"); return EXIT_FAILURE; }
ssize_t rv = read(fd, buf, sizeof(buf)); close(fd);
assert(rv >= 0); assert(rv <= sizeof(buf));
const char *p = buf; const char *const end = p + rv;
/* size */ assert(p != end && *p++ == '0'); assert(p != end && *p++ == ' ');
uint64_t resident; p = parse_u64(p, end, &resident); assert(p != end && *p++ == ' ');
uint64_t shared; p = parse_u64(p, end, &shared); assert(p != end && *p++ == ' ');
uint64_t text; p = parse_u64(p, end, &text); assert(p != end && *p++ == ' ');
assert(p != end && *p++ == '0'); assert(p != end && *p++ == ' ');
/* data */ assert(p != end && *p++ == '0'); assert(p != end && *p++ == ' ');
assert(p != end && *p++ == '0'); assert(p != end && *p++ == '\n');
assert(p == end);
/* * "text" is "mm->end_code - mm->start_code" at execve(2) time. * munmap() doesn't change it. It can be anything (just link * statically). It can't be 0 because executing to this point * implies at least 1 page of code. */ assert(text > 0);
/* * These two are always equal. Always 0 for statically linked * executables and sometimes 0 for dynamically linked executables. * There is no way to tell one from another without parsing ELF * which is too much for this test. */ assert(resident == shared);
return EXIT_SUCCESS;}
int main(void){ int rv = EXIT_SUCCESS;
#ifdef TEST_VSYSCALL vsyscall();#endif
switch (g_vsyscall) { case 0: g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_0; g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_0; break; case 1: g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_1; g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_1; break; case 2: g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_2; g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_2; break; default: abort(); }
g_protection_key_support = protection_key_support();
pid_t pid = fork(); if (pid == -1) { perror("fork"); return EXIT_FAILURE; } else if (pid == 0) { rv = ptrace(PTRACE_TRACEME, 0, NULL, NULL); if (rv != 0) { if (errno == EPERM) { fprintf(stderr,"Did you know? ptrace(PTRACE_TRACEME) doesn't work under strace.\n" ); kill(getppid(), SIGTERM); return EXIT_FAILURE; } perror("ptrace PTRACE_TRACEME"); return EXIT_FAILURE; }
/* * Hide "segfault at ..." messages. Signal handler won't run. */ struct sigaction act = {}; act.sa_flags = SA_SIGINFO; act.sa_sigaction = sigaction_SIGSEGV; sigaction(SIGSEGV, &act, NULL);
#ifdef __amd64__ munmap(NULL, ((size_t)1 << 47) - 4096);#elif defined __i386__ { size_t len;
for (len = -4096;; len -= 4096) { munmap(NULL, len); } }#else#error "implement 'unmap everything'"#endif return EXIT_FAILURE; } else { /* * TODO find reliable way to signal parent that munmap(2) completed. * Child can't do it directly because it effectively doesn't exist * anymore. Looking at child's VM files isn't 100% reliable either: * due to a bug they may not become empty or empty-like. */ sleep(1);
if (rv == EXIT_SUCCESS) { rv = test_proc_pid_maps(pid); } if (rv == EXIT_SUCCESS) { rv = test_proc_pid_numa_maps(pid); } if (rv == EXIT_SUCCESS) { rv = test_proc_pid_smaps(pid); } if (rv == EXIT_SUCCESS) { rv = test_proc_pid_smaps_rollup(pid); } if (rv == EXIT_SUCCESS) { rv = test_proc_pid_statm(pid); }
/* Cut the rope. */ int wstatus; waitpid(pid, &wstatus, 0); assert(WIFSTOPPED(wstatus)); assert(WSTOPSIG(wstatus) == SIGSEGV); }
return rv;}#elseint main(void){ return 4;}#endif