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 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 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 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 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 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