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