proc-pid-vm.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. /*
  2. * Copyright (c) 2019 Alexey Dobriyan <adobriyan@gmail.com>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. /*
  17. * Fork and exec tiny 1 page executable which precisely controls its VM.
  18. * Test /proc/$PID/maps
  19. * Test /proc/$PID/smaps
  20. * Test /proc/$PID/smaps_rollup
  21. * Test /proc/$PID/statm
  22. *
  23. * FIXME require CONFIG_TMPFS which can be disabled
  24. * FIXME test other values from "smaps"
  25. * FIXME support other archs
  26. */
  27. #undef NDEBUG
  28. #include <assert.h>
  29. #include <errno.h>
  30. #include <sched.h>
  31. #include <signal.h>
  32. #include <stdbool.h>
  33. #include <stdint.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <stdlib.h>
  37. #include <sys/mount.h>
  38. #include <sys/types.h>
  39. #include <sys/stat.h>
  40. #include <sys/wait.h>
  41. #include <fcntl.h>
  42. #include <unistd.h>
  43. #include <sys/syscall.h>
  44. #include <sys/uio.h>
  45. #include <linux/kdev_t.h>
  46. #include <sys/time.h>
  47. #include <sys/resource.h>
  48. #include <linux/fs.h>
  49. #include "../kselftest.h"
  50. static inline long sys_execveat(int dirfd, const char *pathname, char **argv, char **envp, int flags)
  51. {
  52. return syscall(SYS_execveat, dirfd, pathname, argv, envp, flags);
  53. }
  54. static void make_private_tmp(void)
  55. {
  56. if (unshare(CLONE_NEWNS) == -1) {
  57. if (errno == ENOSYS || errno == EPERM) {
  58. exit(4);
  59. }
  60. exit(1);
  61. }
  62. if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) == -1) {
  63. exit(1);
  64. }
  65. if (mount(NULL, "/tmp", "tmpfs", 0, NULL) == -1) {
  66. exit(1);
  67. }
  68. }
  69. static pid_t pid = -1;
  70. static void ate(void)
  71. {
  72. if (pid > 0) {
  73. kill(pid, SIGTERM);
  74. }
  75. }
  76. struct elf64_hdr {
  77. uint8_t e_ident[16];
  78. uint16_t e_type;
  79. uint16_t e_machine;
  80. uint32_t e_version;
  81. uint64_t e_entry;
  82. uint64_t e_phoff;
  83. uint64_t e_shoff;
  84. uint32_t e_flags;
  85. uint16_t e_ehsize;
  86. uint16_t e_phentsize;
  87. uint16_t e_phnum;
  88. uint16_t e_shentsize;
  89. uint16_t e_shnum;
  90. uint16_t e_shstrndx;
  91. };
  92. struct elf64_phdr {
  93. uint32_t p_type;
  94. uint32_t p_flags;
  95. uint64_t p_offset;
  96. uint64_t p_vaddr;
  97. uint64_t p_paddr;
  98. uint64_t p_filesz;
  99. uint64_t p_memsz;
  100. uint64_t p_align;
  101. };
  102. #ifdef __x86_64__
  103. #define PAGE_SIZE 4096
  104. #define VADDR (1UL << 32)
  105. #define MAPS_OFFSET 73
  106. #define syscall 0x0f, 0x05
  107. #define mov_rdi(x) \
  108. 0x48, 0xbf, \
  109. (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff, \
  110. ((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
  111. #define mov_rsi(x) \
  112. 0x48, 0xbe, \
  113. (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff, \
  114. ((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, ((x)>>56)&0xff
  115. #define mov_eax(x) \
  116. 0xb8, (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff
  117. static const uint8_t payload[] = {
  118. /* Casually unmap stack, vDSO and everything else. */
  119. /* munmap */
  120. mov_rdi(VADDR + 4096),
  121. mov_rsi((1ULL << 47) - 4096 - VADDR - 4096),
  122. mov_eax(11),
  123. syscall,
  124. /* Ping parent. */
  125. /* write(0, &c, 1); */
  126. 0x31, 0xff, /* xor edi, edi */
  127. 0x48, 0x8d, 0x35, 0x00, 0x00, 0x00, 0x00, /* lea rsi, [rip] */
  128. 0xba, 0x01, 0x00, 0x00, 0x00, /* mov edx, 1 */
  129. mov_eax(1),
  130. syscall,
  131. /* 1: pause(); */
  132. mov_eax(34),
  133. syscall,
  134. 0xeb, 0xf7, /* jmp 1b */
  135. };
  136. static int make_exe(const uint8_t *payload, size_t len)
  137. {
  138. struct elf64_hdr h;
  139. struct elf64_phdr ph;
  140. struct iovec iov[3] = {
  141. {&h, sizeof(struct elf64_hdr)},
  142. {&ph, sizeof(struct elf64_phdr)},
  143. {(void *)payload, len},
  144. };
  145. int fd, fd1;
  146. char buf[64];
  147. memset(&h, 0, sizeof(h));
  148. h.e_ident[0] = 0x7f;
  149. h.e_ident[1] = 'E';
  150. h.e_ident[2] = 'L';
  151. h.e_ident[3] = 'F';
  152. h.e_ident[4] = 2;
  153. h.e_ident[5] = 1;
  154. h.e_ident[6] = 1;
  155. h.e_ident[7] = 0;
  156. h.e_type = 2;
  157. h.e_machine = 0x3e;
  158. h.e_version = 1;
  159. h.e_entry = VADDR + sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr);
  160. h.e_phoff = sizeof(struct elf64_hdr);
  161. h.e_shoff = 0;
  162. h.e_flags = 0;
  163. h.e_ehsize = sizeof(struct elf64_hdr);
  164. h.e_phentsize = sizeof(struct elf64_phdr);
  165. h.e_phnum = 1;
  166. h.e_shentsize = 0;
  167. h.e_shnum = 0;
  168. h.e_shstrndx = 0;
  169. memset(&ph, 0, sizeof(ph));
  170. ph.p_type = 1;
  171. ph.p_flags = (1<<2)|1;
  172. ph.p_offset = 0;
  173. ph.p_vaddr = VADDR;
  174. ph.p_paddr = 0;
  175. ph.p_filesz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
  176. ph.p_memsz = sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len;
  177. ph.p_align = 4096;
  178. fd = openat(AT_FDCWD, "/tmp", O_WRONLY|O_EXCL|O_TMPFILE, 0700);
  179. if (fd == -1) {
  180. exit(1);
  181. }
  182. if (writev(fd, iov, 3) != sizeof(struct elf64_hdr) + sizeof(struct elf64_phdr) + len) {
  183. exit(1);
  184. }
  185. /* Avoid ETXTBSY on exec. */
  186. snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd);
  187. fd1 = open(buf, O_RDONLY|O_CLOEXEC);
  188. close(fd);
  189. return fd1;
  190. }
  191. #endif
  192. /*
  193. * 0: vsyscall VMA doesn't exist vsyscall=none
  194. * 1: vsyscall VMA is --xp vsyscall=xonly
  195. * 2: vsyscall VMA is r-xp vsyscall=emulate
  196. */
  197. static volatile int g_vsyscall;
  198. static const char *str_vsyscall;
  199. static const char str_vsyscall_0[] = "";
  200. static const char str_vsyscall_1[] =
  201. "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]\n";
  202. static const char str_vsyscall_2[] =
  203. "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]\n";
  204. #ifdef __x86_64__
  205. static void sigaction_SIGSEGV(int _, siginfo_t *__, void *___)
  206. {
  207. _exit(g_vsyscall);
  208. }
  209. /*
  210. * vsyscall page can't be unmapped, probe it directly.
  211. */
  212. static void vsyscall(void)
  213. {
  214. pid_t pid;
  215. int wstatus;
  216. pid = fork();
  217. if (pid < 0) {
  218. fprintf(stderr, "fork, errno %d\n", errno);
  219. exit(1);
  220. }
  221. if (pid == 0) {
  222. struct rlimit rlim = {0, 0};
  223. (void)setrlimit(RLIMIT_CORE, &rlim);
  224. /* Hide "segfault at ffffffffff600000" messages. */
  225. struct sigaction act;
  226. memset(&act, 0, sizeof(struct sigaction));
  227. act.sa_flags = SA_SIGINFO;
  228. act.sa_sigaction = sigaction_SIGSEGV;
  229. (void)sigaction(SIGSEGV, &act, NULL);
  230. g_vsyscall = 0;
  231. /* gettimeofday(NULL, NULL); */
  232. uint64_t rax = 0xffffffffff600000;
  233. asm volatile (
  234. "call *%[rax]"
  235. : [rax] "+a" (rax)
  236. : "D" (NULL), "S" (NULL)
  237. : "rcx", "r11"
  238. );
  239. g_vsyscall = 1;
  240. *(volatile int *)0xffffffffff600000UL;
  241. g_vsyscall = 2;
  242. exit(g_vsyscall);
  243. }
  244. waitpid(pid, &wstatus, 0);
  245. if (WIFEXITED(wstatus)) {
  246. g_vsyscall = WEXITSTATUS(wstatus);
  247. } else {
  248. fprintf(stderr, "error: wstatus %08x\n", wstatus);
  249. exit(1);
  250. }
  251. }
  252. int main(void)
  253. {
  254. int pipefd[2];
  255. int exec_fd;
  256. vsyscall();
  257. switch (g_vsyscall) {
  258. case 0:
  259. str_vsyscall = str_vsyscall_0;
  260. break;
  261. case 1:
  262. str_vsyscall = str_vsyscall_1;
  263. break;
  264. case 2:
  265. str_vsyscall = str_vsyscall_2;
  266. break;
  267. default:
  268. abort();
  269. }
  270. atexit(ate);
  271. make_private_tmp();
  272. /* Reserve fd 0 for 1-byte pipe ping from child. */
  273. close(0);
  274. if (open("/", O_RDONLY|O_DIRECTORY|O_PATH) != 0) {
  275. return 1;
  276. }
  277. exec_fd = make_exe(payload, sizeof(payload));
  278. if (pipe(pipefd) == -1) {
  279. return 1;
  280. }
  281. if (dup2(pipefd[1], 0) != 0) {
  282. return 1;
  283. }
  284. pid = fork();
  285. if (pid == -1) {
  286. return 1;
  287. }
  288. if (pid == 0) {
  289. sys_execveat(exec_fd, "", NULL, NULL, AT_EMPTY_PATH);
  290. return 1;
  291. }
  292. char _;
  293. if (read(pipefd[0], &_, 1) != 1) {
  294. return 1;
  295. }
  296. struct stat st;
  297. if (fstat(exec_fd, &st) == -1) {
  298. return 1;
  299. }
  300. /* Generate "head -n1 /proc/$PID/maps" */
  301. char buf0[256];
  302. memset(buf0, ' ', sizeof(buf0));
  303. int len = snprintf(buf0, sizeof(buf0),
  304. "%08lx-%08lx r-xp 00000000 %02lx:%02lx %llu",
  305. VADDR, VADDR + PAGE_SIZE,
  306. MAJOR(st.st_dev), MINOR(st.st_dev),
  307. (unsigned long long)st.st_ino);
  308. buf0[len] = ' ';
  309. snprintf(buf0 + MAPS_OFFSET, sizeof(buf0) - MAPS_OFFSET,
  310. "/tmp/#%llu (deleted)\n", (unsigned long long)st.st_ino);
  311. /* Test /proc/$PID/maps */
  312. {
  313. const size_t len = strlen(buf0) + strlen(str_vsyscall);
  314. char buf[256];
  315. ssize_t rv;
  316. int fd;
  317. snprintf(buf, sizeof(buf), "/proc/%u/maps", pid);
  318. fd = open(buf, O_RDONLY);
  319. if (fd == -1) {
  320. return 1;
  321. }
  322. rv = read(fd, buf, sizeof(buf));
  323. assert(rv == len);
  324. assert(memcmp(buf, buf0, strlen(buf0)) == 0);
  325. if (g_vsyscall > 0) {
  326. assert(memcmp(buf + strlen(buf0), str_vsyscall, strlen(str_vsyscall)) == 0);
  327. }
  328. }
  329. /* Test /proc/$PID/smaps */
  330. {
  331. char buf[4096];
  332. ssize_t rv;
  333. int fd;
  334. snprintf(buf, sizeof(buf), "/proc/%u/smaps", pid);
  335. fd = open(buf, O_RDONLY);
  336. if (fd == -1) {
  337. return 1;
  338. }
  339. rv = read(fd, buf, sizeof(buf));
  340. assert(0 <= rv && rv <= sizeof(buf));
  341. assert(rv >= strlen(buf0));
  342. assert(memcmp(buf, buf0, strlen(buf0)) == 0);
  343. #define RSS1 "Rss: 4 kB\n"
  344. #define RSS2 "Rss: 0 kB\n"
  345. #define PSS1 "Pss: 4 kB\n"
  346. #define PSS2 "Pss: 0 kB\n"
  347. assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
  348. memmem(buf, rv, RSS2, strlen(RSS2)));
  349. assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
  350. memmem(buf, rv, PSS2, strlen(PSS2)));
  351. static const char *S[] = {
  352. "Size: 4 kB\n",
  353. "KernelPageSize: 4 kB\n",
  354. "MMUPageSize: 4 kB\n",
  355. "Anonymous: 0 kB\n",
  356. "AnonHugePages: 0 kB\n",
  357. "Shared_Hugetlb: 0 kB\n",
  358. "Private_Hugetlb: 0 kB\n",
  359. "Locked: 0 kB\n",
  360. };
  361. int i;
  362. for (i = 0; i < ARRAY_SIZE(S); i++) {
  363. assert(memmem(buf, rv, S[i], strlen(S[i])));
  364. }
  365. if (g_vsyscall > 0) {
  366. assert(memmem(buf, rv, str_vsyscall, strlen(str_vsyscall)));
  367. }
  368. }
  369. /* Test /proc/$PID/smaps_rollup */
  370. {
  371. char bufr[256];
  372. memset(bufr, ' ', sizeof(bufr));
  373. len = snprintf(bufr, sizeof(bufr),
  374. "%08lx-%08lx ---p 00000000 00:00 0",
  375. VADDR, VADDR + PAGE_SIZE);
  376. bufr[len] = ' ';
  377. snprintf(bufr + MAPS_OFFSET, sizeof(bufr) - MAPS_OFFSET,
  378. "[rollup]\n");
  379. char buf[1024];
  380. ssize_t rv;
  381. int fd;
  382. snprintf(buf, sizeof(buf), "/proc/%u/smaps_rollup", pid);
  383. fd = open(buf, O_RDONLY);
  384. if (fd == -1) {
  385. return 1;
  386. }
  387. rv = read(fd, buf, sizeof(buf));
  388. assert(0 <= rv && rv <= sizeof(buf));
  389. assert(rv >= strlen(bufr));
  390. assert(memcmp(buf, bufr, strlen(bufr)) == 0);
  391. assert(memmem(buf, rv, RSS1, strlen(RSS1)) ||
  392. memmem(buf, rv, RSS2, strlen(RSS2)));
  393. assert(memmem(buf, rv, PSS1, strlen(PSS1)) ||
  394. memmem(buf, rv, PSS2, strlen(PSS2)));
  395. static const char *S[] = {
  396. "Anonymous: 0 kB\n",
  397. "AnonHugePages: 0 kB\n",
  398. "Shared_Hugetlb: 0 kB\n",
  399. "Private_Hugetlb: 0 kB\n",
  400. "Locked: 0 kB\n",
  401. };
  402. int i;
  403. for (i = 0; i < ARRAY_SIZE(S); i++) {
  404. assert(memmem(buf, rv, S[i], strlen(S[i])));
  405. }
  406. }
  407. /* Test /proc/$PID/statm */
  408. {
  409. char buf[64];
  410. ssize_t rv;
  411. int fd;
  412. snprintf(buf, sizeof(buf), "/proc/%u/statm", pid);
  413. fd = open(buf, O_RDONLY);
  414. if (fd == -1) {
  415. return 1;
  416. }
  417. rv = read(fd, buf, sizeof(buf));
  418. assert(rv == 7 * 2);
  419. assert(buf[0] == '1'); /* ->total_vm */
  420. assert(buf[1] == ' ');
  421. assert(buf[2] == '0' || buf[2] == '1'); /* rss */
  422. assert(buf[3] == ' ');
  423. assert(buf[4] == '0' || buf[2] == '1'); /* file rss */
  424. assert(buf[5] == ' ');
  425. assert(buf[6] == '1'); /* ELF executable segments */
  426. assert(buf[7] == ' ');
  427. assert(buf[8] == '0');
  428. assert(buf[9] == ' ');
  429. assert(buf[10] == '0'); /* ->data_vm + ->stack_vm */
  430. assert(buf[11] == ' ');
  431. assert(buf[12] == '0');
  432. assert(buf[13] == '\n');
  433. }
  434. /* Test PROCMAP_QUERY ioctl() for /proc/$PID/maps */
  435. {
  436. char path_buf[256], exp_path_buf[256];
  437. struct procmap_query q;
  438. int fd, err;
  439. snprintf(path_buf, sizeof(path_buf), "/proc/%u/maps", pid);
  440. fd = open(path_buf, O_RDONLY);
  441. if (fd == -1)
  442. return 1;
  443. /* CASE 1: exact MATCH at VADDR */
  444. memset(&q, 0, sizeof(q));
  445. q.size = sizeof(q);
  446. q.query_addr = VADDR;
  447. q.query_flags = 0;
  448. q.vma_name_addr = (__u64)(unsigned long)path_buf;
  449. q.vma_name_size = sizeof(path_buf);
  450. err = ioctl(fd, PROCMAP_QUERY, &q);
  451. assert(err == 0);
  452. assert(q.query_addr == VADDR);
  453. assert(q.query_flags == 0);
  454. assert(q.vma_flags == (PROCMAP_QUERY_VMA_READABLE | PROCMAP_QUERY_VMA_EXECUTABLE));
  455. assert(q.vma_start == VADDR);
  456. assert(q.vma_end == VADDR + PAGE_SIZE);
  457. assert(q.vma_page_size == PAGE_SIZE);
  458. assert(q.vma_offset == 0);
  459. assert(q.inode == st.st_ino);
  460. assert(q.dev_major == MAJOR(st.st_dev));
  461. assert(q.dev_minor == MINOR(st.st_dev));
  462. snprintf(exp_path_buf, sizeof(exp_path_buf),
  463. "/tmp/#%llu (deleted)", (unsigned long long)st.st_ino);
  464. assert(q.vma_name_size == strlen(exp_path_buf) + 1);
  465. assert(strcmp(path_buf, exp_path_buf) == 0);
  466. /* CASE 2: NO MATCH at VADDR-1 */
  467. memset(&q, 0, sizeof(q));
  468. q.size = sizeof(q);
  469. q.query_addr = VADDR - 1;
  470. q.query_flags = 0; /* exact match */
  471. err = ioctl(fd, PROCMAP_QUERY, &q);
  472. err = err < 0 ? -errno : 0;
  473. assert(err == -ENOENT);
  474. /* CASE 3: MATCH COVERING_OR_NEXT_VMA at VADDR - 1 */
  475. memset(&q, 0, sizeof(q));
  476. q.size = sizeof(q);
  477. q.query_addr = VADDR - 1;
  478. q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
  479. err = ioctl(fd, PROCMAP_QUERY, &q);
  480. assert(err == 0);
  481. assert(q.query_addr == VADDR - 1);
  482. assert(q.query_flags == PROCMAP_QUERY_COVERING_OR_NEXT_VMA);
  483. assert(q.vma_start == VADDR);
  484. assert(q.vma_end == VADDR + PAGE_SIZE);
  485. /* CASE 4: NO MATCH at VADDR + PAGE_SIZE */
  486. memset(&q, 0, sizeof(q));
  487. q.size = sizeof(q);
  488. q.query_addr = VADDR + PAGE_SIZE; /* point right after the VMA */
  489. q.query_flags = PROCMAP_QUERY_COVERING_OR_NEXT_VMA;
  490. err = ioctl(fd, PROCMAP_QUERY, &q);
  491. err = err < 0 ? -errno : 0;
  492. assert(err == -ENOENT);
  493. /* CASE 5: NO MATCH WRITABLE at VADDR */
  494. memset(&q, 0, sizeof(q));
  495. q.size = sizeof(q);
  496. q.query_addr = VADDR;
  497. q.query_flags = PROCMAP_QUERY_VMA_WRITABLE;
  498. err = ioctl(fd, PROCMAP_QUERY, &q);
  499. err = err < 0 ? -errno : 0;
  500. assert(err == -ENOENT);
  501. }
  502. return 0;
  503. }
  504. #else
  505. int main(void)
  506. {
  507. return 4;
  508. }
  509. #endif