xref: /llvm-project/compiler-rt/test/dfsan/release_shadow_space.c (revision 221336c8e2b0d2d1979cdaf198ab0afcf930d795)
1 // DFSAN_OPTIONS=no_huge_pages_for_shadow=false RUN: %clang_dfsan %s -o %t && %run %t
2 // DFSAN_OPTIONS=no_huge_pages_for_shadow=true RUN: %clang_dfsan %s -o %t && %run %t
3 // DFSAN_OPTIONS=no_huge_pages_for_shadow=false RUN: %clang_dfsan %s -DORIGIN_TRACKING -mllvm -dfsan-track-origins=1 -o %t && %run %t
4 // DFSAN_OPTIONS=no_huge_pages_for_shadow=true RUN: %clang_dfsan %s -DORIGIN_TRACKING -mllvm -dfsan-track-origins=1 -o %t && %run %t
5 
6 #include <assert.h>
7 #include <sanitizer/dfsan_interface.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/mman.h>
12 #include <unistd.h>
13 
get_rss_kb()14 size_t get_rss_kb() {
15   size_t ret = 0;
16   pid_t pid = getpid();
17 
18   char fname[256];
19   sprintf(fname, "/proc/%ld/task/%ld/smaps", (long)pid, (long)pid);
20   FILE *f = fopen(fname, "r");
21   assert(f);
22 
23   char buf[256];
24   while (fgets(buf, sizeof(buf), f) != NULL) {
25     int64_t rss;
26     // DFSan's sscanf is broken and doesn't check for ordinary characters in
27     // the format string, hence we use strstr as a secondary check
28     // (https://github.com/llvm/llvm-project/issues/94769).
29     if ((sscanf(buf, "Rss: %ld kB", &rss) == 1) &&
30         (strstr(buf, "Rss: ") != NULL))
31       ret += rss;
32   }
33   assert(feof(f));
34   fclose(f);
35 
36   return ret;
37 }
38 
main(int argc,char ** argv)39 int main(int argc, char **argv) {
40   const size_t map_size = 100 << 20;
41   size_t before = get_rss_kb();
42 
43   // mmap and touch all addresses. The overhead is 1x.
44   char *p = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
45                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
46   memset(p, 0xff, map_size);
47   size_t after_mmap = get_rss_kb();
48 
49   // store labels to all addresses. The overhead is 2x.
50   const dfsan_label label = 8;
51   char val = 0xff;
52   dfsan_set_label(label, &val, sizeof(val));
53   memset(p, val, map_size);
54   size_t after_mmap_and_set_label = get_rss_kb();
55 
56   // fixed-mmap the same address. OS recyles pages and reinitializes data at the
57   // address. This should be the same to calling munmap.
58   p = mmap(p, map_size, PROT_READ | PROT_WRITE,
59            MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
60   size_t after_fixed_mmap = get_rss_kb();
61 
62   // store labels to all addresses.
63   memset(p, val, map_size);
64   size_t after_mmap_and_set_label2 = get_rss_kb();
65 
66   // munmap the addresses.
67   munmap(p, map_size);
68   size_t after_munmap = get_rss_kb();
69 
70   fprintf(
71       stderr,
72       "RSS at start: %zu, after mmap: %zu, after mmap+set label: %zu, after "
73       "fixed map: %zu, after another mmap+set label: %zu, after munmap: %zu\n",
74       before, after_mmap, after_mmap_and_set_label, after_fixed_mmap,
75       after_mmap_and_set_label2, after_munmap);
76 
77   // This is orders of magnitude larger than we expect (typically < 10,000KB).
78   // It is a quick check to ensure that the RSS calculation function isn't
79   // egregriously wrong.
80   assert(before < 1000000);
81 
82   const size_t mmap_cost_kb = map_size >> 10;
83   // Shadow space (1:1 with application memory)
84   const size_t mmap_shadow_cost_kb = sizeof(dfsan_label) * mmap_cost_kb;
85 #ifdef ORIGIN_TRACKING
86   // Origin space (1:1 with application memory)
87   const size_t mmap_origin_cost_kb = mmap_cost_kb;
88 #else
89   const size_t mmap_origin_cost_kb = 0;
90 #endif
91   assert(after_mmap >= before + mmap_cost_kb);
92   assert(after_mmap_and_set_label >=
93          after_mmap + mmap_shadow_cost_kb + mmap_origin_cost_kb);
94   assert(after_mmap_and_set_label2 >=
95          before + mmap_cost_kb + mmap_shadow_cost_kb + mmap_origin_cost_kb);
96 
97 #ifdef ORIGIN_TRACKING
98   // This value is chosen based on observed difference.
99   const size_t mmap_origin_chain_kb = 4000;
100 #else
101   const size_t mmap_origin_chain_kb = 0;
102 #endif
103 
104   // RSS may not change memory amount after munmap to the same level as the
105   // start of the program. The assert checks the memory up to a delta.
106   const size_t delta = 5000;
107   // Origin chains are not freed, even when the origin space which refers to
108   // them is freed, so mmap_origin_chain_kb is added to account for this.
109   assert(after_fixed_mmap <= before + delta + mmap_origin_chain_kb);
110   assert(after_munmap <= before + delta + mmap_origin_chain_kb);
111 
112   return 0;
113 }
114