1 /* $OpenBSD: explicit_bzero.c,v 1.9 2022/02/10 08:39:32 tb Exp $ */ 2 /* 3 * Copyright (c) 2014 Google Inc. 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <assert.h> 19 #include <errno.h> 20 #include <signal.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <unistd.h> 24 25 #define ASSERT_EQ(a, b) assert((a) == (b)) 26 #define ASSERT_NE(a, b) assert((a) != (b)) 27 #define ASSERT_GE(a, b) assert((a) >= (b)) 28 29 #if defined(__has_feature) 30 #if __has_feature(address_sanitizer) 31 #define __SANITIZE_ADDRESS__ 32 #endif 33 #endif 34 #ifdef __SANITIZE_ADDRESS__ 35 #define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) 36 #else 37 #define ATTRIBUTE_NO_SANITIZE_ADDRESS 38 #endif 39 40 /* 128 bits of random data. */ 41 static const char secret[16] = { 42 0xa0, 0x6c, 0x0c, 0x81, 0xba, 0xd8, 0x5b, 0x0c, 43 0xb0, 0xd6, 0xd4, 0xe3, 0xeb, 0x52, 0x5f, 0x96, 44 }; 45 46 enum { 47 SECRETCOUNT = 64, 48 SECRETBYTES = SECRETCOUNT * sizeof(secret) 49 }; 50 51 /* 52 * As of glibc 2.34, when _GNU_SOURCE is defined, SIGSTKSZ is no longer 53 * constant on Linux. SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ). 54 */ 55 static char *altstack; 56 #define ALTSTACK_SIZE (SIGSTKSZ + SECRETBYTES) 57 58 static void 59 setup_stack(void) 60 { 61 altstack = calloc(1, ALTSTACK_SIZE); 62 ASSERT_NE(NULL, altstack); 63 64 const stack_t sigstk = { 65 .ss_sp = altstack, 66 .ss_size = ALTSTACK_SIZE 67 }; 68 69 ASSERT_EQ(0, sigaltstack(&sigstk, NULL)); 70 } 71 72 static void 73 cleanup_stack(void) 74 { 75 free(altstack); 76 } 77 78 static void 79 assert_on_stack(void) 80 { 81 stack_t cursigstk; 82 ASSERT_EQ(0, sigaltstack(NULL, &cursigstk)); 83 ASSERT_EQ(SS_ONSTACK, cursigstk.ss_flags & (SS_DISABLE|SS_ONSTACK)); 84 } 85 86 static void 87 call_on_stack(void (*fn)(int)) 88 { 89 /* 90 * This is a bit more complicated than strictly necessary, but 91 * it ensures we don't have any flaky test failures due to 92 * inherited signal masks/actions/etc. 93 * 94 * On systems where SA_ONSTACK is not supported, this could 95 * alternatively be implemented using makecontext() or 96 * pthread_attr_setstack(). 97 */ 98 99 const struct sigaction sigact = { 100 .sa_handler = fn, 101 .sa_flags = SA_ONSTACK, 102 }; 103 struct sigaction oldsigact; 104 sigset_t sigset, oldsigset; 105 106 /* First, block all signals. */ 107 ASSERT_EQ(0, sigemptyset(&sigset)); 108 ASSERT_EQ(0, sigfillset(&sigset)); 109 ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigset, &oldsigset)); 110 111 /* Next setup the signal handler for SIGUSR1. */ 112 ASSERT_EQ(0, sigaction(SIGUSR1, &sigact, &oldsigact)); 113 114 /* Raise SIGUSR1 and momentarily unblock it to run the handler. */ 115 ASSERT_EQ(0, raise(SIGUSR1)); 116 ASSERT_EQ(0, sigdelset(&sigset, SIGUSR1)); 117 ASSERT_EQ(-1, sigsuspend(&sigset)); 118 ASSERT_EQ(EINTR, errno); 119 120 /* Restore the original signal action, stack, and mask. */ 121 ASSERT_EQ(0, sigaction(SIGUSR1, &oldsigact, NULL)); 122 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &oldsigset, NULL)); 123 } 124 125 static void 126 populate_secret(char *buf, size_t len) 127 { 128 int i, fds[2]; 129 ASSERT_EQ(0, pipe(fds)); 130 131 for (i = 0; i < SECRETCOUNT; i++) 132 ASSERT_EQ(sizeof(secret), write(fds[1], secret, sizeof(secret))); 133 ASSERT_EQ(0, close(fds[1])); 134 135 ASSERT_EQ(len, read(fds[0], buf, len)); 136 ASSERT_EQ(0, close(fds[0])); 137 } 138 139 static int 140 count_secrets(const char *buf) 141 { 142 int res = 0; 143 size_t i; 144 for (i = 0; i < SECRETCOUNT; i++) { 145 if (memcmp(buf + i * sizeof(secret), secret, 146 sizeof(secret)) == 0) 147 res += 1; 148 } 149 return (res); 150 } 151 152 ATTRIBUTE_NO_SANITIZE_ADDRESS static char * 153 test_without_bzero(void) 154 { 155 char buf[SECRETBYTES]; 156 assert_on_stack(); 157 populate_secret(buf, sizeof(buf)); 158 char *res = memmem(altstack, ALTSTACK_SIZE, buf, sizeof(buf)); 159 ASSERT_NE(NULL, res); 160 return (res); 161 } 162 163 ATTRIBUTE_NO_SANITIZE_ADDRESS static char * 164 test_with_bzero(void) 165 { 166 char buf[SECRETBYTES]; 167 assert_on_stack(); 168 populate_secret(buf, sizeof(buf)); 169 char *res = memmem(altstack, ALTSTACK_SIZE, buf, sizeof(buf)); 170 ASSERT_NE(NULL, res); 171 explicit_bzero(buf, sizeof(buf)); 172 return (res); 173 } 174 175 static void 176 do_test_without_bzero(int signo) 177 { 178 char *buf = test_without_bzero(); 179 ASSERT_GE(count_secrets(buf), 1); 180 } 181 182 static void 183 do_test_with_bzero(int signo) 184 { 185 char *buf = test_with_bzero(); 186 ASSERT_EQ(count_secrets(buf), 0); 187 } 188 189 int 190 main(void) 191 { 192 setup_stack(); 193 194 /* 195 * Solaris and OS X clobber the signal stack after returning to the 196 * normal stack, so we need to inspect altstack while we're still 197 * running on it. Unfortunately, this means we risk clobbering the 198 * buffer ourselves. 199 * 200 * To minimize this risk, test_with{,out}_bzero() are responsible for 201 * locating the offset of their buf variable within altstack, and 202 * and returning that address. Then we can simply memcmp() repeatedly 203 * to count how many instances of secret we found. 204 */ 205 206 /* 207 * First, test that if we *don't* call explicit_bzero, that we 208 * *are* able to find at least one instance of the secret data still 209 * on the stack. This sanity checks that call_on_stack() and 210 * populate_secret() work as intended. 211 */ 212 memset(altstack, 0, ALTSTACK_SIZE); 213 call_on_stack(do_test_without_bzero); 214 215 /* 216 * Now test with a call to explicit_bzero() and check that we 217 * *don't* find any instances of the secret data. 218 */ 219 memset(altstack, 0, ALTSTACK_SIZE); 220 call_on_stack(do_test_with_bzero); 221 222 cleanup_stack(); 223 224 return (0); 225 } 226