xref: /openbsd-src/regress/lib/libc/explicit_bzero/explicit_bzero.c (revision 42cbf0f7767c57cd7911e28094e5aac50ee69870)
1 /*	$OpenBSD: explicit_bzero.c,v 1.2 2014/07/09 14:26:59 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 <string.h>
22 #include <unistd.h>
23 
24 #define ASSERT_EQ(a, b) assert((a) == (b))
25 #define ASSERT_NE(a, b) assert((a) != (b))
26 #define ASSERT_GE(a, b) assert((a) >= (b))
27 
28 static void
29 call_on_stack(void (*fn)(int), void *stack, size_t stacklen)
30 {
31 	/*
32 	 * This is a bit more complicated than strictly necessary, but
33 	 * it ensures we don't have any flaky test failures due to
34 	 * inherited signal masks/actions/etc.
35 	 *
36 	 * On systems where SA_ONSTACK is not supported, this could
37 	 * alternatively be implemented using makecontext() or
38 	 * pthread_attr_setstack().
39 	 */
40 
41 	const struct sigaction sigact = {
42 		.sa_handler = fn,
43 		.sa_flags = SA_ONSTACK,
44 	};
45 	const stack_t sigstk = {
46 		.ss_sp = stack,
47 		.ss_size = stacklen,
48 	};
49 	struct sigaction oldsigact;
50 	stack_t oldsigstk;
51 	sigset_t sigset, oldsigset;
52 
53 	/* First, block all signals. */
54 	ASSERT_EQ(0, sigemptyset(&sigset));
55 	ASSERT_EQ(0, sigfillset(&sigset));
56 	ASSERT_EQ(0, sigprocmask(SIG_BLOCK, &sigset, &oldsigset));
57 
58 	/* Next setup the signal stack and handler for SIGUSR1. */
59 	ASSERT_EQ(0, sigaltstack(&sigstk, &oldsigstk));
60 	ASSERT_EQ(0, sigaction(SIGUSR1, &sigact, &oldsigact));
61 
62 	/* Raise SIGUSR1 and momentarily unblock it to run the handler. */
63 	ASSERT_EQ(0, raise(SIGUSR1));
64 	ASSERT_EQ(0, sigdelset(&sigset, SIGUSR1));
65 	ASSERT_EQ(-1, sigsuspend(&sigset));
66 	ASSERT_EQ(EINTR, errno);
67 
68 	/* Restore the original signal action, stack, and mask. */
69 	ASSERT_EQ(0, sigaction(SIGUSR1, &oldsigact, NULL));
70 	if (oldsigstk.ss_flags & SA_ONSTACK)
71 		ASSERT_EQ(0, sigaltstack(&oldsigstk, NULL));
72 	ASSERT_EQ(0, sigprocmask(SIG_SETMASK, &oldsigset, NULL));
73 }
74 
75 /* 128 bits of random data. */
76 static const char secret[16] = {
77 	0xa0, 0x6c, 0x0c, 0x81, 0xba, 0xd8, 0x5b, 0x0c,
78 	0xb0, 0xd6, 0xd4, 0xe3, 0xeb, 0x52, 0x5f, 0x96,
79 };
80 
81 enum {
82 	SECRETCOUNT = 16,
83 	SECRETBYTES = SECRETCOUNT * sizeof(secret)
84 };
85 
86 static void
87 populate_secret(char *buf, size_t len)
88 {
89 	int i, fds[2];
90 	ASSERT_EQ(0, pipe(fds));
91 
92 	for (i = 0; i < SECRETCOUNT; i++)
93 		ASSERT_EQ(sizeof(secret), write(fds[1], secret, sizeof(secret)));
94 	ASSERT_EQ(0, close(fds[1]));
95 
96 	ASSERT_EQ(len, read(fds[0], buf, len));
97 	ASSERT_EQ(0, close(fds[0]));
98 }
99 
100 static void
101 test_without_bzero(int signo)
102 {
103 	char buf[SECRETBYTES];
104 	populate_secret(buf, sizeof(buf));
105 }
106 
107 static void
108 test_with_bzero(int signo)
109 {
110 	char buf[SECRETBYTES];
111 	populate_secret(buf, sizeof(buf));
112 	explicit_bzero(buf, sizeof(buf));
113 }
114 
115 static char altstack[SIGSTKSZ];
116 
117 int
118 main()
119 {
120 	/*
121 	 * First, test that if we *don't* call explicit_bzero, that we
122 	 * *are* able to find the secret data on the stack.  This
123 	 * sanity checks that call_on_stack() and populare_secret()
124 	 * work as intended.
125 	 */
126 	memset(altstack, 0, sizeof(altstack));
127 	call_on_stack(test_without_bzero, altstack, sizeof(altstack));
128 	ASSERT_NE(NULL, memmem(altstack, sizeof(altstack), secret, sizeof(secret)));
129 
130 	/*
131 	 * Now test with a call to explicit_bzero() and check that we
132 	 * *don't* find the secret data.
133 	 */
134 	memset(altstack, 0, sizeof(altstack));
135 	call_on_stack(test_with_bzero, altstack, sizeof(altstack));
136 	ASSERT_EQ(NULL, memmem(altstack, sizeof(altstack), secret, sizeof(secret)));
137 
138 	return (0);
139 }
140