Check that a partial MREMAP_DONTUNMAP operation on mlocked memory
preserves locked-memory accounting and that unmapping both mappings
returns VmLck to its original value.

This tests for a locked_vm accounting leak after a partial remap.

Signed-off-by: Muhammad Mahdi Amirpour <[email protected]>
---
 tools/testing/selftests/mm/mremap_dontunmap.c | 95 ++++++++++++++++++-
 1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/mm/mremap_dontunmap.c 
b/tools/testing/selftests/mm/mremap_dontunmap.c
index 96ba537facf..a0fd99ff825 100644
--- a/tools/testing/selftests/mm/mremap_dontunmap.c
+++ b/tools/testing/selftests/mm/mremap_dontunmap.c
@@ -335,6 +335,98 @@ static void 
mremap_dontunmap_partial_mapping_overwrite(void)
        ksft_test_result_pass("%s\n", __func__);
 }
 
+static long get_vmlck_kb(void)
+{
+       FILE *f = fopen("/proc/self/status", "r");
+       char *line = NULL;
+       size_t len = 0;
+       long kb = -1;
+
+       if (!f)
+               ksft_exit_fail_msg("fopen /proc/self/status: %s\n",
+                                  strerror(errno));
+
+       while (getline(&line, &len, f) != -1) {
+               if (sscanf(line, "VmLck: %ld kB", &kb) == 1)
+                       break;
+       }
+
+       free(line);
+       fclose(f);
+
+       if (kb < 0)
+               ksft_exit_fail_msg("VmLck not found in /proc/self/status\n");
+
+       return kb;
+}
+
+static void mremap_dontunmap_partial_mlocked(void)
+{
+       unsigned long num_pages = 10;
+       size_t size = num_pages * page_size;
+       size_t half = (num_pages / 2) * page_size;
+       void *src, *dst;
+       long baseline, locked, remapped, after;
+       bool pass;
+
+       baseline = get_vmlck_kb();
+
+       src = mmap(NULL, size, PROT_READ | PROT_WRITE,
+                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (src == MAP_FAILED)
+               ksft_exit_fail_msg("mmap source: %s\n", strerror(errno));
+
+       dst = mmap(NULL, half, PROT_READ | PROT_WRITE,
+                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+       if (dst == MAP_FAILED)
+               ksft_exit_fail_msg("mmap destination: %s\n", strerror(errno));
+
+       memset(src, 'a', size);
+
+       if (mlock(src, size)) {
+               if (errno == EPERM || errno == ENOMEM) {
+                       ksft_print_msg("mlock unavailable: %s\n",
+                                      strerror(errno));
+
+                       if (munmap(dst, half))
+                               ksft_exit_fail_msg("munmap destination: %s\n",
+                                                  strerror(errno));
+                       if (munmap(src, size))
+                               ksft_exit_fail_msg("munmap source: %s\n",
+                                                  strerror(errno));
+
+                       ksft_test_result_skip("%s\n", __func__);
+                       return;
+               }
+               ksft_exit_fail_msg("mlock: %s\n", strerror(errno));
+       }
+
+       locked = get_vmlck_kb();
+
+       if (mremap(src + half, half, half,
+                  MREMAP_MAYMOVE | MREMAP_FIXED | MREMAP_DONTUNMAP,
+                  dst) == MAP_FAILED)
+               ksft_exit_fail_msg("mremap: %s\n", strerror(errno));
+
+       remapped = get_vmlck_kb();
+
+       if (munmap(dst, half))
+               ksft_exit_fail_msg("munmap destination: %s\n",
+                                  strerror(errno));
+       if (munmap(src, size))
+               ksft_exit_fail_msg("munmap source: %s\n", strerror(errno));
+
+       after = get_vmlck_kb();
+
+       pass = locked == baseline + (long)(size / 1024) &&
+              remapped == locked &&
+              after == baseline;
+
+       ksft_test_result(pass,
+                        "%s (baseline=%ld kB, locked=%ld kB, remapped=%ld kB, 
after=%ld kB)\n",
+                        __func__, baseline, locked, remapped, after);
+}
+
 int main(void)
 {
        ksft_print_header();
@@ -348,7 +440,7 @@ int main(void)
                ksft_finished();
        }
 
-       ksft_set_plan(5);
+       ksft_set_plan(6);
 
        // Keep a page sized buffer around for when we need it.
        page_buffer =
@@ -361,6 +453,7 @@ int main(void)
        mremap_dontunmap_simple_fixed();
        mremap_dontunmap_partial_mapping();
        mremap_dontunmap_partial_mapping_overwrite();
+       mremap_dontunmap_partial_mlocked();
 
        BUG_ON(munmap(page_buffer, page_size) == -1,
               "unable to unmap page buffer");
-- 
2.55.0


Reply via email to