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 /* Suppress GCC 13.2.1 false alarm, as this test needs a dangling pointer. */
47 #if 12 <= __GNUC__
48 # pragma GCC diagnostic ignored "-Wdangling-pointer"
49 #endif
50
51 /* =================== Verify operation on static memory =================== */
52
53 static char stbuf[SECRET_SIZE];
54
55 static void
56 test_static (void)
57 {
58 memcpy (stbuf, SECRET, SECRET_SIZE);
59 memset_explicit (stbuf, 0, SECRET_SIZE);
60 ASSERT (memcmp (zero, stbuf, SECRET_SIZE) == 0);
61 for (int i = 1; i <= UCHAR_MAX; i++)
62 {
63 char checkbuf[SECRET_SIZE];
64 memset (checkbuf, i, SECRET_SIZE);
65 memcpy (stbuf, SECRET, SECRET_SIZE);
66 memset_explicit (stbuf, i, SECRET_SIZE);
67 ASSERT (memcmp (checkbuf, stbuf, SECRET_SIZE) == 0);
68 }
69 }
70
71 /* =============== Verify operation on heap-allocated memory =============== */
72
73 /* Test whether an address range is mapped in memory. */
74 #if VMA_ITERATE_SUPPORTED
75
76 struct locals
77 {
78 uintptr_t range_start;
79 uintptr_t range_end;
80 };
81
82 static int
83 vma_iterate_callback (void *data, uintptr_t start, uintptr_t end,
84 unsigned int flags)
85 {
86 struct locals *lp = (struct locals *) data;
87
88 /* Remove from [range_start, range_end) the part at the beginning or at the
89 end that is covered by [start, end). */
90 if (start <= lp->range_start && end > lp->range_start)
91 lp->range_start = (end < lp->range_end ? end : lp->range_end);
92 if (start < lp->range_end && end >= lp->range_end)
93 lp->range_end = (start > lp->range_start ? start : lp->range_start);
94
95 return 0;
96 }
97
98 static bool
99 is_range_mapped (uintptr_t range_start, uintptr_t range_end)
100 {
101 struct locals l;
102
103 l.range_start = range_start;
104 l.range_end = range_end;
105 vma_iterate (vma_iterate_callback, &l);
106 return l.range_start == l.range_end;
107 }
108
109 #else
110
111 static bool
112 is_range_mapped (uintptr_t range_start, uintptr_t range_end)
113 {
114 return true;
115 }
116
117 #endif
118
119 static void
120 test_heap (void)
121 {
122 char *heapbuf = (char *) malloc (SECRET_SIZE);
123 ASSERT (heapbuf);
124 uintptr_t volatile addr = (uintptr_t) heapbuf;
125 memcpy (heapbuf, SECRET, SECRET_SIZE);
126 memset_explicit (heapbuf, 0, SECRET_SIZE);
127 free (heapbuf);
128 heapbuf = (char *) addr;
129 if (is_range_mapped (addr, addr + SECRET_SIZE))
130 {
131 /* some implementation could override freed memory by canaries so
132 compare against secret */
133 ASSERT (memcmp (heapbuf, SECRET, SECRET_SIZE) != 0);
134 printf ("test_heap: address range is still mapped after free().\n");
135 }
136 else
137 printf ("test_heap: address range is unmapped after free().\n");
138 }
139
140 /* =============== Verify operation on stack-allocated memory =============== */
141
142 /* There are two passes:
143 1. Put a secret in memory and invoke memset_explicit on it.
144 2. Verify that the memory has been erased.
145 Implement them in the same function, so that they access the same memory
146 range on the stack. Declare the local scalars to be volatile so they
147 are not optimized away. That way, the test verifies that the compiler
148 does not eliminate a call to memset_explicit, even if data flow analysis
149 reveals that the stack area is dead at the end of the function. */
150 static bool _GL_ATTRIBUTE_NOINLINE
151 #if __GNUC__ + (__GNUC_MINOR__ >= 5) > 4
152 __attribute__ ((__noclone__))
153 #endif
154 #if __GNUC__ >= 8
155 __attribute__ ((__noipa__))
156 #endif
157 do_secret_stuff (int volatile pass, char *volatile *volatile last_stackbuf)
158 {
159 char stackbuf[SECRET_SIZE];
160 if (pass == 1)
161 {
162 memcpy (stackbuf, SECRET, SECRET_SIZE);
163 memset_explicit (stackbuf, 0, SECRET_SIZE);
164 *last_stackbuf = stackbuf;
165 return false;
166 }
167 else /* pass == 2 */
168 {
169 /* Use *last_stackbuf here, because stackbuf may be allocated at a
170 different address than *last_stackbuf. This can happen
171 when the compiler splits this function into different functions,
172 one for pass == 1 and one for pass != 1. */
173 return memcmp (zero, *last_stackbuf, SECRET_SIZE) != 0;
174 }
175 }
176
177 static void
178 test_stack (void)
179 {
180 int count = 0;
181 int repeat;
182 char *volatile last_stackbuf;
183
184 for (repeat = 2 * 1000; repeat > 0; repeat--)
185 {
186 /* This odd way of writing two consecutive statements
187 do_secret_stuff (1, &last_stackbuf);
188 count += do_secret_stuff (2, &last_stackbuf);
189 ensures that the two do_secret_stuff calls are performed with the same
190 stack pointer value, on m68k. */
191 if ((repeat % 2) == 0)
192 do_secret_stuff (1, &last_stackbuf);
193 else
194 count += do_secret_stuff (2, &last_stackbuf);
195 }
196 /* If memset_explicit works, count is near 0. (It may be > 0 if there were
197 some asynchronous signal invocations between the two calls of
198 do_secret_stuff.)
199 If memset_explicit is optimized away by the compiler, count comes out as
200 approximately 1000. */
201 printf ("test_stack: count = %d\n", count);
202 ASSERT (count < 50);
203 }
204
205 /* ========================================================================== */
206
207 int
208 main ()
209 {
210 test_static ();
211 test_heap ();
212 test_stack ();
213
214 return 0;
215 }