1 /* Test memset_explicit.
2 Copyright 2020-2023 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 <bruno@clisp.org>, 2020. */
18 /* Adapted for memset_explicit by Paul Eggert <eggert@cs.ucla.edu>, 2022. */
19
20 #include <config.h>
21
22 /* Specification. */
23 #include <string.h>
24
25 #include "signature.h"
26 SIGNATURE_CHECK (memset_explicit, void *, (void *, int, size_t));
27
28 #include <limits.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <stdlib.h>
32
33 #include "vma-iter.h"
34 #include "macros.h"
35
36 #define SECRET "xyzzy1729"
37 #define SECRET_SIZE 9
38
39 static char zero[SECRET_SIZE] = { 0 };
40
41 /* Enable this to verify that the test is effective. */
42 #if 0
43 # define memset_explicit(a, c, n) memset (a, c, n)
44 #endif
45
46 /* =================== Verify operation on static memory =================== */
47
48 static char stbuf[SECRET_SIZE];
49
50 static void
51 test_static (void)
52 {
53 memcpy (stbuf, SECRET, SECRET_SIZE);
54 memset_explicit (stbuf, 0, SECRET_SIZE);
55 ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
56 for (int i = 1; i <= UCHAR_MAX; i++)
57 {
58 char checkbuf[SECRET_SIZE];
59 memset (checkbuf, i, SECRET_SIZE);
60 memcpy (stbuf, SECRET, SECRET_SIZE);
61 memset_explicit (stbuf, i, SECRET_SIZE);
62 ASSERT (memcmp (checkbuf, stbuf, SECRET_SIZE) == 0);
63 }
64 }
65
66 /* =============== Verify operation on heap-allocated memory =============== */
67
68 /* Test whether an address range is mapped in memory. */
69 #if VMA_ITERATE_SUPPORTED
70
71 struct locals
72 {
73 uintptr_t range_start;
74 uintptr_t range_end;
75 };
76
77 static int
78 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
79 unsigned int flags)
80 {
81 struct locals *lp = (struct locals *) data;
82
83 /* Remove from [range_start, range_end) the part at the beginning or at the
84 end that is covered by [start, end). */
85 if (start <= lp->range_start && end > lp->range_start)
86 lp->range_start = (end < lp->range_end ? end : lp->range_end);
87 if (start < lp->range_end && end >= lp->range_end)
88 lp->range_end = (start > lp->range_start ? start : lp->range_start);
89
90 return 0;
91 }
92
93 static bool
94 is_range_mapped (uintptr_t range_start, uintptr_t range_end)
95 {
96 struct locals l;
97
98 l.range_start = range_start;
99 l.range_end = range_end;
100 vma_iterate (vma_iterate_callback, &l);
101 return l.range_start == l.range_end;
102 }
103
104 #else
105
106 static bool
107 is_range_mapped (uintptr_t range_start, uintptr_t range_end)
108 {
109 return true;
110 }
111
112 #endif
113
114 static void
115 test_heap (void)
116 {
117 char *heapbuf = (char *) malloc (SECRET_SIZE);
118 ASSERT (heapbuf);
119 uintptr_t volatile addr = (uintptr_t) heapbuf;
120 memcpy (heapbuf, SECRET, SECRET_SIZE);
121 memset_explicit (heapbuf, 0, SECRET_SIZE);
122 free (heapbuf);
123 heapbuf = (char *) addr;
124 if (is_range_mapped (addr, addr + SECRET_SIZE))
125 {
126 /* some implementation could override freed memory by canaries so
127 compare against secret */
128 ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0);
129 printf ("test_heap: address range is still mapped after free().\n");
130 }
131 else
132 printf ("test_heap: address range is unmapped after free().\n");
133 }
134
135 /* =============== Verify operation on stack-allocated memory =============== */
136
137 /* There are two passes:
138 1. Put a secret in memory and invoke memset_explicit on it.
139 2. Verify that the memory has been erased.
140 Implement them in the same function, so that they access the same memory
141 range on the stack. Declare the local scalars to be volatile so they
142 are not optimized away. That way, the test verifies that the compiler
143 does not eliminate a call to memset_explicit, even if data flow analysis
144 reveals that the stack area is dead at the end of the function. */
145 static bool _GL_ATTRIBUTE_NOINLINE
146 do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
147 {
148 char stackbuf[SECRET_SIZE];
149 if (pass == 1)
150 {
151 memcpy (stackbuf, SECRET, SECRET_SIZE);
152 memset_explicit (stackbuf, 0, SECRET_SIZE);
153 *last_stackbuf = stackbuf;
154 return false;
155 }
156 else /* pass == 2 */
157 {
158 /* Use *last_stackbuf here, because stackbuf may be allocated at a
159 different address than *last_stackbuf. This can happen
160 when the compiler splits this function into different functions,
161 one for pass == 1 and one for pass != 1. */
162 return memcmp (zero, *last_stackbuf, SECRET_SIZE) != 0;
163 }
164 }
165
166 static void
167 test_stack (void)
168 {
169 int count = 0;
170 int repeat;
171 char *volatile last_stackbuf;
172
173 for (repeat = 2 * 1000; repeat > 0; repeat--)
174 {
175 /* This odd way of writing two consecutive statements
176 do_secret_stuff (1, &last_stackbuf);
177 count += do_secret_stuff (2, &last_stackbuf);
178 ensures that the two do_secret_stuff calls are performed with the same
179 stack pointer value, on m68k. */
180 if ((repeat % 2) == 0)
181 do_secret_stuff (1, &last_stackbuf);
182 else
183 count += do_secret_stuff (2, &last_stackbuf);
184 }
185 /* If memset_explicit works, count is near 0. (It may be > 0 if there were
186 some asynchronous signal invocations between the two calls of
187 do_secret_stuff.)
188 If memset_explicit is optimized away by the compiler, count comes out as
189 approximately 1000. */
190 printf ("test_stack: count = %d\n", count);
191 ASSERT (count < 50);
192 }
193
194 /* ========================================================================== */
195
196 int
197 main ()
198 {
199 test_static ();
200 test_heap ();
201 test_stack ();
202
203 return 0;
204 }