xref: /illumos-gate/usr/src/test/crypto-tests/tests/longhash/longhash.c (revision b70bf3ee79d81df3ccc36e553e0ff11049a2b51a)
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 SHA2Update correctly adjusts 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 SHA2Update.
22  *
23  * Since we are only testing the bit count updates in this test,
24  * we point SHA2Update 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 <sha2.h>
37 #include <sys/debug.h>
38 #include <sys/mman.h>
39 
40 static sigjmp_buf from_trap;
41 static struct sigaction trap_sa;
42 static void *buf;
43 
44 static long pagesize;
45 
46 static void
47 trap_handler(int signo, siginfo_t *info, void *ucp)
48 {
49 
50 	if ((info->si_addr >= buf + pagesize) &&
51 	    (info->si_addr < (buf + 2 * pagesize))) {
52 		siglongjmp(from_trap, signo);
53 	}
54 
55 	printf("faulting address outside sentinel page\n");
56 	printf("signal: %d code: %d faulting address: %p\n",
57 	    info->si_signo, info->si_code, info->si_addr);
58 
59 }
60 
61 static void
62 test_update_32(uint64_t mech, void *buf, size_t len, uint32_t c0, uint32_t c1)
63 {
64 	SHA2_CTX ctx;
65 	VERIFY3U(len, >, pagesize);
66 
67 	SHA2Init(mech, &ctx);
68 	VERIFY3U(0, ==, ctx.count.c32[0]);
69 	VERIFY3U(0, ==, ctx.count.c32[1]);
70 
71 	if (sigsetjmp(from_trap, 1) == 0) {
72 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
73 		SHA2Update(&ctx, buf, len);
74 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
75 		    "(after %ld of %zu bytes)", pagesize, len);
76 	} else {
77 		(void) signal(SIGSEGV, SIG_DFL);
78 		VERIFY3U(c0, ==, ctx.count.c32[0]);
79 		VERIFY3U(c1, ==, ctx.count.c32[1]);
80 	}
81 
82 	if (len <= pagesize * 2)
83 		return;
84 
85 	/*
86 	 * Try again with the same length split across two calls
87 	 * to SHA2Update to exercise the other way that the high
88 	 * order word of the bit count gets incremented.
89 	 */
90 	SHA2Init(mech, &ctx);
91 	SHA2Update(&ctx, buf, pagesize);
92 	VERIFY3U(0, ==, ctx.count.c32[0]);
93 	VERIFY3U(pagesize * 8, ==, ctx.count.c32[1]);
94 
95 	if (sigsetjmp(from_trap, 1) == 0) {
96 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
97 		SHA2Update(&ctx, buf, len - pagesize);
98 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
99 		    "(after %ld of %zu bytes)", pagesize, len);
100 	} else {
101 		(void) signal(SIGSEGV, SIG_DFL);
102 		VERIFY3U(c0, ==, ctx.count.c32[0]);
103 		VERIFY3U(c1, ==, ctx.count.c32[1]);
104 	}
105 }
106 
107 static void
108 test_update_64(uint64_t mech, void *buf, size_t len, uint64_t c0, uint64_t c1)
109 {
110 	SHA2_CTX ctx;
111 	VERIFY3U(len, >, pagesize);
112 
113 	SHA2Init(mech, &ctx);
114 	VERIFY3U(0, ==, ctx.count.c64[0]);
115 	VERIFY3U(0, ==, ctx.count.c64[1]);
116 
117 	if (sigsetjmp(from_trap, 1) == 0) {
118 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
119 		SHA2Update(&ctx, buf, len);
120 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
121 		    "(after %ld of %zu bytes)", pagesize, len);
122 	} else {
123 		(void) signal(SIGSEGV, SIG_DFL);
124 		VERIFY3U(c0, ==, ctx.count.c64[0]);
125 		VERIFY3U(c1, ==, ctx.count.c64[1]);
126 	}
127 
128 	if (len <= pagesize * 2)
129 		return;
130 
131 	/*
132 	 * Try again with the same length split across two calls
133 	 * to SHA2Update to exercise the other way that the high
134 	 * order word of the bit count gets incremented.
135 	 */
136 	SHA2Init(mech, &ctx);
137 	SHA2Update(&ctx, buf, pagesize);
138 	VERIFY3U(0, ==, ctx.count.c64[0]);
139 	VERIFY3U(pagesize * 8, ==, ctx.count.c64[1]);
140 
141 	if (sigsetjmp(from_trap, 1) == 0) {
142 		(void) sigaction(SIGSEGV, &trap_sa, NULL);
143 		SHA2Update(&ctx, buf, len - pagesize);
144 		errx(EXIT_FAILURE, "Should have faulted in SHA2Update "
145 		    "(after %ld of %zu bytes)", pagesize, len);
146 	} else {
147 		(void) signal(SIGSEGV, SIG_DFL);
148 		VERIFY3U(c0, ==, ctx.count.c64[0]);
149 		VERIFY3U(c1, ==, ctx.count.c64[1]);
150 	}
151 }
152 
153 int
154 main(int argc, char **argv)
155 {
156 	uint64_t len, max_len;
157 	int flags = MAP_PRIVATE|MAP_ANON;
158 
159 #ifdef _LP64
160 	flags |= MAP_32BIT;
161 #endif
162 	pagesize = sysconf(_SC_PAGESIZE);
163 	buf = mmap(0, 2 * pagesize, PROT_READ|PROT_WRITE, flags, -1, 0);
164 	if (buf == MAP_FAILED) {
165 		err(EXIT_FAILURE, "mmap MAP_PRIVATE|MAP_ANON|... "
166 		    "of %ld bytes failed", 2 * pagesize);
167 	}
168 	if (mprotect(buf + pagesize, pagesize, PROT_NONE) < 0) {
169 		err(EXIT_FAILURE, "mprotect of %ld bytes at %p failed",
170 		    pagesize, buf + pagesize);
171 	}
172 
173 	/*
174 	 * When we set this sigaction, we intend to catch exactly one trap:
175 	 * a memory reference to the page we've just protected.
176 	 */
177 	memset(&trap_sa, 0, sizeof (trap_sa));
178 	trap_sa.sa_flags = SA_SIGINFO|SA_RESETHAND;
179 	trap_sa.sa_sigaction = trap_handler;
180 
181 	max_len = SIZE_MAX;
182 	for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
183 		printf("test SHA256 length 0x%016lx\n", len);
184 		test_update_32(SHA256, buf, len, len >> 29, len << 3);
185 	}
186 
187 	for (len = pagesize * 2; len != 0 && len < max_len; len <<= 1) {
188 		printf("test SHA512 length 0x%016lx\n", len);
189 		test_update_64(SHA512, buf, len, len >> 61, len << 3);
190 	}
191 	return (EXIT_SUCCESS);
192 }
193