1 /* Test that explicit_bzero block clears are not optimized out.
2 Copyright (C) 2016-2020 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19 /* This test is conceptually based on a test designed by Matthew
20 Dempsky for the OpenBSD regression suite:
21 <openbsd>/src/regress/lib/libc/explicit_bzero/explicit_bzero.c.
22 The basic idea is, we have a function that contains a
23 block-clearing operation (not necessarily explicit_bzero), after
24 which the block is dead, in the compiler-jargon sense. Execute
25 that function while running on a user-allocated alternative
26 stack. Then we have another pointer to the memory region affected
27 by the block clear -- namely, the original allocation for the
28 alternative stack -- and can find out whether it actually happened.
29
30 The OpenBSD test uses sigaltstack and SIGUSR1 to get onto an
31 alternative stack. This causes a number of awkward problems; some
32 operating systems (e.g. Solaris and OSX) wipe the signal stack upon
33 returning to the normal stack, there's no way to be sure that other
34 processes running on the same system will not interfere, and the
35 signal stack is very small so it's not safe to call printf there.
36 This implementation instead uses the <ucontext.h> coroutine
37 interface. The coroutine stack is still too small to safely use
38 printf, but we know the OS won't erase it, so we can do all the
39 checks and printing from the normal stack. */
40
41 #include "crypt-port.h"
42
43 #ifndef USE_SWAPCONTEXT
44 /* We can't do this test if we don't have the ucontext API. */
45 int main(void)
46 {
47 return 77;
48 }
49 #else
50
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <ucontext.h>
54
55 #ifdef HAVE_VALGRIND_VALGRIND_H
56 # include <valgrind/valgrind.h>
57 # include <valgrind/memcheck.h>
58 #else
59 # define VALGRIND_STACK_REGISTER(start, end) do {} while (0)
60 # define VALGRIND_MAKE_MEM_DEFINED(addr, len) do {} while (0)
61 #endif
62
63 /* A byte pattern that is unlikely to occur by chance: the first 16
64 prime numbers (OEIS A000040). */
65 static const unsigned char test_pattern[16] =
66 {
67 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
68 };
69
70 /* Immediately after each subtest returns, we call swapcontext to get
71 back onto the main stack. That call might itself overwrite the
72 test pattern, so we fill a modest-sized buffer with copies of it
73 and check whether any of them survived. */
74
75 #define PATTERN_SIZE (sizeof test_pattern)
76 #define PATTERN_REPS 32
77 #define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
78
79 /* There are three subtests, two of which are sanity checks.
80 Each test follows this sequence:
81
82 main coroutine
83 ---- --------
84 advance cur_subtest
85 swap
86 call setup function
87 prepare test buffer
88 swap
89 verify that buffer
90 was filled in
91 swap
92 possibly clear buffer
93 return
94 swap
95 check buffer again,
96 according to test
97 expectation
98
99 In the "no_clear" case, we don't do anything to the test buffer
100 between preparing it and letting it go out of scope, and we expect
101 to find it. This confirms that the test buffer does get filled in
102 and we can find it from the stack buffer. In the "ordinary_clear"
103 case, we clear it using memset. Depending on the target, the
104 compiler may not be able to apply dead store elimination to the
105 memset call, so the test does not fail if the memset is not
106 eliminated. Finally, the "explicit_clear" case uses explicit_bzero
107 and expects _not_ to find the test buffer, which is the real
108 test. */
109
110 static ucontext_t uc_main, uc_co;
111
112 static NO_INLINE int
113 use_test_buffer (unsigned char *buf)
114 {
115 unsigned int sum = 0;
116
117 for (unsigned int i = 0; i < PATTERN_REPS; i++)
118 sum += buf[i * PATTERN_SIZE];
119
120 return (sum == 2 * PATTERN_REPS) ? 0 : 1;
121 }
122
123 /* Always check the test buffer immediately after filling it; this
124 makes externally visible side effects depend on the buffer existing
125 and having been filled in. */
126 static inline void
127 prepare_test_buffer (unsigned char *buf)
128 {
129 for (unsigned int i = 0; i < PATTERN_REPS; i++)
130 memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
131
132 if (swapcontext (&uc_co, &uc_main))
133 abort ();
134
135 /* Force the compiler to really copy the pattern to buf. */
136 if (use_test_buffer (buf))
137 abort ();
138 }
139
140 static void
141 setup_no_clear (void)
142 {
143 unsigned char buf[TEST_BUFFER_SIZE];
144 prepare_test_buffer (buf);
145 }
146
147 static void
148 setup_ordinary_clear (void)
149 {
150 unsigned char buf[TEST_BUFFER_SIZE];
151 prepare_test_buffer (buf);
152 memset (buf, 0, TEST_BUFFER_SIZE);
153 }
154
155 static void
156 setup_explicit_clear (void)
157 {
158 unsigned char buf[TEST_BUFFER_SIZE];
159 prepare_test_buffer (buf);
160 explicit_bzero (buf, TEST_BUFFER_SIZE);
161 }
162
163 enum test_expectation
164 {
165 EXPECT_NONE = 1,
166 EXPECT_SOME,
167 EXPECT_ALL,
168 NO_EXPECTATIONS,
169 ARRAY_END = 0
170 };
171 struct subtest
172 {
173 void (*setup_subtest) (void);
174 const char *label;
175 enum test_expectation expected;
176 };
177 static const struct subtest *cur_subtest;
178
179 static const struct subtest subtests[] =
180 {
181 { setup_no_clear, "no clear", EXPECT_SOME },
182 /* The memset may happen or not, depending on compiler
183 optimizations. */
184 { setup_ordinary_clear, "ordinary clear", NO_EXPECTATIONS },
185 { setup_explicit_clear, "explicit clear", EXPECT_NONE },
186 { 0, 0, ARRAY_END }
187 };
188
189 static void
190 test_coroutine (void)
191 {
192 while (cur_subtest->setup_subtest)
193 {
194 cur_subtest->setup_subtest ();
195 if (swapcontext (&uc_co, &uc_main))
196 abort ();
197 }
198 }
199
200 /* All the code above this point runs on the coroutine stack.
201 All the code below this point runs on the main stack. */
202
203 static int test_status;
204 static unsigned char *co_stack_buffer;
205 static size_t co_stack_size;
206
207 static unsigned int
208 count_test_patterns (unsigned char *buf, size_t bufsiz)
209 {
210 VALGRIND_MAKE_MEM_DEFINED (buf, bufsiz);
211 unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
212 if (!first)
213 return 0;
214 unsigned int cnt = 0;
215 for (unsigned int i = 0; i < PATTERN_REPS; i++)
216 {
217 unsigned char *p = first + i*PATTERN_SIZE;
218 if (p + PATTERN_SIZE - buf > (ptrdiff_t)bufsiz)
219 break;
220 if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
221 cnt++;
222 }
223 return cnt;
224 }
225
226 static void
227 check_test_buffer (enum test_expectation expected,
228 const char *label, const char *stage)
229 {
230 unsigned int cnt = count_test_patterns (co_stack_buffer, co_stack_size);
231 switch (expected)
232 {
233 case EXPECT_NONE:
234 if (cnt == 0)
235 printf ("PASS: %s/%s: expected 0 got %u\n", label, stage, cnt);
236 else
237 {
238 printf ("FAIL: %s/%s: expected 0 got %u\n", label, stage, cnt);
239 test_status = 1;
240 }
241 break;
242
243 case EXPECT_SOME:
244 if (cnt > 0)
245 printf ("PASS: %s/%s: expected some got %u\n", label, stage, cnt);
246 else
247 {
248 printf ("FAIL: %s/%s: expected some got 0\n", label, stage);
249 test_status = 1;
250 }
251 break;
252
253 case EXPECT_ALL:
254 if (cnt == PATTERN_REPS)
255 printf ("PASS: %s/%s: expected %d got %u\n", label, stage,
256 PATTERN_REPS, cnt);
257 else
258 {
259 printf ("FAIL: %s/%s: expected %d got %u\n", label, stage,
260 PATTERN_REPS, cnt);
261 test_status = 1;
262 }
263 break;
264
265 case NO_EXPECTATIONS:
266 printf ("INFO: %s/%s: found %u patterns%s\n", label, stage, cnt,
267 cnt == 0 ? " (memset not eliminated)" : "");
268 break;
269
270 default:
271 printf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
272 label, stage, (int)expected);
273 test_status = 1;
274 }
275 }
276
277 static void
278 test_loop (void)
279 {
280 cur_subtest = subtests;
281 while (cur_subtest->setup_subtest)
282 {
283 if (swapcontext (&uc_main, &uc_co))
284 abort ();
285 check_test_buffer (EXPECT_ALL, cur_subtest->label, "prepare");
286 if (swapcontext (&uc_main, &uc_co))
287 abort ();
288 check_test_buffer (cur_subtest->expected, cur_subtest->label, "test");
289 cur_subtest++;
290 }
291 /* Terminate the coroutine. */
292 if (swapcontext (&uc_main, &uc_co))
293 abort ();
294 }
295
296 int
297 main (void)
298 {
299 size_t page_alignment = sizeof (void *);
300 long page_alignment_l = sysconf (_SC_PAGESIZE);
301 if (page_alignment_l > (long) sizeof (void *))
302 page_alignment = (size_t) page_alignment_l;
303
304 co_stack_size = (size_t) SIGSTKSZ + (size_t) TEST_BUFFER_SIZE;
305 if (co_stack_size < page_alignment * 4)
306 co_stack_size = page_alignment * 4;
307
308 void *p;
309 int err = posix_memalign (&p, page_alignment, co_stack_size);
310 if (err || !p)
311 {
312 printf ("ERROR: allocating alt stack: %s\n", strerror (err));
313 return 2;
314 }
315 co_stack_buffer = p;
316 memset (co_stack_buffer, 0, co_stack_size);
317 VALGRIND_STACK_REGISTER (co_stack_buffer, co_stack_buffer + co_stack_size);
318
319 if (getcontext (&uc_co))
320 {
321 printf ("ERROR: allocating coroutine context: %s\n", strerror (err));
322 return 2;
323 }
324 uc_co.uc_stack.ss_sp = co_stack_buffer;
325 uc_co.uc_stack.ss_size = co_stack_size;
326 uc_co.uc_link = &uc_main;
327 makecontext (&uc_co, test_coroutine, 0);
328
329 test_loop ();
330 return test_status;
331 }
332
333 #endif /* have ucontext.h */