mlock-random-test.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * It tests the mlock/mlock2() when they are invoked
  4. * on randomly memory region.
  5. */
  6. #include <unistd.h>
  7. #include <sys/resource.h>
  8. #include <sys/capability.h>
  9. #include <sys/mman.h>
  10. #include <fcntl.h>
  11. #include <string.h>
  12. #include <sys/ipc.h>
  13. #include <sys/shm.h>
  14. #include <time.h>
  15. #include "mlock2.h"
  16. #define CHUNK_UNIT (128 * 1024)
  17. #define MLOCK_RLIMIT_SIZE (CHUNK_UNIT * 2)
  18. #define MLOCK_WITHIN_LIMIT_SIZE CHUNK_UNIT
  19. #define MLOCK_OUTOF_LIMIT_SIZE (CHUNK_UNIT * 3)
  20. #define TEST_LOOP 100
  21. #define PAGE_ALIGN(size, ps) (((size) + ((ps) - 1)) & ~((ps) - 1))
  22. int set_cap_limits(rlim_t max)
  23. {
  24. struct rlimit new;
  25. cap_t cap = cap_init();
  26. new.rlim_cur = max;
  27. new.rlim_max = max;
  28. if (setrlimit(RLIMIT_MEMLOCK, &new)) {
  29. perror("setrlimit() returns error\n");
  30. return -1;
  31. }
  32. /* drop capabilities including CAP_IPC_LOCK */
  33. if (cap_set_proc(cap)) {
  34. perror("cap_set_proc() returns error\n");
  35. return -2;
  36. }
  37. return 0;
  38. }
  39. int get_proc_locked_vm_size(void)
  40. {
  41. FILE *f;
  42. int ret = -1;
  43. char line[1024] = {0};
  44. unsigned long lock_size = 0;
  45. f = fopen("/proc/self/status", "r");
  46. if (!f) {
  47. perror("fopen");
  48. return -1;
  49. }
  50. while (fgets(line, 1024, f)) {
  51. if (strstr(line, "VmLck")) {
  52. ret = sscanf(line, "VmLck:\t%8lu kB", &lock_size);
  53. if (ret <= 0) {
  54. printf("sscanf() on VmLck error: %s: %d\n",
  55. line, ret);
  56. fclose(f);
  57. return -1;
  58. }
  59. fclose(f);
  60. return (int)(lock_size << 10);
  61. }
  62. }
  63. perror("cannot parse VmLck in /proc/self/status\n");
  64. fclose(f);
  65. return -1;
  66. }
  67. /*
  68. * Get the MMUPageSize of the memory region including input
  69. * address from proc file.
  70. *
  71. * return value: on error case, 0 will be returned.
  72. * Otherwise the page size(in bytes) is returned.
  73. */
  74. int get_proc_page_size(unsigned long addr)
  75. {
  76. FILE *smaps;
  77. char *line;
  78. unsigned long mmupage_size = 0;
  79. size_t size;
  80. smaps = seek_to_smaps_entry(addr);
  81. if (!smaps) {
  82. printf("Unable to parse /proc/self/smaps\n");
  83. return 0;
  84. }
  85. while (getline(&line, &size, smaps) > 0) {
  86. if (!strstr(line, "MMUPageSize")) {
  87. free(line);
  88. line = NULL;
  89. size = 0;
  90. continue;
  91. }
  92. /* found the MMUPageSize of this section */
  93. if (sscanf(line, "MMUPageSize: %8lu kB",
  94. &mmupage_size) < 1) {
  95. printf("Unable to parse smaps entry for Size:%s\n",
  96. line);
  97. break;
  98. }
  99. }
  100. free(line);
  101. if (smaps)
  102. fclose(smaps);
  103. return mmupage_size << 10;
  104. }
  105. /*
  106. * Test mlock/mlock2() on provided memory chunk.
  107. * It expects the mlock/mlock2() to be successful (within rlimit)
  108. *
  109. * With allocated memory chunk [p, p + alloc_size), this
  110. * test will choose start/len randomly to perform mlock/mlock2
  111. * [start, start + len] memory range. The range is within range
  112. * of the allocated chunk.
  113. *
  114. * The memory region size alloc_size is within the rlimit.
  115. * So we always expect a success of mlock/mlock2.
  116. *
  117. * VmLck is assumed to be 0 before this test.
  118. *
  119. * return value: 0 - success
  120. * else: failure
  121. */
  122. int test_mlock_within_limit(char *p, int alloc_size)
  123. {
  124. int i;
  125. int ret = 0;
  126. int locked_vm_size = 0;
  127. struct rlimit cur;
  128. int page_size = 0;
  129. getrlimit(RLIMIT_MEMLOCK, &cur);
  130. if (cur.rlim_cur < alloc_size) {
  131. printf("alloc_size[%d] < %u rlimit,lead to mlock failure\n",
  132. alloc_size, (unsigned int)cur.rlim_cur);
  133. return -1;
  134. }
  135. srand(time(NULL));
  136. for (i = 0; i < TEST_LOOP; i++) {
  137. /*
  138. * - choose mlock/mlock2 randomly
  139. * - choose lock_size randomly but lock_size < alloc_size
  140. * - choose start_offset randomly but p+start_offset+lock_size
  141. * < p+alloc_size
  142. */
  143. int is_mlock = !!(rand() % 2);
  144. int lock_size = rand() % alloc_size;
  145. int start_offset = rand() % (alloc_size - lock_size);
  146. if (is_mlock)
  147. ret = mlock(p + start_offset, lock_size);
  148. else
  149. ret = mlock2_(p + start_offset, lock_size,
  150. MLOCK_ONFAULT);
  151. if (ret) {
  152. printf("%s() failure at |%p(%d)| mlock:|%p(%d)|\n",
  153. is_mlock ? "mlock" : "mlock2",
  154. p, alloc_size,
  155. p + start_offset, lock_size);
  156. return ret;
  157. }
  158. }
  159. /*
  160. * Check VmLck left by the tests.
  161. */
  162. locked_vm_size = get_proc_locked_vm_size();
  163. page_size = get_proc_page_size((unsigned long)p);
  164. if (page_size == 0) {
  165. printf("cannot get proc MMUPageSize\n");
  166. return -1;
  167. }
  168. if (locked_vm_size > PAGE_ALIGN(alloc_size, page_size) + page_size) {
  169. printf("test_mlock_within_limit() left VmLck:%d on %d chunk\n",
  170. locked_vm_size, alloc_size);
  171. return -1;
  172. }
  173. return 0;
  174. }
  175. /*
  176. * We expect the mlock/mlock2() to be fail (outof limitation)
  177. *
  178. * With allocated memory chunk [p, p + alloc_size), this
  179. * test will randomly choose start/len and perform mlock/mlock2
  180. * on [start, start+len] range.
  181. *
  182. * The memory region size alloc_size is above the rlimit.
  183. * And the len to be locked is higher than rlimit.
  184. * So we always expect a failure of mlock/mlock2.
  185. * No locked page number should be increased as a side effect.
  186. *
  187. * return value: 0 - success
  188. * else: failure
  189. */
  190. int test_mlock_outof_limit(char *p, int alloc_size)
  191. {
  192. int i;
  193. int ret = 0;
  194. int locked_vm_size = 0, old_locked_vm_size = 0;
  195. struct rlimit cur;
  196. getrlimit(RLIMIT_MEMLOCK, &cur);
  197. if (cur.rlim_cur >= alloc_size) {
  198. printf("alloc_size[%d] >%u rlimit, violates test condition\n",
  199. alloc_size, (unsigned int)cur.rlim_cur);
  200. return -1;
  201. }
  202. old_locked_vm_size = get_proc_locked_vm_size();
  203. srand(time(NULL));
  204. for (i = 0; i < TEST_LOOP; i++) {
  205. int is_mlock = !!(rand() % 2);
  206. int lock_size = (rand() % (alloc_size - cur.rlim_cur))
  207. + cur.rlim_cur;
  208. int start_offset = rand() % (alloc_size - lock_size);
  209. if (is_mlock)
  210. ret = mlock(p + start_offset, lock_size);
  211. else
  212. ret = mlock2_(p + start_offset, lock_size,
  213. MLOCK_ONFAULT);
  214. if (ret == 0) {
  215. printf("%s() succeeds? on %p(%d) mlock%p(%d)\n",
  216. is_mlock ? "mlock" : "mlock2",
  217. p, alloc_size,
  218. p + start_offset, lock_size);
  219. return -1;
  220. }
  221. }
  222. locked_vm_size = get_proc_locked_vm_size();
  223. if (locked_vm_size != old_locked_vm_size) {
  224. printf("tests leads to new mlocked page: old[%d], new[%d]\n",
  225. old_locked_vm_size,
  226. locked_vm_size);
  227. return -1;
  228. }
  229. return 0;
  230. }
  231. int main(int argc, char **argv)
  232. {
  233. char *p = NULL;
  234. int ret = 0;
  235. if (set_cap_limits(MLOCK_RLIMIT_SIZE))
  236. return -1;
  237. p = malloc(MLOCK_WITHIN_LIMIT_SIZE);
  238. if (p == NULL) {
  239. perror("malloc() failure\n");
  240. return -1;
  241. }
  242. ret = test_mlock_within_limit(p, MLOCK_WITHIN_LIMIT_SIZE);
  243. if (ret)
  244. return ret;
  245. munlock(p, MLOCK_WITHIN_LIMIT_SIZE);
  246. free(p);
  247. p = malloc(MLOCK_OUTOF_LIMIT_SIZE);
  248. if (p == NULL) {
  249. perror("malloc() failure\n");
  250. return -1;
  251. }
  252. ret = test_mlock_outof_limit(p, MLOCK_OUTOF_LIMIT_SIZE);
  253. if (ret)
  254. return ret;
  255. munlock(p, MLOCK_OUTOF_LIMIT_SIZE);
  256. free(p);
  257. return 0;
  258. }