mlock2-tests.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. #define _GNU_SOURCE
  2. #include <sys/mman.h>
  3. #include <stdint.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #include <sys/time.h>
  7. #include <sys/resource.h>
  8. #include <stdbool.h>
  9. #include "mlock2.h"
  10. struct vm_boundaries {
  11. unsigned long start;
  12. unsigned long end;
  13. };
  14. static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
  15. {
  16. FILE *file;
  17. int ret = 1;
  18. char line[1024] = {0};
  19. char *end_addr;
  20. char *stop;
  21. unsigned long start;
  22. unsigned long end;
  23. if (!area)
  24. return ret;
  25. file = fopen("/proc/self/maps", "r");
  26. if (!file) {
  27. perror("fopen");
  28. return ret;
  29. }
  30. memset(area, 0, sizeof(struct vm_boundaries));
  31. while(fgets(line, 1024, file)) {
  32. end_addr = strchr(line, '-');
  33. if (!end_addr) {
  34. printf("cannot parse /proc/self/maps\n");
  35. goto out;
  36. }
  37. *end_addr = '\0';
  38. end_addr++;
  39. stop = strchr(end_addr, ' ');
  40. if (!stop) {
  41. printf("cannot parse /proc/self/maps\n");
  42. goto out;
  43. }
  44. stop = '\0';
  45. sscanf(line, "%lx", &start);
  46. sscanf(end_addr, "%lx", &end);
  47. if (start <= addr && end > addr) {
  48. area->start = start;
  49. area->end = end;
  50. ret = 0;
  51. goto out;
  52. }
  53. }
  54. out:
  55. fclose(file);
  56. return ret;
  57. }
  58. static uint64_t get_pageflags(unsigned long addr)
  59. {
  60. FILE *file;
  61. uint64_t pfn;
  62. unsigned long offset;
  63. file = fopen("/proc/self/pagemap", "r");
  64. if (!file) {
  65. perror("fopen pagemap");
  66. _exit(1);
  67. }
  68. offset = addr / getpagesize() * sizeof(pfn);
  69. if (fseek(file, offset, SEEK_SET)) {
  70. perror("fseek pagemap");
  71. _exit(1);
  72. }
  73. if (fread(&pfn, sizeof(pfn), 1, file) != 1) {
  74. perror("fread pagemap");
  75. _exit(1);
  76. }
  77. fclose(file);
  78. return pfn;
  79. }
  80. static uint64_t get_kpageflags(unsigned long pfn)
  81. {
  82. uint64_t flags;
  83. FILE *file;
  84. file = fopen("/proc/kpageflags", "r");
  85. if (!file) {
  86. perror("fopen kpageflags");
  87. _exit(1);
  88. }
  89. if (fseek(file, pfn * sizeof(flags), SEEK_SET)) {
  90. perror("fseek kpageflags");
  91. _exit(1);
  92. }
  93. if (fread(&flags, sizeof(flags), 1, file) != 1) {
  94. perror("fread kpageflags");
  95. _exit(1);
  96. }
  97. fclose(file);
  98. return flags;
  99. }
  100. #define VMFLAGS "VmFlags:"
  101. static bool is_vmflag_set(unsigned long addr, const char *vmflag)
  102. {
  103. char *line = NULL;
  104. char *flags;
  105. size_t size = 0;
  106. bool ret = false;
  107. FILE *smaps;
  108. smaps = seek_to_smaps_entry(addr);
  109. if (!smaps) {
  110. printf("Unable to parse /proc/self/smaps\n");
  111. goto out;
  112. }
  113. while (getline(&line, &size, smaps) > 0) {
  114. if (!strstr(line, VMFLAGS)) {
  115. free(line);
  116. line = NULL;
  117. size = 0;
  118. continue;
  119. }
  120. flags = line + strlen(VMFLAGS);
  121. ret = (strstr(flags, vmflag) != NULL);
  122. goto out;
  123. }
  124. out:
  125. free(line);
  126. fclose(smaps);
  127. return ret;
  128. }
  129. #define SIZE "Size:"
  130. #define RSS "Rss:"
  131. #define LOCKED "lo"
  132. static bool is_vma_lock_on_fault(unsigned long addr)
  133. {
  134. bool ret = false;
  135. bool locked;
  136. FILE *smaps = NULL;
  137. unsigned long vma_size, vma_rss;
  138. char *line = NULL;
  139. char *value;
  140. size_t size = 0;
  141. locked = is_vmflag_set(addr, LOCKED);
  142. if (!locked)
  143. goto out;
  144. smaps = seek_to_smaps_entry(addr);
  145. if (!smaps) {
  146. printf("Unable to parse /proc/self/smaps\n");
  147. goto out;
  148. }
  149. while (getline(&line, &size, smaps) > 0) {
  150. if (!strstr(line, SIZE)) {
  151. free(line);
  152. line = NULL;
  153. size = 0;
  154. continue;
  155. }
  156. value = line + strlen(SIZE);
  157. if (sscanf(value, "%lu kB", &vma_size) < 1) {
  158. printf("Unable to parse smaps entry for Size\n");
  159. goto out;
  160. }
  161. break;
  162. }
  163. while (getline(&line, &size, smaps) > 0) {
  164. if (!strstr(line, RSS)) {
  165. free(line);
  166. line = NULL;
  167. size = 0;
  168. continue;
  169. }
  170. value = line + strlen(RSS);
  171. if (sscanf(value, "%lu kB", &vma_rss) < 1) {
  172. printf("Unable to parse smaps entry for Rss\n");
  173. goto out;
  174. }
  175. break;
  176. }
  177. ret = locked && (vma_rss < vma_size);
  178. out:
  179. free(line);
  180. if (smaps)
  181. fclose(smaps);
  182. return ret;
  183. }
  184. #define PRESENT_BIT 0x8000000000000000ULL
  185. #define PFN_MASK 0x007FFFFFFFFFFFFFULL
  186. #define UNEVICTABLE_BIT (1UL << 18)
  187. static int lock_check(char *map)
  188. {
  189. unsigned long page_size = getpagesize();
  190. uint64_t page1_flags, page2_flags;
  191. page1_flags = get_pageflags((unsigned long)map);
  192. page2_flags = get_pageflags((unsigned long)map + page_size);
  193. /* Both pages should be present */
  194. if (((page1_flags & PRESENT_BIT) == 0) ||
  195. ((page2_flags & PRESENT_BIT) == 0)) {
  196. printf("Failed to make both pages present\n");
  197. return 1;
  198. }
  199. page1_flags = get_kpageflags(page1_flags & PFN_MASK);
  200. page2_flags = get_kpageflags(page2_flags & PFN_MASK);
  201. /* Both pages should be unevictable */
  202. if (((page1_flags & UNEVICTABLE_BIT) == 0) ||
  203. ((page2_flags & UNEVICTABLE_BIT) == 0)) {
  204. printf("Failed to make both pages unevictable\n");
  205. return 1;
  206. }
  207. if (!is_vmflag_set((unsigned long)map, LOCKED)) {
  208. printf("VMA flag %s is missing on page 1\n", LOCKED);
  209. return 1;
  210. }
  211. if (!is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
  212. printf("VMA flag %s is missing on page 2\n", LOCKED);
  213. return 1;
  214. }
  215. return 0;
  216. }
  217. static int unlock_lock_check(char *map)
  218. {
  219. unsigned long page_size = getpagesize();
  220. uint64_t page1_flags, page2_flags;
  221. page1_flags = get_pageflags((unsigned long)map);
  222. page2_flags = get_pageflags((unsigned long)map + page_size);
  223. page1_flags = get_kpageflags(page1_flags & PFN_MASK);
  224. page2_flags = get_kpageflags(page2_flags & PFN_MASK);
  225. if ((page1_flags & UNEVICTABLE_BIT) || (page2_flags & UNEVICTABLE_BIT)) {
  226. printf("A page is still marked unevictable after unlock\n");
  227. return 1;
  228. }
  229. if (is_vmflag_set((unsigned long)map, LOCKED)) {
  230. printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
  231. return 1;
  232. }
  233. if (is_vmflag_set((unsigned long)map + page_size, LOCKED)) {
  234. printf("VMA flag %s is present on page 2 after unlock\n", LOCKED);
  235. return 1;
  236. }
  237. return 0;
  238. }
  239. static int test_mlock_lock()
  240. {
  241. char *map;
  242. int ret = 1;
  243. unsigned long page_size = getpagesize();
  244. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  245. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  246. if (map == MAP_FAILED) {
  247. perror("test_mlock_locked mmap");
  248. goto out;
  249. }
  250. if (mlock2_(map, 2 * page_size, 0)) {
  251. if (errno == ENOSYS) {
  252. printf("Cannot call new mlock family, skipping test\n");
  253. _exit(0);
  254. }
  255. perror("mlock2(0)");
  256. goto unmap;
  257. }
  258. if (lock_check(map))
  259. goto unmap;
  260. /* Now unlock and recheck attributes */
  261. if (munlock(map, 2 * page_size)) {
  262. perror("munlock()");
  263. goto unmap;
  264. }
  265. ret = unlock_lock_check(map);
  266. unmap:
  267. munmap(map, 2 * page_size);
  268. out:
  269. return ret;
  270. }
  271. static int onfault_check(char *map)
  272. {
  273. unsigned long page_size = getpagesize();
  274. uint64_t page1_flags, page2_flags;
  275. page1_flags = get_pageflags((unsigned long)map);
  276. page2_flags = get_pageflags((unsigned long)map + page_size);
  277. /* Neither page should be present */
  278. if ((page1_flags & PRESENT_BIT) || (page2_flags & PRESENT_BIT)) {
  279. printf("Pages were made present by MLOCK_ONFAULT\n");
  280. return 1;
  281. }
  282. *map = 'a';
  283. page1_flags = get_pageflags((unsigned long)map);
  284. page2_flags = get_pageflags((unsigned long)map + page_size);
  285. /* Only page 1 should be present */
  286. if ((page1_flags & PRESENT_BIT) == 0) {
  287. printf("Page 1 is not present after fault\n");
  288. return 1;
  289. } else if (page2_flags & PRESENT_BIT) {
  290. printf("Page 2 was made present\n");
  291. return 1;
  292. }
  293. page1_flags = get_kpageflags(page1_flags & PFN_MASK);
  294. /* Page 1 should be unevictable */
  295. if ((page1_flags & UNEVICTABLE_BIT) == 0) {
  296. printf("Failed to make faulted page unevictable\n");
  297. return 1;
  298. }
  299. if (!is_vma_lock_on_fault((unsigned long)map)) {
  300. printf("VMA is not marked for lock on fault\n");
  301. return 1;
  302. }
  303. if (!is_vma_lock_on_fault((unsigned long)map + page_size)) {
  304. printf("VMA is not marked for lock on fault\n");
  305. return 1;
  306. }
  307. return 0;
  308. }
  309. static int unlock_onfault_check(char *map)
  310. {
  311. unsigned long page_size = getpagesize();
  312. uint64_t page1_flags;
  313. page1_flags = get_pageflags((unsigned long)map);
  314. page1_flags = get_kpageflags(page1_flags & PFN_MASK);
  315. if (page1_flags & UNEVICTABLE_BIT) {
  316. printf("Page 1 is still marked unevictable after unlock\n");
  317. return 1;
  318. }
  319. if (is_vma_lock_on_fault((unsigned long)map) ||
  320. is_vma_lock_on_fault((unsigned long)map + page_size)) {
  321. printf("VMA is still lock on fault after unlock\n");
  322. return 1;
  323. }
  324. return 0;
  325. }
  326. static int test_mlock_onfault()
  327. {
  328. char *map;
  329. int ret = 1;
  330. unsigned long page_size = getpagesize();
  331. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  332. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  333. if (map == MAP_FAILED) {
  334. perror("test_mlock_locked mmap");
  335. goto out;
  336. }
  337. if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
  338. if (errno == ENOSYS) {
  339. printf("Cannot call new mlock family, skipping test\n");
  340. _exit(0);
  341. }
  342. perror("mlock2(MLOCK_ONFAULT)");
  343. goto unmap;
  344. }
  345. if (onfault_check(map))
  346. goto unmap;
  347. /* Now unlock and recheck attributes */
  348. if (munlock(map, 2 * page_size)) {
  349. if (errno == ENOSYS) {
  350. printf("Cannot call new mlock family, skipping test\n");
  351. _exit(0);
  352. }
  353. perror("munlock()");
  354. goto unmap;
  355. }
  356. ret = unlock_onfault_check(map);
  357. unmap:
  358. munmap(map, 2 * page_size);
  359. out:
  360. return ret;
  361. }
  362. static int test_lock_onfault_of_present()
  363. {
  364. char *map;
  365. int ret = 1;
  366. unsigned long page_size = getpagesize();
  367. uint64_t page1_flags, page2_flags;
  368. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  369. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  370. if (map == MAP_FAILED) {
  371. perror("test_mlock_locked mmap");
  372. goto out;
  373. }
  374. *map = 'a';
  375. if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
  376. if (errno == ENOSYS) {
  377. printf("Cannot call new mlock family, skipping test\n");
  378. _exit(0);
  379. }
  380. perror("mlock2(MLOCK_ONFAULT)");
  381. goto unmap;
  382. }
  383. page1_flags = get_pageflags((unsigned long)map);
  384. page2_flags = get_pageflags((unsigned long)map + page_size);
  385. page1_flags = get_kpageflags(page1_flags & PFN_MASK);
  386. page2_flags = get_kpageflags(page2_flags & PFN_MASK);
  387. /* Page 1 should be unevictable */
  388. if ((page1_flags & UNEVICTABLE_BIT) == 0) {
  389. printf("Failed to make present page unevictable\n");
  390. goto unmap;
  391. }
  392. if (!is_vma_lock_on_fault((unsigned long)map) ||
  393. !is_vma_lock_on_fault((unsigned long)map + page_size)) {
  394. printf("VMA with present pages is not marked lock on fault\n");
  395. goto unmap;
  396. }
  397. ret = 0;
  398. unmap:
  399. munmap(map, 2 * page_size);
  400. out:
  401. return ret;
  402. }
  403. static int test_munlockall()
  404. {
  405. char *map;
  406. int ret = 1;
  407. unsigned long page_size = getpagesize();
  408. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  409. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  410. if (map == MAP_FAILED) {
  411. perror("test_munlockall mmap");
  412. goto out;
  413. }
  414. if (mlockall(MCL_CURRENT)) {
  415. perror("mlockall(MCL_CURRENT)");
  416. goto out;
  417. }
  418. if (lock_check(map))
  419. goto unmap;
  420. if (munlockall()) {
  421. perror("munlockall()");
  422. goto unmap;
  423. }
  424. if (unlock_lock_check(map))
  425. goto unmap;
  426. munmap(map, 2 * page_size);
  427. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  428. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  429. if (map == MAP_FAILED) {
  430. perror("test_munlockall second mmap");
  431. goto out;
  432. }
  433. if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
  434. perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
  435. goto unmap;
  436. }
  437. if (onfault_check(map))
  438. goto unmap;
  439. if (munlockall()) {
  440. perror("munlockall()");
  441. goto unmap;
  442. }
  443. if (unlock_onfault_check(map))
  444. goto unmap;
  445. if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
  446. perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
  447. goto out;
  448. }
  449. if (lock_check(map))
  450. goto unmap;
  451. if (munlockall()) {
  452. perror("munlockall()");
  453. goto unmap;
  454. }
  455. ret = unlock_lock_check(map);
  456. unmap:
  457. munmap(map, 2 * page_size);
  458. out:
  459. munlockall();
  460. return ret;
  461. }
  462. static int test_vma_management(bool call_mlock)
  463. {
  464. int ret = 1;
  465. void *map;
  466. unsigned long page_size = getpagesize();
  467. struct vm_boundaries page1;
  468. struct vm_boundaries page2;
  469. struct vm_boundaries page3;
  470. map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
  471. MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
  472. if (map == MAP_FAILED) {
  473. perror("mmap()");
  474. return ret;
  475. }
  476. if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
  477. if (errno == ENOSYS) {
  478. printf("Cannot call new mlock family, skipping test\n");
  479. _exit(0);
  480. }
  481. perror("mlock(ONFAULT)\n");
  482. goto out;
  483. }
  484. if (get_vm_area((unsigned long)map, &page1) ||
  485. get_vm_area((unsigned long)map + page_size, &page2) ||
  486. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  487. printf("couldn't find mapping in /proc/self/maps\n");
  488. goto out;
  489. }
  490. /*
  491. * Before we unlock a portion, we need to that all three pages are in
  492. * the same VMA. If they are not we abort this test (Note that this is
  493. * not a failure)
  494. */
  495. if (page1.start != page2.start || page2.start != page3.start) {
  496. printf("VMAs are not merged to start, aborting test\n");
  497. ret = 0;
  498. goto out;
  499. }
  500. if (munlock(map + page_size, page_size)) {
  501. perror("munlock()");
  502. goto out;
  503. }
  504. if (get_vm_area((unsigned long)map, &page1) ||
  505. get_vm_area((unsigned long)map + page_size, &page2) ||
  506. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  507. printf("couldn't find mapping in /proc/self/maps\n");
  508. goto out;
  509. }
  510. /* All three VMAs should be different */
  511. if (page1.start == page2.start || page2.start == page3.start) {
  512. printf("failed to split VMA for munlock\n");
  513. goto out;
  514. }
  515. /* Now unlock the first and third page and check the VMAs again */
  516. if (munlock(map, page_size * 3)) {
  517. perror("munlock()");
  518. goto out;
  519. }
  520. if (get_vm_area((unsigned long)map, &page1) ||
  521. get_vm_area((unsigned long)map + page_size, &page2) ||
  522. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  523. printf("couldn't find mapping in /proc/self/maps\n");
  524. goto out;
  525. }
  526. /* Now all three VMAs should be the same */
  527. if (page1.start != page2.start || page2.start != page3.start) {
  528. printf("failed to merge VMAs after munlock\n");
  529. goto out;
  530. }
  531. ret = 0;
  532. out:
  533. munmap(map, 3 * page_size);
  534. return ret;
  535. }
  536. static int test_mlockall(int (test_function)(bool call_mlock))
  537. {
  538. int ret = 1;
  539. if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
  540. perror("mlockall");
  541. return ret;
  542. }
  543. ret = test_function(false);
  544. munlockall();
  545. return ret;
  546. }
  547. int main(int argc, char **argv)
  548. {
  549. int ret = 0;
  550. ret += test_mlock_lock();
  551. ret += test_mlock_onfault();
  552. ret += test_munlockall();
  553. ret += test_lock_onfault_of_present();
  554. ret += test_vma_management(true);
  555. ret += test_mlockall(test_vma_management);
  556. return ret;
  557. }