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