1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>
14 */
15
16 /*
17 * Test that SHA1Update and SHA2Update correctly adjust the bit count
18 * when fed large inputs.
19 *
20 * This is a very focussed white-box unit test that examines
21 * handling of the running message bit count updated by SHA*Update.
22 *
23 * Since we are only testing the bit count updates in this test,
24 * we point SHA*Update at a buffer with an unmapped page, catch
25 * the SIGSEGV, and siglongjmp back out to the test assertions.
26 */
27
28 #include <err.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <setjmp.h>
35
36 #include <sha1.h>
37 #include <sha2.h>
38 #include <sys/debug.h>
39 #include <sys/mman.h>
40
41 static sigjmp_buf from_trap;
42 static struct sigaction trap_sa;
43 static void *buf;
44
45 static long pagesize;
46
47 static void
trap_handler(int signo,siginfo_t * info,void * ucp)48 trap_handler(int signo, siginfo_t *info, void *ucp)
49 {
50
51 if ((info->si_addr >= buf + pagesize) &&
52 (info->si_addr < (buf + 2 * pagesize))) {
53 siglongjmp(from_trap, signo);
54 }
55
56 printf("faulting address outside sentinel page\n");
57 printf("signal: %d code: %d faulting address: %p\n",
58 info->si_signo, info->si_code, info->si_addr);
59
60 }
61
62 static void
test_update_sha1(void * buf,size_t len,uint32_t c0,uint32_t c1)63 test_update_sha1(void *buf, size_t len, uint32_t c0, uint32_t c1)
64 {
65 SHA1_CTX ctx;
66 VERIFY3U(len, >, pagesize);
67
68 SHA1Init(&ctx);
69 VERIFY3U(0, ==, ctx.count[0]);
70 VERIFY3U(0, ==, ctx.count[1]);
71
72 if (sigsetjmp(from_trap, 1) == 0) {
73 (void) sigaction(SIGSEGV, &trap_sa, NULL);
74 SHA1Update(&ctx, buf, len);
75 errx(EXIT_FAILURE, "Should have faulted in SHA1Update "
76 "(after %ld of %zu bytes)", pagesize, len);
77 } else {
78 (void) signal(SIGSEGV, SIG_DFL);
79 VERIFY3U(c0, ==, ctx.count[0]);
80 VERIFY3U(c1, ==, ctx.count[1]);
81 }
82
83 if (len <= pagesize * 2)
84 return;
85
86 /*
87 * Try again with the same length split across two calls
88 * to SHA1Update to exercise the other way that the high
89 * order word of the bit count gets incremented.
90 */
91 SHA1Init(&ctx);
92 SHA1Update(&ctx, buf, pagesize);
93 VERIFY3U(0, ==, ctx.count[0]);
94 VERIFY3U(pagesize * 8, ==, ctx.count[1]);
95
96 if (sigsetjmp(from_trap, 1) == 0) {
97 (void) sigaction(SIGSEGV, &trap_sa, NULL);
98 SHA1Update(&ctx, buf, len - pagesize);
99 errx(EXIT_FAILURE, "Should have faulted in SHA1Update "
100 "(after %ld of %zu bytes)", pagesize, len);
101 } else {
102 (void) signal(SIGSEGV, SIG_DFL);
103 VERIFY3U(c0, ==, ctx.count[0]);
104 VERIFY3U(c1, ==, ctx.count[1]);
105 }
106 }
107
108 static void
test_update_32(uint64_t mech,void * buf,size_t len,uint32_t c0,uint32_t c1)109 test_update_32(uint64_t mech, void *buf, size_t len, uint32_t c0, uint32_t c1)
110 {
111 SHA2_CTX ctx;
112 VERIFY3U(len, >, pagesize);
113
114 SHA2Init(mech, &ctx);
115 VERIFY3U(0, ==, ctx.count.c32[0]);
116 VERIFY3U(0, ==, ctx.count.c32[1]);
117
118 if (sigsetjmp(from_trap, 1) == 0) {
119 (void) sigaction(SIGSEGV, &trap_sa, NULL);
120 SHA2Update(&ctx, buf, len);
121 errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
122 "(after %ld of %zu bytes)", pagesize, len);
123 } else {
124 (void) signal(SIGSEGV, SIG_DFL);
125 VERIFY3U(c0, ==, ctx.count.c32[0]);
126 VERIFY3U(c1, ==, ctx.count.c32[1]);
127 }
128
129 if (len <= pagesize * 2)
130 return;
131
132 /*
133 * Try again with the same length split across two calls
134 * to SHA2Update to exercise the other way that the high
135 * order word of the bit count gets incremented.
136 */
137 SHA2Init(mech, &ctx);
138 SHA2Update(&ctx, buf, pagesize);
139 VERIFY3U(0, ==, ctx.count.c32[0]);
140 VERIFY3U(pagesize * 8, ==, ctx.count.c32[1]);
141
142 if (sigsetjmp(from_trap, 1) == 0) {
143 (void) sigaction(SIGSEGV, &trap_sa, NULL);
144 SHA2Update(&ctx, buf, len - pagesize);
145 errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
146 "(after %ld of %zu bytes)", pagesize, len);
147 } else {
148 (void) signal(SIGSEGV, SIG_DFL);
149 VERIFY3U(c0, ==, ctx.count.c32[0]);
150 VERIFY3U(c1, ==, ctx.count.c32[1]);
151 }
152 }
153
154 static void
test_update_64(uint64_t mech,void * buf,size_t len,uint64_t c0,uint64_t c1)155 test_update_64(uint64_t mech, void *buf, size_t len, uint64_t c0, uint64_t c1)
156 {
157 SHA2_CTX ctx;
158 VERIFY3U(len, >, pagesize);
159
160 SHA2Init(mech, &ctx);
161 VERIFY3U(0, ==, ctx.count.c64[0]);
162 VERIFY3U(0, ==, ctx.count.c64[1]);
163
164 if (sigsetjmp(from_trap, 1) == 0) {
165 (void) sigaction(SIGSEGV, &trap_sa, NULL);
166 SHA2Update(&ctx, buf, len);
167 errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
168 "(after %ld of %zu bytes)", pagesize, len);
169 } else {
170 (void) signal(SIGSEGV, SIG_DFL);
171 VERIFY3U(c0, ==, ctx.count.c64[0]);
172 VERIFY3U(c1, ==, ctx.count.c64[1]);
173 }
174
175 if (len <= pagesize * 2)
176 return;
177
178 /*
179 * Try again with the same length split across two calls
180 * to SHA2Update to exercise the other way that the high
181 * order word of the bit count gets incremented.
182 */
183 SHA2Init(mech, &ctx);
184 SHA2Update(&ctx, buf, pagesize);
185 VERIFY3U(0, ==, ctx.count.c64[0]);
186 VERIFY3U(pagesize * 8, ==, ctx.count.c64[1]);
187
188 if (sigsetjmp(from_trap, 1) == 0) {
189 (void) sigaction(SIGSEGV, &trap_sa, NULL);
190 SHA2Update(&ctx, buf, len - pagesize);
191 errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
192 "(after %ld of %zu bytes)", pagesize, len);
193 } else {
194 (void) signal(SIGSEGV, SIG_DFL);
195 VERIFY3U(c0, ==, ctx.count.c64[0]);
196 VERIFY3U(c1, ==, ctx.count.c64[1]);
197 }
198 }
199
200 int
main(int argc,char ** argv)201 main(int argc, char **argv)
202 {
203 uint64_t len, max_len;
204 int flags = MAP_PRIVATE|MAP_ANON;
205
206 #ifdef _LP64
207 flags |= MAP_32BIT;
208 #endif
209 pagesize = sysconf(_SC_PAGESIZE);
210 buf = mmap(0, 2 * pagesize, PROT_READ|PROT_WRITE, flags, -1, 0);
211 if (buf == MAP_FAILED) {
212 err(EXIT_FAILURE, "mmap MAP_PRIVATE|MAP_ANON|... "
213 "of %ld bytes failed", 2 * pagesize);
214 }
215 if (mprotect(buf + pagesize, pagesize, PROT_NONE) < 0) {
216 err(EXIT_FAILURE, "mprotect of %ld bytes at %p failed",
217 pagesize, buf + pagesize);
218 }
219
220 /*
221 * When we set this sigaction, we intend to catch exactly one trap:
222 * a memory reference to the page we've just protected.
223 */
224 memset(&trap_sa, 0, sizeof (trap_sa));
225 trap_sa.sa_flags = SA_SIGINFO|SA_RESETHAND;
226 trap_sa.sa_sigaction = trap_handler;
227
228 max_len = SIZE_MAX;
229 for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
230 printf("test SHA1 length 0x%016lx\n", len);
231 test_update_sha1(buf, len, len >> 29, len << 3);
232 }
233
234 for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
235 printf("test SHA256 length 0x%016lx\n", len);
236 test_update_32(SHA256, buf, len, len >> 29, len << 3);
237 }
238
239 for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
240 printf("test SHA512 length 0x%016lx\n", len);
241 test_update_64(SHA512, buf, len, len >> 61, len << 3);
242 }
243 return (EXIT_SUCCESS);
244 }
245