test-memwipe.c (5870B)
1 /* Copyright (c) 2015-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 #include "orconfig.h" 5 #include "lib/crypt_ops/crypto_util.h" 6 7 #include "lib/intmath/cmp.h" 8 #include "lib/malloc/malloc.h" 9 10 #include <string.h> 11 #include <stdio.h> 12 #include <sys/types.h> 13 #include <stdlib.h> 14 15 #ifdef HAVE_SYS_PARAM_H 16 #include <sys/param.h> 17 #endif 18 19 static unsigned fill_a_buffer_memset(void) __attribute__((noinline)); 20 static unsigned fill_a_buffer_memwipe(void) __attribute__((noinline)); 21 static unsigned fill_a_buffer_nothing(void) __attribute__((noinline)); 22 static unsigned fill_heap_buffer_memset(void) __attribute__((noinline)); 23 static unsigned fill_heap_buffer_memwipe(void) __attribute__((noinline)); 24 static unsigned fill_heap_buffer_nothing(void) __attribute__((noinline)); 25 static unsigned check_a_buffer(void) __attribute__((noinline)); 26 27 extern const char *s; /* Make the linkage global */ 28 const char *s = NULL; 29 30 #define BUF_LEN 2048 31 32 #define FILL_BUFFER_IMPL() \ 33 do { \ 34 unsigned int i; \ 35 \ 36 /* Fill up a 1k buffer with a recognizable pattern. */ \ 37 for (i = 0; i < BUF_LEN; i += strlen(s)) { \ 38 memcpy(buf+i, s, MIN(strlen(s), BUF_LEN-i)); \ 39 } \ 40 \ 41 /* Use the buffer as input to a computation so the above can't get */ \ 42 /* optimized away. */ \ 43 for (i = 0; i < BUF_LEN; ++i) { \ 44 sum += (unsigned char)buf[i]; \ 45 } \ 46 } while (0) 47 48 #ifdef OpenBSD 49 /* Disable some of OpenBSD's malloc protections for this test. This helps 50 * us do bad things, such as access freed buffers, without crashing. */ 51 extern const char *malloc_options; 52 const char *malloc_options = "sufjj"; 53 #endif /* defined(OpenBSD) */ 54 55 static unsigned 56 fill_a_buffer_memset(void) 57 { 58 char buf[BUF_LEN]; 59 unsigned sum = 0; 60 FILL_BUFFER_IMPL(); 61 memset(buf, 0, sizeof(buf)); 62 return sum; 63 } 64 65 static unsigned 66 fill_a_buffer_memwipe(void) 67 { 68 char buf[BUF_LEN]; 69 unsigned sum = 0; 70 FILL_BUFFER_IMPL(); 71 memwipe(buf, 0, sizeof(buf)); 72 return sum; 73 } 74 75 static unsigned 76 fill_a_buffer_nothing(void) 77 { 78 char buf[BUF_LEN]; 79 unsigned sum = 0; 80 FILL_BUFFER_IMPL(); 81 return sum; 82 } 83 84 static inline int 85 vmemeq(volatile char *a, const char *b, size_t n) 86 { 87 while (n--) { 88 if (*a++ != *b++) 89 return 0; 90 } 91 return 1; 92 } 93 94 static unsigned 95 check_a_buffer(void) 96 { 97 unsigned int i; 98 volatile char buf[BUF_LEN]; 99 unsigned sum = 0; 100 101 /* See if this buffer has the string in it. 102 103 YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM AN UNINITIALIZED 104 BUFFER. 105 106 If you know a better way to figure out whether the compiler eliminated 107 the memset/memwipe calls or not, please let me know. 108 */ 109 for (i = 0; i < BUF_LEN - strlen(s); ++i) { 110 if (vmemeq(buf+i, s, strlen(s))) 111 ++sum; 112 } 113 114 return sum; 115 } 116 117 static char *heap_buf = NULL; 118 119 static unsigned 120 fill_heap_buffer_memset(void) 121 { 122 char *buf = heap_buf = raw_malloc(BUF_LEN); 123 unsigned sum = 0; 124 FILL_BUFFER_IMPL(); 125 memset(buf, 0, BUF_LEN); 126 raw_free(buf); 127 return sum; 128 } 129 130 static unsigned 131 fill_heap_buffer_memwipe(void) 132 { 133 char *buf = heap_buf = raw_malloc(BUF_LEN); 134 unsigned sum = 0; 135 FILL_BUFFER_IMPL(); 136 memwipe(buf, 0, BUF_LEN); 137 raw_free(buf); 138 return sum; 139 } 140 141 static unsigned 142 fill_heap_buffer_nothing(void) 143 { 144 char *buf = heap_buf = raw_malloc(BUF_LEN); 145 unsigned sum = 0; 146 FILL_BUFFER_IMPL(); 147 raw_free(buf); 148 return sum; 149 } 150 151 static unsigned 152 check_heap_buffer(void) 153 { 154 unsigned int i; 155 unsigned sum = 0; 156 volatile char *buf = heap_buf; 157 158 /* See if this buffer has the string in it. 159 160 YES, THIS DOES INVOKE UNDEFINED BEHAVIOR BY READING FROM A FREED BUFFER. 161 162 If you know a better way to figure out whether the compiler eliminated 163 the memset/memwipe calls or not, please let me know. 164 */ 165 for (i = 0; i < BUF_LEN - strlen(s); ++i) { 166 if (vmemeq(buf+i, s, strlen(s))) 167 ++sum; 168 } 169 170 return sum; 171 } 172 173 static struct testcase { 174 const char *name; 175 /* this spacing satisfies make check-spaces */ 176 unsigned 177 (*fill_fn)(void); 178 unsigned 179 (*check_fn)(void); 180 } testcases[] = { 181 { "nil", fill_a_buffer_nothing, check_a_buffer }, 182 { "nil-heap", fill_heap_buffer_nothing, check_heap_buffer }, 183 { "memset", fill_a_buffer_memset, check_a_buffer }, 184 { "memset-heap", fill_heap_buffer_memset, check_heap_buffer }, 185 { "memwipe", fill_a_buffer_memwipe, check_a_buffer }, 186 { "memwipe-heap", fill_heap_buffer_memwipe, check_heap_buffer }, 187 { NULL, NULL, NULL } 188 }; 189 190 int 191 main(int argc, char **argv) 192 { 193 unsigned x, x2; 194 int i; 195 int working = 1; 196 unsigned found[6]; 197 (void) argc; (void) argv; 198 199 s = "squamous haberdasher gallimaufry"; 200 201 memset(found, 0, sizeof(found)); 202 203 for (i = 0; testcases[i].name; ++i) { 204 x = testcases[i].fill_fn(); 205 found[i] = testcases[i].check_fn(); 206 207 x2 = fill_a_buffer_nothing(); 208 209 if (x != x2) { 210 working = 0; 211 } 212 } 213 214 if (!working || !found[0] || !found[1]) { 215 printf("It appears that this test case may not give you reliable " 216 "information. Sorry.\n"); 217 } 218 219 if (!found[2] && !found[3]) { 220 printf("It appears that memset is good enough on this platform. Good.\n"); 221 } 222 223 if (found[4] || found[5]) { 224 printf("ERROR: memwipe does not wipe data!\n"); 225 return 1; 226 } else { 227 printf("OKAY: memwipe seems to work.\n"); 228 return 0; 229 } 230 }