0001
0002 #define _GNU_SOURCE
0003 #include <sys/mman.h>
0004 #include <stdint.h>
0005 #include <unistd.h>
0006 #include <string.h>
0007 #include <sys/time.h>
0008 #include <sys/resource.h>
0009 #include <stdbool.h>
0010 #include "mlock2.h"
0011
0012 #include "../kselftest.h"
0013
0014 struct vm_boundaries {
0015 unsigned long start;
0016 unsigned long end;
0017 };
0018
0019 static int get_vm_area(unsigned long addr, struct vm_boundaries *area)
0020 {
0021 FILE *file;
0022 int ret = 1;
0023 char line[1024] = {0};
0024 char *end_addr;
0025 char *stop;
0026 unsigned long start;
0027 unsigned long end;
0028
0029 if (!area)
0030 return ret;
0031
0032 file = fopen("/proc/self/maps", "r");
0033 if (!file) {
0034 perror("fopen");
0035 return ret;
0036 }
0037
0038 memset(area, 0, sizeof(struct vm_boundaries));
0039
0040 while(fgets(line, 1024, file)) {
0041 end_addr = strchr(line, '-');
0042 if (!end_addr) {
0043 printf("cannot parse /proc/self/maps\n");
0044 goto out;
0045 }
0046 *end_addr = '\0';
0047 end_addr++;
0048 stop = strchr(end_addr, ' ');
0049 if (!stop) {
0050 printf("cannot parse /proc/self/maps\n");
0051 goto out;
0052 }
0053 stop = '\0';
0054
0055 sscanf(line, "%lx", &start);
0056 sscanf(end_addr, "%lx", &end);
0057
0058 if (start <= addr && end > addr) {
0059 area->start = start;
0060 area->end = end;
0061 ret = 0;
0062 goto out;
0063 }
0064 }
0065 out:
0066 fclose(file);
0067 return ret;
0068 }
0069
0070 #define VMFLAGS "VmFlags:"
0071
0072 static bool is_vmflag_set(unsigned long addr, const char *vmflag)
0073 {
0074 char *line = NULL;
0075 char *flags;
0076 size_t size = 0;
0077 bool ret = false;
0078 FILE *smaps;
0079
0080 smaps = seek_to_smaps_entry(addr);
0081 if (!smaps) {
0082 printf("Unable to parse /proc/self/smaps\n");
0083 goto out;
0084 }
0085
0086 while (getline(&line, &size, smaps) > 0) {
0087 if (!strstr(line, VMFLAGS)) {
0088 free(line);
0089 line = NULL;
0090 size = 0;
0091 continue;
0092 }
0093
0094 flags = line + strlen(VMFLAGS);
0095 ret = (strstr(flags, vmflag) != NULL);
0096 goto out;
0097 }
0098
0099 out:
0100 free(line);
0101 fclose(smaps);
0102 return ret;
0103 }
0104
0105 #define SIZE "Size:"
0106 #define RSS "Rss:"
0107 #define LOCKED "lo"
0108
0109 static unsigned long get_value_for_name(unsigned long addr, const char *name)
0110 {
0111 char *line = NULL;
0112 size_t size = 0;
0113 char *value_ptr;
0114 FILE *smaps = NULL;
0115 unsigned long value = -1UL;
0116
0117 smaps = seek_to_smaps_entry(addr);
0118 if (!smaps) {
0119 printf("Unable to parse /proc/self/smaps\n");
0120 goto out;
0121 }
0122
0123 while (getline(&line, &size, smaps) > 0) {
0124 if (!strstr(line, name)) {
0125 free(line);
0126 line = NULL;
0127 size = 0;
0128 continue;
0129 }
0130
0131 value_ptr = line + strlen(name);
0132 if (sscanf(value_ptr, "%lu kB", &value) < 1) {
0133 printf("Unable to parse smaps entry for Size\n");
0134 goto out;
0135 }
0136 break;
0137 }
0138
0139 out:
0140 if (smaps)
0141 fclose(smaps);
0142 free(line);
0143 return value;
0144 }
0145
0146 static bool is_vma_lock_on_fault(unsigned long addr)
0147 {
0148 bool locked;
0149 unsigned long vma_size, vma_rss;
0150
0151 locked = is_vmflag_set(addr, LOCKED);
0152 if (!locked)
0153 return false;
0154
0155 vma_size = get_value_for_name(addr, SIZE);
0156 vma_rss = get_value_for_name(addr, RSS);
0157
0158
0159 return (vma_rss < vma_size);
0160 }
0161
0162 #define PRESENT_BIT 0x8000000000000000ULL
0163 #define PFN_MASK 0x007FFFFFFFFFFFFFULL
0164 #define UNEVICTABLE_BIT (1UL << 18)
0165
0166 static int lock_check(unsigned long addr)
0167 {
0168 bool locked;
0169 unsigned long vma_size, vma_rss;
0170
0171 locked = is_vmflag_set(addr, LOCKED);
0172 if (!locked)
0173 return false;
0174
0175 vma_size = get_value_for_name(addr, SIZE);
0176 vma_rss = get_value_for_name(addr, RSS);
0177
0178 return (vma_rss == vma_size);
0179 }
0180
0181 static int unlock_lock_check(char *map)
0182 {
0183 if (is_vmflag_set((unsigned long)map, LOCKED)) {
0184 printf("VMA flag %s is present on page 1 after unlock\n", LOCKED);
0185 return 1;
0186 }
0187
0188 return 0;
0189 }
0190
0191 static int test_mlock_lock()
0192 {
0193 char *map;
0194 int ret = 1;
0195 unsigned long page_size = getpagesize();
0196
0197 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
0198 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0199 if (map == MAP_FAILED) {
0200 perror("test_mlock_locked mmap");
0201 goto out;
0202 }
0203
0204 if (mlock2_(map, 2 * page_size, 0)) {
0205 if (errno == ENOSYS) {
0206 printf("Cannot call new mlock family, skipping test\n");
0207 _exit(KSFT_SKIP);
0208 }
0209 perror("mlock2(0)");
0210 goto unmap;
0211 }
0212
0213 if (!lock_check((unsigned long)map))
0214 goto unmap;
0215
0216
0217 if (munlock(map, 2 * page_size)) {
0218 perror("munlock()");
0219 goto unmap;
0220 }
0221
0222 ret = unlock_lock_check(map);
0223
0224 unmap:
0225 munmap(map, 2 * page_size);
0226 out:
0227 return ret;
0228 }
0229
0230 static int onfault_check(char *map)
0231 {
0232 *map = 'a';
0233 if (!is_vma_lock_on_fault((unsigned long)map)) {
0234 printf("VMA is not marked for lock on fault\n");
0235 return 1;
0236 }
0237
0238 return 0;
0239 }
0240
0241 static int unlock_onfault_check(char *map)
0242 {
0243 unsigned long page_size = getpagesize();
0244
0245 if (is_vma_lock_on_fault((unsigned long)map) ||
0246 is_vma_lock_on_fault((unsigned long)map + page_size)) {
0247 printf("VMA is still lock on fault after unlock\n");
0248 return 1;
0249 }
0250
0251 return 0;
0252 }
0253
0254 static int test_mlock_onfault()
0255 {
0256 char *map;
0257 int ret = 1;
0258 unsigned long page_size = getpagesize();
0259
0260 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
0261 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0262 if (map == MAP_FAILED) {
0263 perror("test_mlock_locked mmap");
0264 goto out;
0265 }
0266
0267 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
0268 if (errno == ENOSYS) {
0269 printf("Cannot call new mlock family, skipping test\n");
0270 _exit(KSFT_SKIP);
0271 }
0272 perror("mlock2(MLOCK_ONFAULT)");
0273 goto unmap;
0274 }
0275
0276 if (onfault_check(map))
0277 goto unmap;
0278
0279
0280 if (munlock(map, 2 * page_size)) {
0281 if (errno == ENOSYS) {
0282 printf("Cannot call new mlock family, skipping test\n");
0283 _exit(KSFT_SKIP);
0284 }
0285 perror("munlock()");
0286 goto unmap;
0287 }
0288
0289 ret = unlock_onfault_check(map);
0290 unmap:
0291 munmap(map, 2 * page_size);
0292 out:
0293 return ret;
0294 }
0295
0296 static int test_lock_onfault_of_present()
0297 {
0298 char *map;
0299 int ret = 1;
0300 unsigned long page_size = getpagesize();
0301
0302 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
0303 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0304 if (map == MAP_FAILED) {
0305 perror("test_mlock_locked mmap");
0306 goto out;
0307 }
0308
0309 *map = 'a';
0310
0311 if (mlock2_(map, 2 * page_size, MLOCK_ONFAULT)) {
0312 if (errno == ENOSYS) {
0313 printf("Cannot call new mlock family, skipping test\n");
0314 _exit(KSFT_SKIP);
0315 }
0316 perror("mlock2(MLOCK_ONFAULT)");
0317 goto unmap;
0318 }
0319
0320 if (!is_vma_lock_on_fault((unsigned long)map) ||
0321 !is_vma_lock_on_fault((unsigned long)map + page_size)) {
0322 printf("VMA with present pages is not marked lock on fault\n");
0323 goto unmap;
0324 }
0325 ret = 0;
0326 unmap:
0327 munmap(map, 2 * page_size);
0328 out:
0329 return ret;
0330 }
0331
0332 static int test_munlockall()
0333 {
0334 char *map;
0335 int ret = 1;
0336 unsigned long page_size = getpagesize();
0337
0338 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
0339 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0340
0341 if (map == MAP_FAILED) {
0342 perror("test_munlockall mmap");
0343 goto out;
0344 }
0345
0346 if (mlockall(MCL_CURRENT)) {
0347 perror("mlockall(MCL_CURRENT)");
0348 goto out;
0349 }
0350
0351 if (!lock_check((unsigned long)map))
0352 goto unmap;
0353
0354 if (munlockall()) {
0355 perror("munlockall()");
0356 goto unmap;
0357 }
0358
0359 if (unlock_lock_check(map))
0360 goto unmap;
0361
0362 munmap(map, 2 * page_size);
0363
0364 map = mmap(NULL, 2 * page_size, PROT_READ | PROT_WRITE,
0365 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0366
0367 if (map == MAP_FAILED) {
0368 perror("test_munlockall second mmap");
0369 goto out;
0370 }
0371
0372 if (mlockall(MCL_CURRENT | MCL_ONFAULT)) {
0373 perror("mlockall(MCL_CURRENT | MCL_ONFAULT)");
0374 goto unmap;
0375 }
0376
0377 if (onfault_check(map))
0378 goto unmap;
0379
0380 if (munlockall()) {
0381 perror("munlockall()");
0382 goto unmap;
0383 }
0384
0385 if (unlock_onfault_check(map))
0386 goto unmap;
0387
0388 if (mlockall(MCL_CURRENT | MCL_FUTURE)) {
0389 perror("mlockall(MCL_CURRENT | MCL_FUTURE)");
0390 goto out;
0391 }
0392
0393 if (!lock_check((unsigned long)map))
0394 goto unmap;
0395
0396 if (munlockall()) {
0397 perror("munlockall()");
0398 goto unmap;
0399 }
0400
0401 ret = unlock_lock_check(map);
0402
0403 unmap:
0404 munmap(map, 2 * page_size);
0405 out:
0406 munlockall();
0407 return ret;
0408 }
0409
0410 static int test_vma_management(bool call_mlock)
0411 {
0412 int ret = 1;
0413 void *map;
0414 unsigned long page_size = getpagesize();
0415 struct vm_boundaries page1;
0416 struct vm_boundaries page2;
0417 struct vm_boundaries page3;
0418
0419 map = mmap(NULL, 3 * page_size, PROT_READ | PROT_WRITE,
0420 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
0421 if (map == MAP_FAILED) {
0422 perror("mmap()");
0423 return ret;
0424 }
0425
0426 if (call_mlock && mlock2_(map, 3 * page_size, MLOCK_ONFAULT)) {
0427 if (errno == ENOSYS) {
0428 printf("Cannot call new mlock family, skipping test\n");
0429 _exit(KSFT_SKIP);
0430 }
0431 perror("mlock(ONFAULT)\n");
0432 goto out;
0433 }
0434
0435 if (get_vm_area((unsigned long)map, &page1) ||
0436 get_vm_area((unsigned long)map + page_size, &page2) ||
0437 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
0438 printf("couldn't find mapping in /proc/self/maps\n");
0439 goto out;
0440 }
0441
0442
0443
0444
0445
0446
0447 if (page1.start != page2.start || page2.start != page3.start) {
0448 printf("VMAs are not merged to start, aborting test\n");
0449 ret = 0;
0450 goto out;
0451 }
0452
0453 if (munlock(map + page_size, page_size)) {
0454 perror("munlock()");
0455 goto out;
0456 }
0457
0458 if (get_vm_area((unsigned long)map, &page1) ||
0459 get_vm_area((unsigned long)map + page_size, &page2) ||
0460 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
0461 printf("couldn't find mapping in /proc/self/maps\n");
0462 goto out;
0463 }
0464
0465
0466 if (page1.start == page2.start || page2.start == page3.start) {
0467 printf("failed to split VMA for munlock\n");
0468 goto out;
0469 }
0470
0471
0472 if (munlock(map, page_size * 3)) {
0473 perror("munlock()");
0474 goto out;
0475 }
0476
0477 if (get_vm_area((unsigned long)map, &page1) ||
0478 get_vm_area((unsigned long)map + page_size, &page2) ||
0479 get_vm_area((unsigned long)map + page_size * 2, &page3)) {
0480 printf("couldn't find mapping in /proc/self/maps\n");
0481 goto out;
0482 }
0483
0484
0485 if (page1.start != page2.start || page2.start != page3.start) {
0486 printf("failed to merge VMAs after munlock\n");
0487 goto out;
0488 }
0489
0490 ret = 0;
0491 out:
0492 munmap(map, 3 * page_size);
0493 return ret;
0494 }
0495
0496 static int test_mlockall(int (test_function)(bool call_mlock))
0497 {
0498 int ret = 1;
0499
0500 if (mlockall(MCL_CURRENT | MCL_ONFAULT | MCL_FUTURE)) {
0501 perror("mlockall");
0502 return ret;
0503 }
0504
0505 ret = test_function(false);
0506 munlockall();
0507 return ret;
0508 }
0509
0510 int main(int argc, char **argv)
0511 {
0512 int ret = 0;
0513 ret += test_mlock_lock();
0514 ret += test_mlock_onfault();
0515 ret += test_munlockall();
0516 ret += test_lock_onfault_of_present();
0517 ret += test_vma_management(true);
0518 ret += test_mlockall(test_vma_management);
0519 return ret;
0520 }