1 | /* Test of explicit_bzero() function.
|
---|
2 | Copyright (C) 2020-2022 Free Software Foundation, Inc.
|
---|
3 |
|
---|
4 | This program is free software: you can redistribute it and/or modify
|
---|
5 | it under the terms of the GNU General Public License as published by
|
---|
6 | the Free Software Foundation, either version 3 of the License, or
|
---|
7 | (at your option) any later version.
|
---|
8 |
|
---|
9 | This program is distributed in the hope that it will be useful,
|
---|
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
12 | GNU General Public License for more details.
|
---|
13 |
|
---|
14 | You should have received a copy of the GNU General Public License
|
---|
15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
---|
16 |
|
---|
17 | /* Written by Bruno Haible <[email protected]>, 2020. */
|
---|
18 |
|
---|
19 | #include <config.h>
|
---|
20 |
|
---|
21 | /* Specification. */
|
---|
22 | #include <string.h>
|
---|
23 |
|
---|
24 | #include "signature.h"
|
---|
25 | SIGNATURE_CHECK (explicit_bzero, void, (void *, size_t));
|
---|
26 |
|
---|
27 | #include <stdio.h>
|
---|
28 | #include <stdint.h>
|
---|
29 | #include <stdlib.h>
|
---|
30 |
|
---|
31 | #include "vma-iter.h"
|
---|
32 | #include "macros.h"
|
---|
33 |
|
---|
34 | #define SECRET "xyzzy1729"
|
---|
35 | #define SECRET_SIZE 9
|
---|
36 |
|
---|
37 | static char zero[SECRET_SIZE] = { 0 };
|
---|
38 |
|
---|
39 | /* Enable this to verify that the test is effective. */
|
---|
40 | #if 0
|
---|
41 | # define explicit_bzero(a, n) memset (a, '\0', n)
|
---|
42 | #endif
|
---|
43 |
|
---|
44 | /* =================== Verify operation on static memory =================== */
|
---|
45 |
|
---|
46 | static char stbuf[SECRET_SIZE];
|
---|
47 |
|
---|
48 | static void
|
---|
49 | test_static (void)
|
---|
50 | {
|
---|
51 | memcpy (stbuf, SECRET, SECRET_SIZE);
|
---|
52 | explicit_bzero (stbuf, SECRET_SIZE);
|
---|
53 | ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
|
---|
54 | }
|
---|
55 |
|
---|
56 | /* =============== Verify operation on heap-allocated memory =============== */
|
---|
57 |
|
---|
58 | /* Test whether an address range is mapped in memory. */
|
---|
59 | #if VMA_ITERATE_SUPPORTED
|
---|
60 |
|
---|
61 | struct locals
|
---|
62 | {
|
---|
63 | uintptr_t range_start;
|
---|
64 | uintptr_t range_end;
|
---|
65 | };
|
---|
66 |
|
---|
67 | static int
|
---|
68 | vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
|
---|
69 | unsigned int flags)
|
---|
70 | {
|
---|
71 | struct locals *lp = (struct locals *) data;
|
---|
72 |
|
---|
73 | /* Remove from [range_start, range_end) the part at the beginning or at the
|
---|
74 | end that is covered by [start, end). */
|
---|
75 | if (start <= lp->range_start && end > lp->range_start)
|
---|
76 | lp->range_start = (end < lp->range_end ? end : lp->range_end);
|
---|
77 | if (start < lp->range_end && end >= lp->range_end)
|
---|
78 | lp->range_end = (start > lp->range_start ? start : lp->range_start);
|
---|
79 |
|
---|
80 | return 0;
|
---|
81 | }
|
---|
82 |
|
---|
83 | static bool
|
---|
84 | is_range_mapped (uintptr_t range_start, uintptr_t range_end)
|
---|
85 | {
|
---|
86 | struct locals l;
|
---|
87 |
|
---|
88 | l.range_start = range_start;
|
---|
89 | l.range_end = range_end;
|
---|
90 | vma_iterate (vma_iterate_callback, &l);
|
---|
91 | return l.range_start == l.range_end;
|
---|
92 | }
|
---|
93 |
|
---|
94 | #else
|
---|
95 |
|
---|
96 | static bool
|
---|
97 | is_range_mapped (uintptr_t range_start, uintptr_t range_end)
|
---|
98 | {
|
---|
99 | return true;
|
---|
100 | }
|
---|
101 |
|
---|
102 | #endif
|
---|
103 |
|
---|
104 | static void
|
---|
105 | test_heap (void)
|
---|
106 | {
|
---|
107 | char *heapbuf = (char *) malloc (SECRET_SIZE);
|
---|
108 | ASSERT (heapbuf);
|
---|
109 | uintptr_t volatile addr = (uintptr_t) heapbuf;
|
---|
110 | memcpy (heapbuf, SECRET, SECRET_SIZE);
|
---|
111 | explicit_bzero (heapbuf, SECRET_SIZE);
|
---|
112 | free (heapbuf);
|
---|
113 | heapbuf = (char *) addr;
|
---|
114 | if (is_range_mapped (addr, addr + SECRET_SIZE))
|
---|
115 | {
|
---|
116 | /* some implementation could override freed memory by canaries so
|
---|
117 | compare against secret */
|
---|
118 | ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0);
|
---|
119 | printf ("test_heap: address range is still mapped after free().\n");
|
---|
120 | }
|
---|
121 | else
|
---|
122 | printf ("test_heap: address range is unmapped after free().\n");
|
---|
123 | }
|
---|
124 |
|
---|
125 | /* =============== Verify operation on stack-allocated memory =============== */
|
---|
126 |
|
---|
127 | /* There are two passes:
|
---|
128 | 1. Put a secret in memory and invoke explicit_bzero on it.
|
---|
129 | 2. Verify that the memory has been erased.
|
---|
130 | Implement them in the same function, so that they access the same memory
|
---|
131 | range on the stack. Declare the local scalars to be volatile so they
|
---|
132 | are not optimized away. That way, the test verifies that the compiler
|
---|
133 | does not eliminate a call to explicit_bzero, even if data flow analysis
|
---|
134 | reveals that the stack area is dead at the end of the function. */
|
---|
135 | static bool _GL_ATTRIBUTE_NOINLINE
|
---|
136 | do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
|
---|
137 | {
|
---|
138 | char stackbuf[SECRET_SIZE];
|
---|
139 | if (pass == 1)
|
---|
140 | {
|
---|
141 | memcpy (stackbuf, SECRET, SECRET_SIZE);
|
---|
142 | explicit_bzero (stackbuf, SECRET_SIZE);
|
---|
143 | *last_stackbuf = stackbuf;
|
---|
144 | return false;
|
---|
145 | }
|
---|
146 | else /* pass == 2 */
|
---|
147 | {
|
---|
148 | /* Use *last_stackbuf here, because stackbuf may be allocated at a
|
---|
149 | different address than *last_stackbuf. This can happen
|
---|
150 | when the compiler splits this function into different functions,
|
---|
151 | one for pass == 1 and one for pass != 1. */
|
---|
152 | return memcmp (zero, *last_stackbuf, SECRET_SIZE) != 0;
|
---|
153 | }
|
---|
154 | }
|
---|
155 |
|
---|
156 | static void
|
---|
157 | test_stack (void)
|
---|
158 | {
|
---|
159 | int count = 0;
|
---|
160 | int repeat;
|
---|
161 | char *volatile last_stackbuf;
|
---|
162 |
|
---|
163 | for (repeat = 2 * 1000; repeat > 0; repeat--)
|
---|
164 | {
|
---|
165 | /* This odd way of writing two consecutive statements
|
---|
166 | do_secret_stuff (1, &last_stackbuf);
|
---|
167 | count += do_secret_stuff (2, &last_stackbuf);
|
---|
168 | ensures that the two do_secret_stuff calls are performed with the same
|
---|
169 | stack pointer value, on m68k. */
|
---|
170 | if ((repeat % 2) == 0)
|
---|
171 | do_secret_stuff (1, &last_stackbuf);
|
---|
172 | else
|
---|
173 | count += do_secret_stuff (2, &last_stackbuf);
|
---|
174 | }
|
---|
175 | /* If explicit_bzero works, count is near 0. (It may be > 0 if there were
|
---|
176 | some asynchronous signal invocations between the two calls of
|
---|
177 | do_secret_stuff.)
|
---|
178 | If explicit_bzero is optimized away by the compiler, count comes out as
|
---|
179 | approximately 1000. */
|
---|
180 | printf ("test_stack: count = %d\n", count);
|
---|
181 | ASSERT (count < 50);
|
---|
182 | }
|
---|
183 |
|
---|
184 | /* ========================================================================== */
|
---|
185 |
|
---|
186 | int
|
---|
187 | main ()
|
---|
188 | {
|
---|
189 | test_static ();
|
---|
190 | test_heap ();
|
---|
191 | test_stack ();
|
---|
192 |
|
---|
193 | return 0;
|
---|
194 | }
|
---|