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 | |
14 | size_t () { |
15 | size_t ret = 0; |
16 | pid_t pid = getpid(); |
17 | |
18 | char fname[256]; |
19 | sprintf(s: fname, format: "/proc/%ld/task/%ld/smaps" , (long)pid, (long)pid); |
20 | FILE *f = fopen(filename: fname, modes: "r" ); |
21 | assert(f); |
22 | |
23 | char buf[256]; |
24 | while (fgets(s: buf, n: sizeof(buf), stream: f) != NULL) { |
25 | int64_t ; |
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(s: buf, format: "Rss: %ld kB" , &rss) == 1) && |
30 | (strstr(haystack: buf, needle: "Rss: " ) != NULL)) |
31 | ret += rss; |
32 | } |
33 | assert(feof(f)); |
34 | fclose(stream: f); |
35 | |
36 | return ret; |
37 | } |
38 | |
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, len: map_size, PROT_READ | PROT_WRITE, |
45 | MAP_PRIVATE | MAP_ANONYMOUS, fd: -1, offset: 0); |
46 | memset(s: p, c: 0xff, n: 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, addr: &val, size: sizeof(val)); |
53 | memset(s: p, c: val, n: 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(addr: p, len: map_size, PROT_READ | PROT_WRITE, |
59 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, fd: -1, offset: 0); |
60 | size_t after_fixed_mmap = get_rss_kb(); |
61 | |
62 | // store labels to all addresses. |
63 | memset(s: p, c: val, n: map_size); |
64 | size_t after_mmap_and_set_label2 = get_rss_kb(); |
65 | |
66 | // munmap the addresses. |
67 | munmap(addr: p, len: map_size); |
68 | size_t after_munmap = get_rss_kb(); |
69 | |
70 | fprintf( |
71 | stderr, |
72 | format: "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 | |