proc-empty-vm.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. #if defined __amd64__ || defined __i386__
  2. /*
  3. * Copyright (c) 2022 Alexey Dobriyan <adobriyan@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /*
  18. * Create a process without mappings by unmapping everything at once and
  19. * holding it with ptrace(2). See what happens to
  20. *
  21. * /proc/${pid}/maps
  22. * /proc/${pid}/numa_maps
  23. * /proc/${pid}/smaps
  24. * /proc/${pid}/smaps_rollup
  25. */
  26. #undef _GNU_SOURCE
  27. #define _GNU_SOURCE
  28. #undef NDEBUG
  29. #include <assert.h>
  30. #include <errno.h>
  31. #include <stdint.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <fcntl.h>
  36. #include <sys/mman.h>
  37. #include <sys/ptrace.h>
  38. #include <sys/resource.h>
  39. #include <sys/syscall.h>
  40. #include <sys/types.h>
  41. #include <sys/wait.h>
  42. #include <unistd.h>
  43. #ifdef __amd64__
  44. #define TEST_VSYSCALL
  45. #endif
  46. #if defined __amd64__
  47. #ifndef SYS_pkey_alloc
  48. #define SYS_pkey_alloc 330
  49. #endif
  50. #ifndef SYS_pkey_free
  51. #define SYS_pkey_free 331
  52. #endif
  53. #elif defined __i386__
  54. #ifndef SYS_pkey_alloc
  55. #define SYS_pkey_alloc 381
  56. #endif
  57. #ifndef SYS_pkey_free
  58. #define SYS_pkey_free 382
  59. #endif
  60. #else
  61. #error "SYS_pkey_alloc"
  62. #endif
  63. static int g_protection_key_support;
  64. static int protection_key_support(void)
  65. {
  66. long rv = syscall(SYS_pkey_alloc, 0, 0);
  67. if (rv > 0) {
  68. syscall(SYS_pkey_free, (int)rv);
  69. return 1;
  70. } else if (rv == -1 && errno == ENOSYS) {
  71. return 0;
  72. } else if (rv == -1 && errno == EINVAL) {
  73. // ospke=n
  74. return 0;
  75. } else {
  76. fprintf(stderr, "%s: error: rv %ld, errno %d\n", __func__, rv, errno);
  77. exit(EXIT_FAILURE);
  78. }
  79. }
  80. /*
  81. * 0: vsyscall VMA doesn't exist vsyscall=none
  82. * 1: vsyscall VMA is --xp vsyscall=xonly
  83. * 2: vsyscall VMA is r-xp vsyscall=emulate
  84. */
  85. static volatile int g_vsyscall;
  86. static const char *g_proc_pid_maps_vsyscall;
  87. static const char *g_proc_pid_smaps_vsyscall;
  88. static const char proc_pid_maps_vsyscall_0[] = "";
  89. static const char proc_pid_maps_vsyscall_1[] =
  90. "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n";
  91. static const char proc_pid_maps_vsyscall_2[] =
  92. "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n";
  93. static const char proc_pid_smaps_vsyscall_0[] = "";
  94. static const char proc_pid_smaps_vsyscall_1[] =
  95. "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n"
  96. "Size: 4 kB\n"
  97. "KernelPageSize: 4 kB\n"
  98. "MMUPageSize: 4 kB\n"
  99. "Rss: 0 kB\n"
  100. "Pss: 0 kB\n"
  101. "Pss_Dirty: 0 kB\n"
  102. "Shared_Clean: 0 kB\n"
  103. "Shared_Dirty: 0 kB\n"
  104. "Private_Clean: 0 kB\n"
  105. "Private_Dirty: 0 kB\n"
  106. "Referenced: 0 kB\n"
  107. "Anonymous: 0 kB\n"
  108. "KSM: 0 kB\n"
  109. "LazyFree: 0 kB\n"
  110. "AnonHugePages: 0 kB\n"
  111. "ShmemPmdMapped: 0 kB\n"
  112. "FilePmdMapped: 0 kB\n"
  113. "Shared_Hugetlb: 0 kB\n"
  114. "Private_Hugetlb: 0 kB\n"
  115. "Swap: 0 kB\n"
  116. "SwapPss: 0 kB\n"
  117. "Locked: 0 kB\n"
  118. "THPeligible: 0\n"
  119. ;
  120. static const char proc_pid_smaps_vsyscall_2[] =
  121. "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n"
  122. "Size: 4 kB\n"
  123. "KernelPageSize: 4 kB\n"
  124. "MMUPageSize: 4 kB\n"
  125. "Rss: 0 kB\n"
  126. "Pss: 0 kB\n"
  127. "Pss_Dirty: 0 kB\n"
  128. "Shared_Clean: 0 kB\n"
  129. "Shared_Dirty: 0 kB\n"
  130. "Private_Clean: 0 kB\n"
  131. "Private_Dirty: 0 kB\n"
  132. "Referenced: 0 kB\n"
  133. "Anonymous: 0 kB\n"
  134. "KSM: 0 kB\n"
  135. "LazyFree: 0 kB\n"
  136. "AnonHugePages: 0 kB\n"
  137. "ShmemPmdMapped: 0 kB\n"
  138. "FilePmdMapped: 0 kB\n"
  139. "Shared_Hugetlb: 0 kB\n"
  140. "Private_Hugetlb: 0 kB\n"
  141. "Swap: 0 kB\n"
  142. "SwapPss: 0 kB\n"
  143. "Locked: 0 kB\n"
  144. "THPeligible: 0\n"
  145. ;
  146. static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
  147. {
  148. _exit(EXIT_FAILURE);
  149. }
  150. #ifdef TEST_VSYSCALL
  151. static void sigaction_SIGSEGV_vsyscall(int _, siginfo_t *__, void *___)
  152. {
  153. _exit(g_vsyscall);
  154. }
  155. /*
  156. * vsyscall page can't be unmapped, probe it directly.
  157. */
  158. static void vsyscall(void)
  159. {
  160. pid_t pid;
  161. int wstatus;
  162. pid = fork();
  163. if (pid < 0) {
  164. fprintf(stderr, "fork, errno %d\n", errno);
  165. exit(1);
  166. }
  167. if (pid == 0) {
  168. setrlimit(RLIMIT_CORE, &(struct rlimit){});
  169. /* Hide "segfault at ffffffffff600000" messages. */
  170. struct sigaction act = {};
  171. act.sa_flags = SA_SIGINFO;
  172. act.sa_sigaction = sigaction_SIGSEGV_vsyscall;
  173. sigaction(SIGSEGV, &act, NULL);
  174. g_vsyscall = 0;
  175. /* gettimeofday(NULL, NULL); */
  176. uint64_t rax = 0xffffffffff600000;
  177. asm volatile (
  178. "call *%[rax]"
  179. : [rax] "+a" (rax)
  180. : "D" (NULL), "S" (NULL)
  181. : "rcx", "r11"
  182. );
  183. g_vsyscall = 1;
  184. *(volatile int *)0xffffffffff600000UL;
  185. g_vsyscall = 2;
  186. exit(g_vsyscall);
  187. }
  188. waitpid(pid, &wstatus, 0);
  189. if (WIFEXITED(wstatus)) {
  190. g_vsyscall = WEXITSTATUS(wstatus);
  191. } else {
  192. fprintf(stderr, "error: vsyscall wstatus %08x\n", wstatus);
  193. exit(1);
  194. }
  195. }
  196. #endif
  197. static int test_proc_pid_maps(pid_t pid)
  198. {
  199. char buf[4096];
  200. snprintf(buf, sizeof(buf), "/proc/%u/maps", pid);
  201. int fd = open(buf, O_RDONLY);
  202. if (fd == -1) {
  203. perror("open /proc/${pid}/maps");
  204. return EXIT_FAILURE;
  205. } else {
  206. ssize_t rv = read(fd, buf, sizeof(buf));
  207. close(fd);
  208. if (g_vsyscall == 0) {
  209. assert(rv == 0);
  210. } else {
  211. size_t len = strlen(g_proc_pid_maps_vsyscall);
  212. assert(rv == len);
  213. assert(memcmp(buf, g_proc_pid_maps_vsyscall, len) == 0);
  214. }
  215. return EXIT_SUCCESS;
  216. }
  217. }
  218. static int test_proc_pid_numa_maps(pid_t pid)
  219. {
  220. char buf[4096];
  221. snprintf(buf, sizeof(buf), "/proc/%u/numa_maps", pid);
  222. int fd = open(buf, O_RDONLY);
  223. if (fd == -1) {
  224. if (errno == ENOENT) {
  225. /*
  226. * /proc/${pid}/numa_maps is under CONFIG_NUMA,
  227. * it doesn't necessarily exist.
  228. */
  229. return EXIT_SUCCESS;
  230. }
  231. perror("open /proc/${pid}/numa_maps");
  232. return EXIT_FAILURE;
  233. } else {
  234. ssize_t rv = read(fd, buf, sizeof(buf));
  235. close(fd);
  236. assert(rv == 0);
  237. return EXIT_SUCCESS;
  238. }
  239. }
  240. static int test_proc_pid_smaps(pid_t pid)
  241. {
  242. char buf[4096];
  243. snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid);
  244. int fd = open(buf, O_RDONLY);
  245. if (fd == -1) {
  246. if (errno == ENOENT) {
  247. /*
  248. * /proc/${pid}/smaps is under CONFIG_PROC_PAGE_MONITOR,
  249. * it doesn't necessarily exist.
  250. */
  251. return EXIT_SUCCESS;
  252. }
  253. perror("open /proc/${pid}/smaps");
  254. return EXIT_FAILURE;
  255. }
  256. ssize_t rv = read(fd, buf, sizeof(buf));
  257. close(fd);
  258. assert(0 <= rv);
  259. assert(rv <= sizeof(buf));
  260. if (g_vsyscall == 0) {
  261. assert(rv == 0);
  262. } else {
  263. size_t len = strlen(g_proc_pid_smaps_vsyscall);
  264. assert(rv > len);
  265. assert(memcmp(buf, g_proc_pid_smaps_vsyscall, len) == 0);
  266. if (g_protection_key_support) {
  267. #define PROTECTION_KEY "ProtectionKey: 0\n"
  268. assert(memmem(buf, rv, PROTECTION_KEY, strlen(PROTECTION_KEY)));
  269. }
  270. }
  271. return EXIT_SUCCESS;
  272. }
  273. static const char g_smaps_rollup[] =
  274. "00000000-00000000 ---p 00000000 00:00 0 [rollup]\n"
  275. "Rss: 0 kB\n"
  276. "Pss: 0 kB\n"
  277. "Pss_Dirty: 0 kB\n"
  278. "Pss_Anon: 0 kB\n"
  279. "Pss_File: 0 kB\n"
  280. "Pss_Shmem: 0 kB\n"
  281. "Shared_Clean: 0 kB\n"
  282. "Shared_Dirty: 0 kB\n"
  283. "Private_Clean: 0 kB\n"
  284. "Private_Dirty: 0 kB\n"
  285. "Referenced: 0 kB\n"
  286. "Anonymous: 0 kB\n"
  287. "KSM: 0 kB\n"
  288. "LazyFree: 0 kB\n"
  289. "AnonHugePages: 0 kB\n"
  290. "ShmemPmdMapped: 0 kB\n"
  291. "FilePmdMapped: 0 kB\n"
  292. "Shared_Hugetlb: 0 kB\n"
  293. "Private_Hugetlb: 0 kB\n"
  294. "Swap: 0 kB\n"
  295. "SwapPss: 0 kB\n"
  296. "Locked: 0 kB\n"
  297. ;
  298. static int test_proc_pid_smaps_rollup(pid_t pid)
  299. {
  300. char buf[4096];
  301. snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid);
  302. int fd = open(buf, O_RDONLY);
  303. if (fd == -1) {
  304. if (errno == ENOENT) {
  305. /*
  306. * /proc/${pid}/smaps_rollup is under CONFIG_PROC_PAGE_MONITOR,
  307. * it doesn't necessarily exist.
  308. */
  309. return EXIT_SUCCESS;
  310. }
  311. perror("open /proc/${pid}/smaps_rollup");
  312. return EXIT_FAILURE;
  313. } else {
  314. ssize_t rv = read(fd, buf, sizeof(buf));
  315. close(fd);
  316. assert(rv == sizeof(g_smaps_rollup) - 1);
  317. assert(memcmp(buf, g_smaps_rollup, sizeof(g_smaps_rollup) - 1) == 0);
  318. return EXIT_SUCCESS;
  319. }
  320. }
  321. static const char *parse_u64(const char *p, const char *const end, uint64_t *rv)
  322. {
  323. *rv = 0;
  324. for (; p != end; p += 1) {
  325. if ('0' <= *p && *p <= '9') {
  326. assert(!__builtin_mul_overflow(*rv, 10, rv));
  327. assert(!__builtin_add_overflow(*rv, *p - '0', rv));
  328. } else {
  329. break;
  330. }
  331. }
  332. assert(p != end);
  333. return p;
  334. }
  335. /*
  336. * There seems to be 2 types of valid output:
  337. * "0 A A B 0 0 0\n" for dynamic exeuctables,
  338. * "0 0 0 B 0 0 0\n" for static executables.
  339. */
  340. static int test_proc_pid_statm(pid_t pid)
  341. {
  342. char buf[4096];
  343. snprintf(buf, sizeof(buf), "/proc/%u/statm", pid);
  344. int fd = open(buf, O_RDONLY);
  345. if (fd == -1) {
  346. perror("open /proc/${pid}/statm");
  347. return EXIT_FAILURE;
  348. }
  349. ssize_t rv = read(fd, buf, sizeof(buf));
  350. close(fd);
  351. assert(rv >= 0);
  352. assert(rv <= sizeof(buf));
  353. const char *p = buf;
  354. const char *const end = p + rv;
  355. /* size */
  356. assert(p != end && *p++ == '0');
  357. assert(p != end && *p++ == ' ');
  358. uint64_t resident;
  359. p = parse_u64(p, end, &resident);
  360. assert(p != end && *p++ == ' ');
  361. uint64_t shared;
  362. p = parse_u64(p, end, &shared);
  363. assert(p != end && *p++ == ' ');
  364. uint64_t text;
  365. p = parse_u64(p, end, &text);
  366. assert(p != end && *p++ == ' ');
  367. assert(p != end && *p++ == '0');
  368. assert(p != end && *p++ == ' ');
  369. /* data */
  370. assert(p != end && *p++ == '0');
  371. assert(p != end && *p++ == ' ');
  372. assert(p != end && *p++ == '0');
  373. assert(p != end && *p++ == '\n');
  374. assert(p == end);
  375. /*
  376. * "text" is "mm->end_code - mm->start_code" at execve(2) time.
  377. * munmap() doesn't change it. It can be anything (just link
  378. * statically). It can't be 0 because executing to this point
  379. * implies at least 1 page of code.
  380. */
  381. assert(text > 0);
  382. /*
  383. * These two are always equal. Always 0 for statically linked
  384. * executables and sometimes 0 for dynamically linked executables.
  385. * There is no way to tell one from another without parsing ELF
  386. * which is too much for this test.
  387. */
  388. assert(resident == shared);
  389. return EXIT_SUCCESS;
  390. }
  391. int main(void)
  392. {
  393. int rv = EXIT_SUCCESS;
  394. #ifdef TEST_VSYSCALL
  395. vsyscall();
  396. #endif
  397. switch (g_vsyscall) {
  398. case 0:
  399. g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_0;
  400. g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_0;
  401. break;
  402. case 1:
  403. g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_1;
  404. g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_1;
  405. break;
  406. case 2:
  407. g_proc_pid_maps_vsyscall = proc_pid_maps_vsyscall_2;
  408. g_proc_pid_smaps_vsyscall = proc_pid_smaps_vsyscall_2;
  409. break;
  410. default:
  411. abort();
  412. }
  413. g_protection_key_support = protection_key_support();
  414. pid_t pid = fork();
  415. if (pid == -1) {
  416. perror("fork");
  417. return EXIT_FAILURE;
  418. } else if (pid == 0) {
  419. rv = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
  420. if (rv != 0) {
  421. if (errno == EPERM) {
  422. fprintf(stderr,
  423. "Did you know? ptrace(PTRACE_TRACEME) doesn't work under strace.\n"
  424. );
  425. kill(getppid(), SIGTERM);
  426. return EXIT_FAILURE;
  427. }
  428. perror("ptrace PTRACE_TRACEME");
  429. return EXIT_FAILURE;
  430. }
  431. /*
  432. * Hide "segfault at ..." messages. Signal handler won't run.
  433. */
  434. struct sigaction act = {};
  435. act.sa_flags = SA_SIGINFO;
  436. act.sa_sigaction = sigaction_SIGSEGV;
  437. sigaction(SIGSEGV, &act, NULL);
  438. #ifdef __amd64__
  439. munmap(NULL, ((size_t)1 << 47) - 4096);
  440. #elif defined __i386__
  441. {
  442. size_t len;
  443. for (len = -4096;; len -= 4096) {
  444. munmap(NULL, len);
  445. }
  446. }
  447. #else
  448. #error "implement 'unmap everything'"
  449. #endif
  450. return EXIT_FAILURE;
  451. } else {
  452. /*
  453. * TODO find reliable way to signal parent that munmap(2) completed.
  454. * Child can't do it directly because it effectively doesn't exist
  455. * anymore. Looking at child's VM files isn't 100% reliable either:
  456. * due to a bug they may not become empty or empty-like.
  457. */
  458. sleep(1);
  459. if (rv == EXIT_SUCCESS) {
  460. rv = test_proc_pid_maps(pid);
  461. }
  462. if (rv == EXIT_SUCCESS) {
  463. rv = test_proc_pid_numa_maps(pid);
  464. }
  465. if (rv == EXIT_SUCCESS) {
  466. rv = test_proc_pid_smaps(pid);
  467. }
  468. if (rv == EXIT_SUCCESS) {
  469. rv = test_proc_pid_smaps_rollup(pid);
  470. }
  471. if (rv == EXIT_SUCCESS) {
  472. rv = test_proc_pid_statm(pid);
  473. }
  474. /* Cut the rope. */
  475. int wstatus;
  476. waitpid(pid, &wstatus, 0);
  477. assert(WIFSTOPPED(wstatus));
  478. assert(WSTOPSIG(wstatus) == SIGSEGV);
  479. }
  480. return rv;
  481. }
  482. #else
  483. int main(void)
  484. {
  485. return 4;
  486. }
  487. #endif