1 /* $OpenBSD: explicit_bzero.c,v 1.7 2021/03/27 11:17:58 bcook 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 /* 128 bits of random data. */ 30 static const char secret[16] = { 31 0xa0, 0x6c, 0x0c, 0x81, 0xba, 0xd8, 0x5b, 0x0c, 32 0xb0, 0xd6, 0xd4, 0xe3, 0xeb, 0x52, 0x5f, 0x96, 33 }; 34 35 enum { 36 SECRETCOUNT = 64, 37 SECRETBYTES = SECRETCOUNT * sizeof(secret) 38 }; 39 40 /* 41 * As of glibc 2.34, when _GNU_SOURCE is defined, SIGSTKSZ is no longer 42 * constant on Linux. SIGSTKSZ is redefined to sysconf (_SC_SIGSTKSZ). 43 */ 44 static char *altstack; 45 #define ALTSTACK_SIZE (SIGSTKSZ + SECRETBYTES) 46 47 static void 48 setup_stack(void) 49 { 50 altstack = calloc(1, ALTSTACK_SIZE); 51 ASSERT_NE(NULL, altstack); 52 53 const stack_t sigstk = { 54 .ss_sp = altstack, 55 .ss_size = ALTSTACK_SIZE 56 }; 57 58 ASSERT_EQ(0, sigaltstack(&sigstk, NULL)); 59 } 60 61 static void 62 cleanup_stack(void) 63 { 64 free(altstack); 65 } 66 67 static void 68 assert_on_stack(void) 69 { 70 stack_t cursigstk; 71 ASSERT_EQ(0, sigaltstack(NULL, &cursigstk)); 72 ASSERT_EQ(SS_ONSTACK, cursigstk.ss_flags & (SS_DISABLE|SS_ONSTACK)); 73 } 74 75 static void 76 call_on_stack(void (*fn)(int)) 77 { 78 /* 79 * This is a bit more complicated than strictly necessary, but 80 * it ensures we don't have any flaky test failures due to 81 * inherited signal masks/actions/etc. 82 * 83 * On systems where SA_ONSTACK is not supported, this could 84 * alternatively be implemented using makecontext() or 85 * pthread_attr_setstack(). 86 */ 87 88 const struct sigaction sigact = { 89 .sa_handler = fn, 90 .sa_flags = SA_ONSTACK, 91 }; 92 struct sigaction oldsigact; 93 sigset_t sigset, oldsigset; 94 95 /* First, block all signals. */ 96 ASSERT_EQ(0, sigemptyset(&sigset)); 97 ASSERT_EQ(0, sigfillset(&sigset)); 98 ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigset, &oldsigset)); 99 100 /* Next setup the signal handler for SIGUSR1. */ 101 ASSERT_EQ(0, sigaction(SIGUSR1, &sigact, &oldsigact)); 102 103 /* Raise SIGUSR1 and momentarily unblock it to run the handler. */ 104 ASSERT_EQ(0, raise(SIGUSR1)); 105 ASSERT_EQ(0, sigdelset(&sigset, SIGUSR1)); 106 ASSERT_EQ(-1, sigsuspend(&sigset)); 107 ASSERT_EQ(EINTR, errno); 108 109 /* Restore the original signal action, stack, and mask. */ 110 ASSERT_EQ(0, sigaction(SIGUSR1, &oldsigact, NULL)); 111 ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &oldsigset, NULL)); 112 } 113 114 static void 115 populate_secret(char *buf, size_t len) 116 { 117 int i, fds[2]; 118 ASSERT_EQ(0, pipe(fds)); 119 120 for (i = 0; i < SECRETCOUNT; i++) 121 ASSERT_EQ(sizeof(secret), write(fds[1], secret, sizeof(secret))); 122 ASSERT_EQ(0, close(fds[1])); 123 124 ASSERT_EQ(len, read(fds[0], buf, len)); 125 ASSERT_EQ(0, close(fds[0])); 126 } 127 128 static int 129 count_secrets(const char *buf) 130 { 131 int res = 0; 132 size_t i; 133 for (i = 0; i < SECRETCOUNT; i++) { 134 if (memcmp(buf + i * sizeof(secret), secret, 135 sizeof(secret)) == 0) 136 res += 1; 137 } 138 return (res); 139 } 140 141 static char * 142 test_without_bzero() 143 { 144 char buf[SECRETBYTES]; 145 assert_on_stack(); 146 populate_secret(buf, sizeof(buf)); 147 char *res = memmem(altstack, ALTSTACK_SIZE, buf, sizeof(buf)); 148 ASSERT_NE(NULL, res); 149 return (res); 150 } 151 152 static char * 153 test_with_bzero() 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 explicit_bzero(buf, sizeof(buf)); 161 return (res); 162 } 163 164 static void 165 do_test_without_bzero(int signo) 166 { 167 char *buf = test_without_bzero(); 168 ASSERT_GE(count_secrets(buf), 1); 169 } 170 171 static void 172 do_test_with_bzero(int signo) 173 { 174 char *buf = test_with_bzero(); 175 ASSERT_EQ(count_secrets(buf), 0); 176 } 177 178 int 179 main() 180 { 181 setup_stack(); 182 183 /* 184 * Solaris and OS X clobber the signal stack after returning to the 185 * normal stack, so we need to inspect altstack while we're still 186 * running on it. Unfortunately, this means we risk clobbering the 187 * buffer ourselves. 188 * 189 * To minimize this risk, test_with{,out}_bzero() are responsible for 190 * locating the offset of their buf variable within altstack, and 191 * and returning that address. Then we can simply memcmp() repeatedly 192 * to count how many instances of secret we found. 193 */ 194 195 /* 196 * First, test that if we *don't* call explicit_bzero, that we 197 * *are* able to find at least one instance of the secret data still 198 * on the stack. This sanity checks that call_on_stack() and 199 * populate_secret() work as intended. 200 */ 201 memset(altstack, 0, ALTSTACK_SIZE); 202 call_on_stack(do_test_without_bzero); 203 204 /* 205 * Now test with a call to explicit_bzero() and check that we 206 * *don't* find any instances of the secret data. 207 */ 208 memset(altstack, 0, ALTSTACK_SIZE); 209 call_on_stack(do_test_with_bzero); 210 211 cleanup_stack(); 212 213 return (0); 214 } 215