san.c revision 1.1.1.1 1 #include "test/jemalloc_test.h"
2 #include "test/arena_util.h"
3 #include "test/san.h"
4
5 #include "jemalloc/internal/san.h"
6
7 static void
8 verify_extent_guarded(tsdn_t *tsdn, void *ptr) {
9 expect_true(extent_is_guarded(tsdn, ptr),
10 "All extents should be guarded.");
11 }
12
13 #define MAX_SMALL_ALLOCATIONS 4096
14 void *small_alloc[MAX_SMALL_ALLOCATIONS];
15
16 /*
17 * This test allocates page sized slabs and checks that every two slabs have
18 * at least one page in between them. That page is supposed to be the guard
19 * page.
20 */
21 TEST_BEGIN(test_guarded_small) {
22 test_skip_if(opt_prof);
23
24 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
25 unsigned npages = 16, pages_found = 0, ends_found = 0;
26 VARIABLE_ARRAY(uintptr_t, pages, npages);
27
28 /* Allocate to get sanitized pointers. */
29 size_t slab_sz = PAGE;
30 size_t sz = slab_sz / 8;
31 unsigned n_alloc = 0;
32 while (n_alloc < MAX_SMALL_ALLOCATIONS) {
33 void *ptr = malloc(sz);
34 expect_ptr_not_null(ptr, "Unexpected malloc() failure");
35 small_alloc[n_alloc] = ptr;
36 verify_extent_guarded(tsdn, ptr);
37 if ((uintptr_t)ptr % PAGE == 0) {
38 assert_u_lt(pages_found, npages,
39 "Unexpectedly large number of page aligned allocs");
40 pages[pages_found++] = (uintptr_t)ptr;
41 }
42 if (((uintptr_t)ptr + (uintptr_t)sz) % PAGE == 0) {
43 ends_found++;
44 }
45 n_alloc++;
46 if (pages_found == npages && ends_found == npages) {
47 break;
48 }
49 }
50 /* Should found the ptrs being checked for overflow and underflow. */
51 expect_u_eq(pages_found, npages, "Could not found the expected pages.");
52 expect_u_eq(ends_found, npages, "Could not found the expected pages.");
53
54 /* Verify the pages are not continuous, i.e. separated by guards. */
55 for (unsigned i = 0; i < npages - 1; i++) {
56 for (unsigned j = i + 1; j < npages; j++) {
57 uintptr_t ptr_diff = pages[i] > pages[j] ?
58 pages[i] - pages[j] : pages[j] - pages[i];
59 expect_zu_ge((size_t)ptr_diff, slab_sz + PAGE,
60 "There should be at least one pages between "
61 "guarded slabs");
62 }
63 }
64
65 for (unsigned i = 0; i < n_alloc + 1; i++) {
66 free(small_alloc[i]);
67 }
68 }
69 TEST_END
70
71 TEST_BEGIN(test_guarded_large) {
72 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
73 unsigned nlarge = 32;
74 VARIABLE_ARRAY(uintptr_t, large, nlarge);
75
76 /* Allocate to get sanitized pointers. */
77 size_t large_sz = SC_LARGE_MINCLASS;
78 for (unsigned i = 0; i < nlarge; i++) {
79 void *ptr = malloc(large_sz);
80 verify_extent_guarded(tsdn, ptr);
81 expect_ptr_not_null(ptr, "Unexpected malloc() failure");
82 large[i] = (uintptr_t)ptr;
83 }
84
85 /* Verify the pages are not continuous, i.e. separated by guards. */
86 for (unsigned i = 0; i < nlarge; i++) {
87 for (unsigned j = i + 1; j < nlarge; j++) {
88 uintptr_t ptr_diff = large[i] > large[j] ?
89 large[i] - large[j] : large[j] - large[i];
90 expect_zu_ge((size_t)ptr_diff, large_sz + 2 * PAGE,
91 "There should be at least two pages between "
92 " guarded large allocations");
93 }
94 }
95
96 for (unsigned i = 0; i < nlarge; i++) {
97 free((void *)large[i]);
98 }
99 }
100 TEST_END
101
102 static void
103 verify_pdirty(unsigned arena_ind, uint64_t expected) {
104 uint64_t pdirty = get_arena_pdirty(arena_ind);
105 expect_u64_eq(pdirty, expected / PAGE,
106 "Unexpected dirty page amount.");
107 }
108
109 static void
110 verify_pmuzzy(unsigned arena_ind, uint64_t expected) {
111 uint64_t pmuzzy = get_arena_pmuzzy(arena_ind);
112 expect_u64_eq(pmuzzy, expected / PAGE,
113 "Unexpected muzzy page amount.");
114 }
115
116 TEST_BEGIN(test_guarded_decay) {
117 unsigned arena_ind = do_arena_create(-1, -1);
118 do_decay(arena_ind);
119 do_purge(arena_ind);
120
121 verify_pdirty(arena_ind, 0);
122 verify_pmuzzy(arena_ind, 0);
123
124 /* Verify that guarded extents as dirty. */
125 size_t sz1 = PAGE, sz2 = PAGE * 2;
126 /* W/o maps_coalesce, guarded extents are unguarded eagerly. */
127 size_t add_guard_size = maps_coalesce ? 0 : SAN_PAGE_GUARDS_SIZE;
128 generate_dirty(arena_ind, sz1);
129 verify_pdirty(arena_ind, sz1 + add_guard_size);
130 verify_pmuzzy(arena_ind, 0);
131
132 /* Should reuse the first extent. */
133 generate_dirty(arena_ind, sz1);
134 verify_pdirty(arena_ind, sz1 + add_guard_size);
135 verify_pmuzzy(arena_ind, 0);
136
137 /* Should not reuse; expect new dirty pages. */
138 generate_dirty(arena_ind, sz2);
139 verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
140 verify_pmuzzy(arena_ind, 0);
141
142 tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
143 int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
144
145 /* Should reuse dirty extents for the two mallocx. */
146 void *p1 = do_mallocx(sz1, flags);
147 verify_extent_guarded(tsdn, p1);
148 verify_pdirty(arena_ind, sz2 + add_guard_size);
149
150 void *p2 = do_mallocx(sz2, flags);
151 verify_extent_guarded(tsdn, p2);
152 verify_pdirty(arena_ind, 0);
153 verify_pmuzzy(arena_ind, 0);
154
155 dallocx(p1, flags);
156 verify_pdirty(arena_ind, sz1 + add_guard_size);
157 dallocx(p2, flags);
158 verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
159 verify_pmuzzy(arena_ind, 0);
160
161 do_purge(arena_ind);
162 verify_pdirty(arena_ind, 0);
163 verify_pmuzzy(arena_ind, 0);
164
165 if (config_stats) {
166 expect_u64_eq(get_arena_npurge(arena_ind), 1,
167 "Expected purging to occur");
168 expect_u64_eq(get_arena_dirty_npurge(arena_ind), 1,
169 "Expected purging to occur");
170 expect_u64_eq(get_arena_dirty_purged(arena_ind),
171 (sz1 + sz2 + 2 * add_guard_size) / PAGE,
172 "Expected purging to occur");
173 expect_u64_eq(get_arena_muzzy_npurge(arena_ind), 0,
174 "Expected purging to occur");
175 }
176
177 if (opt_retain) {
178 /*
179 * With retain, guarded extents are not mergable and will be
180 * cached in ecache_retained. They should be reused.
181 */
182 void *new_p1 = do_mallocx(sz1, flags);
183 verify_extent_guarded(tsdn, p1);
184 expect_ptr_eq(p1, new_p1, "Expect to reuse p1");
185
186 void *new_p2 = do_mallocx(sz2, flags);
187 verify_extent_guarded(tsdn, p2);
188 expect_ptr_eq(p2, new_p2, "Expect to reuse p2");
189
190 dallocx(new_p1, flags);
191 verify_pdirty(arena_ind, sz1 + add_guard_size);
192 dallocx(new_p2, flags);
193 verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
194 verify_pmuzzy(arena_ind, 0);
195 }
196
197 do_arena_destroy(arena_ind);
198 }
199 TEST_END
200
201 int
202 main(void) {
203 return test(
204 test_guarded_small,
205 test_guarded_large,
206 test_guarded_decay);
207 }
208