Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
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     /* only one page is faulted in */
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     /* Now unlock and recheck attributes */
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     /* Now unlock and recheck attributes */
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      * Before we unlock a portion, we need to that all three pages are in
0444      * the same VMA.  If they are not we abort this test (Note that this is
0445      * not a failure)
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     /* All three VMAs should be different */
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     /* Now unlock the first and third page and check the VMAs again */
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     /* Now all three VMAs should be the same */
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 }