diff --git a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp --- a/compiler-rt/lib/dfsan/dfsan_interceptors.cpp +++ b/compiler-rt/lib/dfsan/dfsan_interceptors.cpp @@ -46,12 +46,25 @@ return res; } +INTERCEPTOR(int, munmap, void *addr, SIZE_T length) { + int res = REAL(munmap)(addr, length); + if (res != -1) { + uptr beg_shadow_addr = (uptr)__dfsan::shadow_for(addr); + void *end_addr = + (void *)((uptr)addr + RoundUpTo(length, GetPageSizeCached())); + uptr end_shadow_addr = (uptr)__dfsan::shadow_for(end_addr); + ReleaseMemoryPagesToOS(beg_shadow_addr, end_shadow_addr); + } + return res; +} + namespace __dfsan { void InitializeInterceptors() { CHECK(!interceptors_initialized); INTERCEPT_FUNCTION(mmap); INTERCEPT_FUNCTION(mmap64); + INTERCEPT_FUNCTION(munmap); interceptors_initialized = true; } diff --git a/compiler-rt/test/dfsan/munmap_release_shadow.c b/compiler-rt/test/dfsan/munmap_release_shadow.c new file mode 100644 --- /dev/null +++ b/compiler-rt/test/dfsan/munmap_release_shadow.c @@ -0,0 +1,71 @@ +// RUN: %clang_dfsan %s -o %t && %run %t + +#include +#include +#include +#include +#include +#include +#include + +ssize_t get_rss_with_unit_k() { + long rss = 0L; + FILE *f = NULL; + assert((f = fopen("/proc/self/statm", "r"))); + assert(fscanf(f, "%*s%ld", &rss) == 1); + fclose(f); + return ((ssize_t)rss * (ssize_t)sysconf(_SC_PAGESIZE)) >> 10; +} + +int mmap_track_and_munmap(dfsan_label label, int **hint, bool use_intercepted) { + // mmap a buffer with N int values. hint is the suggested address of the mmap. + const int N = 1000000; + int *ptr = (int *)mmap(*hint, N * sizeof(int), PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ptr == MAP_FAILED) + return 1; + + // fills and track the buffer. + for (int k = 0; k < N; ++k) { + ptr[k] = 1; + dfsan_set_label(label, ptr + k, sizeof(int)); + } + + // munmap the buffer + int err = use_intercepted ? munmap(ptr, N * sizeof(int)) + : syscall(__NR_munmap, ptr, N * sizeof(int)); + if (err != 0) + return 1; + + // hint is changed after each mmap to prevent mmap from reusing the same + // address. This helps us measure memory usage. + *hint = ptr + N * sizeof(int); + + return 0; +} + +ssize_t loop_mmap_track_and_munmap(int num_loops, bool use_intercepted) { + // Calls mmap/munmap in a loop. + ssize_t start_rss = get_rss_with_unit_k(); + + int *hint = NULL; + dfsan_label label = dfsan_create_label("l", 0); + + for (int i = 0; i < num_loops; ++i) + assert(!mmap_track_and_munmap(label, &hint, use_intercepted)); + + ssize_t end_rss = get_rss_with_unit_k(); + fprintf(stderr, "RSS increment (%d): %td-%td=%td\n", use_intercepted, end_rss, + start_rss, end_rss - start_rss); + return end_rss - start_rss; +} + +int main(int argc, char **argv) { + const int num_loops = 500; + ssize_t rss_inc_intercepted = loop_mmap_track_and_munmap(num_loops, true); + ssize_t rss_inc_system = loop_mmap_track_and_munmap(num_loops, false); + // Without releasing shadow space at munmap, RSS increment is > 4G. + // With releasing shadow space at munmap, RSS increment is < 400M. + assert(rss_inc_system > 5 * rss_inc_intercepted); + return 0; +}