mlock2-tests.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // SPDX-License-Identifier: GPL-2.0
  2. #define _GNU_SOURCE
  3. #include <sys/mman.h>
  4. #include <stdint.h>
  5. #include <unistd.h>
  6. #include <string.h>
  7. #include <sys/time.h>
  8. #include <sys/resource.h>
  9. #include <stdbool.h>
  10. #include "kselftest.h"
  11. #include "mlock2.h"
  12. struct vm_boundaries {
  13. unsigned long start;
  14. unsigned long end;
  15. };
  16. static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
  17. {
  18. FILE *file;
  19. int ret = 1;
  20. char line[1024] = {0};
  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. if (sscanf(line, "%lx-%lx", &start, &end) != 2) {
  33. ksft_print_msg("cannot parse /proc/self/maps\n");
  34. goto out;
  35. }
  36. if (start <= addr && end > addr) {
  37. area->start = start;
  38. area->end = end;
  39. ret = 0;
  40. goto out;
  41. }
  42. }
  43. out:
  44. fclose(file);
  45. return ret;
  46. }
  47. #define VMFLAGS "VmFlags:"
  48. static bool is_vmflag_set(unsigned long addr, const char *vmflag)
  49. {
  50. char *line = NULL;
  51. char *flags;
  52. size_t size = 0;
  53. bool ret = false;
  54. FILE *smaps;
  55. smaps = seek_to_smaps_entry(addr);
  56. if (!smaps) {
  57. ksft_print_msg("Unable to parse /proc/self/smaps\n");
  58. goto out;
  59. }
  60. while (getline(&line, &size, smaps) > 0) {
  61. if (!strstr(line, VMFLAGS)) {
  62. free(line);
  63. line = NULL;
  64. size = 0;
  65. continue;
  66. }
  67. flags = line + strlen(VMFLAGS);
  68. ret = (strstr(flags, vmflag) != NULL);
  69. goto out;
  70. }
  71. out:
  72. free(line);
  73. fclose(smaps);
  74. return ret;
  75. }
  76. #define SIZE "Size:"
  77. #define RSS "Rss:"
  78. #define LOCKED "lo"
  79. static unsigned long get_value_for_name(unsigned long addr, const char *name)
  80. {
  81. char *line = NULL;
  82. size_t size = 0;
  83. char *value_ptr;
  84. FILE *smaps = NULL;
  85. unsigned long value = -1UL;
  86. smaps = seek_to_smaps_entry(addr);
  87. if (!smaps) {
  88. ksft_print_msg("Unable to parse /proc/self/smaps\n");
  89. goto out;
  90. }
  91. while (getline(&line, &size, smaps) > 0) {
  92. if (!strstr(line, name)) {
  93. free(line);
  94. line = NULL;
  95. size = 0;
  96. continue;
  97. }
  98. value_ptr = line + strlen(name);
  99. if (sscanf(value_ptr, "%lu kB", &value) < 1) {
  100. ksft_print_msg("Unable to parse smaps entry for Size\n");
  101. goto out;
  102. }
  103. break;
  104. }
  105. out:
  106. if (smaps)
  107. fclose(smaps);
  108. free(line);
  109. return value;
  110. }
  111. static bool is_vma_lock_on_fault(unsigned long addr)
  112. {
  113. bool locked;
  114. unsigned long vma_size, vma_rss;
  115. locked = is_vmflag_set(addr, LOCKED);
  116. if (!locked)
  117. return false;
  118. vma_size = get_value_for_name(addr, SIZE);
  119. vma_rss = get_value_for_name(addr, RSS);
  120. /* only one page is faulted in */
  121. return (vma_rss < vma_size);
  122. }
  123. #define PRESENT_BIT 0x8000000000000000ULL
  124. #define PFN_MASK 0x007FFFFFFFFFFFFFULL
  125. #define UNEVICTABLE_BIT (1UL << 18)
  126. static int lock_check(unsigned long addr)
  127. {
  128. bool locked;
  129. unsigned long vma_size, vma_rss;
  130. locked = is_vmflag_set(addr, LOCKED);
  131. if (!locked)
  132. return false;
  133. vma_size = get_value_for_name(addr, SIZE);
  134. vma_rss = get_value_for_name(addr, RSS);
  135. return (vma_rss == vma_size);
  136. }
  137. static int unlock_lock_check(char *map)
  138. {
  139. if (is_vmflag_set((unsigned long)map, LOCKED)) {
  140. ksft_print_msg("VMA flag %s is present on page 1 after unlock\n", LOCKED);
  141. return 1;
  142. }
  143. return 0;
  144. }
  145. static void test_mlock_lock(void)
  146. {
  147. char *map;
  148. unsigned long page_size = getpagesize();
  149. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  150. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  151. if (map == MAP_FAILED)
  152. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  153. if (mlock2_(map, 2 * page_size, 0)) {
  154. munmap(map, 2 * page_size);
  155. ksft_exit_fail_msg("mlock2(0): %s\n", strerror(errno));
  156. }
  157. ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
  158. /* Now unlock and recheck attributes */
  159. if (munlock(map, 2 * page_size)) {
  160. munmap(map, 2 * page_size);
  161. ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
  162. }
  163. ksft_test_result(!unlock_lock_check(map), "%s: Unlocked\n", __func__);
  164. munmap(map, 2 * page_size);
  165. }
  166. static int onfault_check(char *map)
  167. {
  168. *map = 'a';
  169. if (!is_vma_lock_on_fault((unsigned long)map)) {
  170. ksft_print_msg("VMA is not marked for lock on fault\n");
  171. return 1;
  172. }
  173. return 0;
  174. }
  175. static int unlock_onfault_check(char *map)
  176. {
  177. unsigned long page_size = getpagesize();
  178. if (is_vma_lock_on_fault((unsigned long)map) ||
  179. is_vma_lock_on_fault((unsigned long)map + page_size)) {
  180. ksft_print_msg("VMA is still lock on fault after unlock\n");
  181. return 1;
  182. }
  183. return 0;
  184. }
  185. static void test_mlock_onfault(void)
  186. {
  187. char *map;
  188. unsigned long page_size = getpagesize();
  189. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  190. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  191. if (map == MAP_FAILED)
  192. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  193. if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
  194. munmap(map, 2 * page_size);
  195. ksft_exit_fail_msg("mlock2(MLOCK_ONFAULT): %s\n", strerror(errno));
  196. }
  197. ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
  198. /* Now unlock and recheck attributes */
  199. if (munlock(map, 2 * page_size)) {
  200. munmap(map, 2 * page_size);
  201. ksft_exit_fail_msg("munlock(): %s\n", strerror(errno));
  202. }
  203. ksft_test_result(!unlock_onfault_check(map), "VMA open lock after fault\n");
  204. munmap(map, 2 * page_size);
  205. }
  206. static void test_lock_onfault_of_present(void)
  207. {
  208. char *map;
  209. unsigned long page_size = getpagesize();
  210. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  211. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  212. if (map == MAP_FAILED)
  213. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  214. *map = 'a';
  215. if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
  216. munmap(map, 2 * page_size);
  217. ksft_test_result_fail("mlock2(MLOCK_ONFAULT) error: %s", strerror(errno));
  218. }
  219. ksft_test_result(is_vma_lock_on_fault((unsigned long)map) ||
  220. is_vma_lock_on_fault((unsigned long)map + page_size),
  221. "VMA with present pages is not marked lock on fault\n");
  222. munmap(map, 2 * page_size);
  223. }
  224. static void test_munlockall0(void)
  225. {
  226. char *map;
  227. unsigned long page_size = getpagesize();
  228. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  229. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  230. if (map == MAP_FAILED)
  231. ksft_exit_fail_msg("mmap error: %s\n", strerror(errno));
  232. if (mlockall(MCL_CURRENT)) {
  233. munmap(map, 2 * page_size);
  234. ksft_exit_fail_msg("mlockall(MCL_CURRENT): %s\n", strerror(errno));
  235. }
  236. ksft_test_result(lock_check((unsigned long)map), "%s: Locked memory area\n", __func__);
  237. if (munlockall()) {
  238. munmap(map, 2 * page_size);
  239. ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
  240. }
  241. ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
  242. munmap(map, 2 * page_size);
  243. }
  244. static void test_munlockall1(void)
  245. {
  246. char *map;
  247. unsigned long page_size = getpagesize();
  248. map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
  249. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  250. if (map == MAP_FAILED)
  251. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  252. if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
  253. munmap(map, 2 * page_size);
  254. ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_ONFAULT): %s\n", strerror(errno));
  255. }
  256. ksft_test_result(!onfault_check(map), "%s: VMA marked for lock on fault\n", __func__);
  257. if (munlockall()) {
  258. munmap(map, 2 * page_size);
  259. ksft_exit_fail_msg("munlockall(): %s\n", strerror(errno));
  260. }
  261. ksft_test_result(!unlock_onfault_check(map), "%s: Unlocked\n", __func__);
  262. if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
  263. munmap(map, 2 * page_size);
  264. ksft_exit_fail_msg("mlockall(MCL_CURRENT | MCL_FUTURE): %s\n", strerror(errno));
  265. }
  266. ksft_test_result(lock_check((unsigned long)map), "%s: Locked\n", __func__);
  267. if (munlockall()) {
  268. munmap(map, 2 * page_size);
  269. ksft_exit_fail_msg("munlockall() %s\n", strerror(errno));
  270. }
  271. ksft_test_result(!unlock_lock_check(map), "%s: No locked memory\n", __func__);
  272. munmap(map, 2 * page_size);
  273. }
  274. static void test_vma_management(bool call_mlock)
  275. {
  276. void *map;
  277. unsigned long page_size = getpagesize();
  278. struct vm_boundaries page1;
  279. struct vm_boundaries page2;
  280. struct vm_boundaries page3;
  281. map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
  282. MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  283. if (map == MAP_FAILED)
  284. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  285. if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
  286. munmap(map, 3 * page_size);
  287. ksft_test_result_fail("mlock error: %s", strerror(errno));
  288. }
  289. if (get_vm_area((unsigned long)map, &page1) ||
  290. get_vm_area((unsigned long)map + page_size, &page2) ||
  291. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  292. munmap(map, 3 * page_size);
  293. ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
  294. }
  295. /*
  296. * Before we unlock a portion, we need to that all three pages are in
  297. * the same VMA. If they are not we abort this test (Note that this is
  298. * not a failure)
  299. */
  300. if (page1.start != page2.start || page2.start != page3.start) {
  301. munmap(map, 3 * page_size);
  302. ksft_test_result_fail("VMAs are not merged to start, aborting test");
  303. }
  304. if (munlock(map + page_size, page_size)) {
  305. munmap(map, 3 * page_size);
  306. ksft_test_result_fail("munlock(): %s", strerror(errno));
  307. }
  308. if (get_vm_area((unsigned long)map, &page1) ||
  309. get_vm_area((unsigned long)map + page_size, &page2) ||
  310. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  311. munmap(map, 3 * page_size);
  312. ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
  313. }
  314. /* All three VMAs should be different */
  315. if (page1.start == page2.start || page2.start == page3.start) {
  316. munmap(map, 3 * page_size);
  317. ksft_test_result_fail("failed to split VMA for munlock");
  318. }
  319. /* Now unlock the first and third page and check the VMAs again */
  320. if (munlock(map, page_size * 3)) {
  321. munmap(map, 3 * page_size);
  322. ksft_test_result_fail("munlock(): %s", strerror(errno));
  323. }
  324. if (get_vm_area((unsigned long)map, &page1) ||
  325. get_vm_area((unsigned long)map + page_size, &page2) ||
  326. get_vm_area((unsigned long)map + page_size * 2, &page3)) {
  327. munmap(map, 3 * page_size);
  328. ksft_test_result_fail("couldn't find mapping in /proc/self/maps");
  329. }
  330. /* Now all three VMAs should be the same */
  331. if (page1.start != page2.start || page2.start != page3.start) {
  332. munmap(map, 3 * page_size);
  333. ksft_test_result_fail("failed to merge VMAs after munlock");
  334. }
  335. ksft_test_result_pass("%s call_mlock %d\n", __func__, call_mlock);
  336. munmap(map, 3 * page_size);
  337. }
  338. static void test_mlockall(void)
  339. {
  340. if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE))
  341. ksft_exit_fail_msg("mlockall failed: %s\n", strerror(errno));
  342. test_vma_management(false);
  343. munlockall();
  344. }
  345. int main(int argc, char **argv)
  346. {
  347. int ret, size = 3 * getpagesize();
  348. void *map;
  349. ksft_print_header();
  350. map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  351. if (map == MAP_FAILED)
  352. ksft_exit_fail_msg("mmap error: %s", strerror(errno));
  353. ret = mlock2_(map, size, MLOCK_ONFAULT);
  354. if (ret && errno == ENOSYS)
  355. ksft_finished();
  356. munmap(map, size);
  357. ksft_set_plan(13);
  358. test_mlock_lock();
  359. test_mlock_onfault();
  360. test_munlockall0();
  361. test_munlockall1();
  362. test_lock_onfault_of_present();
  363. test_vma_management(true);
  364. test_mlockall();
  365. ksft_finished();
  366. }