]> git.ipfire.org Git - thirdparty/glibc.git/blame - string/tst-xbzero-opt.c
Update install.texi latest GCC version known to work.
[thirdparty/glibc.git] / string / tst-xbzero-opt.c
CommitLineData
ea1bd74d 1/* Test that explicit_bzero block clears are not optimized out.
bfff8b1b 2 Copyright (C) 2016-2017 Free Software Foundation, Inc.
ea1bd74d
ZW
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 <http://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#define _GNU_SOURCE 1
42
43#include <errno.h>
44#include <signal.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <ucontext.h>
49#include <unistd.h>
50
51/* A byte pattern that is unlikely to occur by chance: the first 16
52 prime numbers (OEIS A000040). */
53static const unsigned char test_pattern[16] =
54{
55 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53
56};
57
58/* Immediately after each subtest returns, we call swapcontext to get
59 back onto the main stack. That call might itself overwrite the
60 test pattern, so we fill a modest-sized buffer with copies of it
61 and check whether any of them survived. */
62
63#define PATTERN_SIZE (sizeof test_pattern)
64#define PATTERN_REPS 32
65#define TEST_BUFFER_SIZE (PATTERN_SIZE * PATTERN_REPS)
66
67/* There are three subtests, two of which are sanity checks.
68 Each test follows this sequence:
69
70 main coroutine
71 ---- --------
72 advance cur_subtest
73 swap
74 call setup function
75 prepare test buffer
76 swap
77 verify that buffer
78 was filled in
79 swap
80 possibly clear buffer
81 return
82 swap
83 check buffer again,
84 according to test
85 expectation
86
87 In the "no_clear" case, we don't do anything to the test buffer
88 between preparing it and letting it go out of scope, and we expect
89 to find it. This confirms that the test buffer does get filled in
90 and we can find it from the stack buffer. In the "ordinary_clear"
8d71242e
FW
91 case, we clear it using memset. Depending on the target, the
92 compiler may not be able to apply dead store elimination to the
93 memset call, so the test does not fail if the memset is not
94 eliminated. Finally, the "explicit_clear" case uses explicit_bzero
95 and expects _not_ to find the test buffer, which is the real
96 test. */
ea1bd74d
ZW
97
98static ucontext_t uc_main, uc_co;
99
100/* Always check the test buffer immediately after filling it; this
101 makes externally visible side effects depend on the buffer existing
102 and having been filled in. */
8d71242e 103static inline __attribute__ ((always_inline)) void
ea1bd74d
ZW
104prepare_test_buffer (unsigned char *buf)
105{
106 for (unsigned int i = 0; i < PATTERN_REPS; i++)
107 memcpy (buf + i*PATTERN_SIZE, test_pattern, PATTERN_SIZE);
108
109 if (swapcontext (&uc_co, &uc_main))
110 abort ();
111}
112
113static void
114setup_no_clear (void)
115{
116 unsigned char buf[TEST_BUFFER_SIZE];
117 prepare_test_buffer (buf);
118}
119
120static void
121setup_ordinary_clear (void)
122{
123 unsigned char buf[TEST_BUFFER_SIZE];
124 prepare_test_buffer (buf);
125 memset (buf, 0, TEST_BUFFER_SIZE);
126}
127
128static void
129setup_explicit_clear (void)
130{
131 unsigned char buf[TEST_BUFFER_SIZE];
132 prepare_test_buffer (buf);
133 explicit_bzero (buf, TEST_BUFFER_SIZE);
134}
135
8d71242e
FW
136enum test_expectation
137 {
138 EXPECT_NONE, EXPECT_SOME, EXPECT_ALL, NO_EXPECTATIONS
139 };
ea1bd74d
ZW
140struct subtest
141{
142 void (*setup_subtest) (void);
143 const char *label;
144 enum test_expectation expected;
145};
146static const struct subtest *cur_subtest;
147
148static const struct subtest subtests[] =
149{
150 { setup_no_clear, "no clear", EXPECT_SOME },
8d71242e
FW
151 /* The memset may happen or not, depending on compiler
152 optimizations. */
153 { setup_ordinary_clear, "ordinary clear", NO_EXPECTATIONS },
ea1bd74d
ZW
154 { setup_explicit_clear, "explicit clear", EXPECT_NONE },
155 { 0, 0, -1 }
156};
157
158static void
159test_coroutine (void)
160{
161 while (cur_subtest->setup_subtest)
162 {
163 cur_subtest->setup_subtest ();
164 if (swapcontext (&uc_co, &uc_main))
165 abort ();
166 }
167}
168
169/* All the code above this point runs on the coroutine stack.
170 All the code below this point runs on the main stack. */
171
172static int test_status;
173static unsigned char *co_stack_buffer;
174static size_t co_stack_size;
175
176static unsigned int
177count_test_patterns (unsigned char *buf, size_t bufsiz)
178{
179 unsigned char *first = memmem (buf, bufsiz, test_pattern, PATTERN_SIZE);
180 if (!first)
181 return 0;
182 unsigned int cnt = 0;
183 for (unsigned int i = 0; i < PATTERN_REPS; i++)
184 {
185 unsigned char *p = first + i*PATTERN_SIZE;
186 if (p + PATTERN_SIZE - buf > bufsiz)
187 break;
188 if (memcmp (p, test_pattern, PATTERN_SIZE) == 0)
189 cnt++;
190 }
191 return cnt;
192}
193
194static void
195check_test_buffer (enum test_expectation expected,
196 const char *label, const char *stage)
197{
198 unsigned int cnt = count_test_patterns (co_stack_buffer, co_stack_size);
199 switch (expected)
200 {
201 case EXPECT_NONE:
202 if (cnt == 0)
203 printf ("PASS: %s/%s: expected 0 got %d\n", label, stage, cnt);
204 else
205 {
206 printf ("FAIL: %s/%s: expected 0 got %d\n", label, stage, cnt);
207 test_status = 1;
208 }
209 break;
210
211 case EXPECT_SOME:
212 if (cnt > 0)
213 printf ("PASS: %s/%s: expected some got %d\n", label, stage, cnt);
214 else
215 {
216 printf ("FAIL: %s/%s: expected some got 0\n", label, stage);
217 test_status = 1;
218 }
219 break;
220
221 case EXPECT_ALL:
222 if (cnt == PATTERN_REPS)
223 printf ("PASS: %s/%s: expected %d got %d\n", label, stage,
224 PATTERN_REPS, cnt);
225 else
226 {
227 printf ("FAIL: %s/%s: expected %d got %d\n", label, stage,
228 PATTERN_REPS, cnt);
229 test_status = 1;
230 }
231 break;
232
8d71242e
FW
233 case NO_EXPECTATIONS:
234 printf ("INFO: %s/%s: found %d patterns%s\n", label, stage, cnt,
235 cnt == 0 ? " (memset not eliminated)" : "");
236 break;
237
ea1bd74d
ZW
238 default:
239 printf ("ERROR: %s/%s: invalid value for 'expected' = %d\n",
240 label, stage, (int)expected);
241 test_status = 1;
242 }
243}
244
245static void
246test_loop (void)
247{
248 cur_subtest = subtests;
249 while (cur_subtest->setup_subtest)
250 {
251 if (swapcontext (&uc_main, &uc_co))
252 abort ();
253 check_test_buffer (EXPECT_ALL, cur_subtest->label, "prepare");
254 if (swapcontext (&uc_main, &uc_co))
255 abort ();
256 check_test_buffer (cur_subtest->expected, cur_subtest->label, "test");
257 cur_subtest++;
258 }
259 /* Terminate the coroutine. */
260 if (swapcontext (&uc_main, &uc_co))
261 abort ();
262}
263
264int
265do_test (void)
266{
267 size_t page_alignment = sysconf (_SC_PAGESIZE);
268 if (page_alignment < sizeof (void *))
269 page_alignment = sizeof (void *);
270
271 co_stack_size = SIGSTKSZ + TEST_BUFFER_SIZE;
272 if (co_stack_size < page_alignment * 4)
273 co_stack_size = page_alignment * 4;
274
275 void *p;
276 int err = posix_memalign (&p, page_alignment, co_stack_size);
277 if (err || !p)
278 {
279 printf ("ERROR: allocating alt stack: %s\n", strerror (err));
280 return 2;
281 }
282 co_stack_buffer = p;
283
284 if (getcontext (&uc_co))
285 {
286 printf ("ERROR: allocating coroutine context: %s\n", strerror (err));
287 return 2;
288 }
289 uc_co.uc_stack.ss_sp = co_stack_buffer;
290 uc_co.uc_stack.ss_size = co_stack_size;
291 uc_co.uc_link = &uc_main;
292 makecontext (&uc_co, test_coroutine, 0);
293
294 test_loop ();
295 return test_status;
296}
297
298#include <support/test-driver.c>