From 3fc8d4910acd7615cf8d0fe09a79ce99cc634c8b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 12 Aug 2026 10:01:21 +0200 Subject: [PATCH] tests: drive the sha256 and sha512 length-guard error halves --- tests/unit-mcdc/test_sha256_whitebox.c | 127 +++++++++++++++++++++++++ tests/unit-mcdc/test_sha512_whitebox.c | 126 ++++++++++++++++++++++++ 2 files changed, 253 insertions(+) diff --git a/tests/unit-mcdc/test_sha256_whitebox.c b/tests/unit-mcdc/test_sha256_whitebox.c index bbf593e9f2..3786f014ee 100644 --- a/tests/unit-mcdc/test_sha256_whitebox.c +++ b/tests/unit-mcdc/test_sha256_whitebox.c @@ -47,6 +47,28 @@ * NULL: every block then flows through the per-block route that evaluates the * decision, and the C transform is safe to run whatever intel_flags claims * (so forcing e.g. SHA-NI never executes SHA-NI asm). + * + * Second supplement: Sha256Update()'s save-remainder guard (line ~2034) + * + * if (ret == 0 && len > 0) + * + * idx0 ("ret == 0") FALSE half. Nothing on the path to that guard can set ret + * except the block transform: the two earlier error exits (buffLen >= block + * size -> BUFFER_E, and the argument checks in wc_Sha256Update) return before + * it. In the plain C variants XTRANSFORM expands straight to the static + * Transform_Sha256(), which has no failing return, so the FALSE half is only + * reachable where the transform is dispatched through the file-static + * Transform_Sha256_p -- i.e. exactly this USE_INTEL_SPEEDUP build. Retargeting + * that pointer at a stub returning BAD_FUNC_ARG (and NULLing + * Transform_Sha256_Len_p so the multi-block loop, not XTRANSFORM_LEN, runs) + * makes the first loop iteration break with ret != 0; the guard is then + * evaluated with idx0 FALSE and idx1 short-circuited (never read). + * Vectors, all in this one binary: + * stub + 3 whole blocks -> (F, -) [the residual] + * C transform + 1 block + 5 -> (T, T) + * C transform + exactly 1 block -> (T, F) + * No digest is ever taken from the faulted context: it is freed and + * re-initialised before the baseline vectors run. */ #include @@ -118,6 +140,110 @@ static void wb_intel_dispatch(void) #endif +/* -------------------------------------------------------------------------- + * Sha256Update(): "ret == 0" half of the save-remainder guard (~line 2034). + * ----------------------------------------------------------------------- */ +#if !defined(NO_SHA256) && defined(WOLFSSL_X86_64_BUILD) && \ + defined(USE_INTEL_SPEEDUP) && !defined(WC_C_DYNAMIC_FALLBACK) && \ + !defined(WC_NO_INTERNAL_FUNCTION_POINTERS) && \ + (defined(HAVE_INTEL_AVX1) || defined(HAVE_INTEL_AVX2)) + +static int wb_transform_fails(wc_Sha256* sha256, const byte* data) +{ + (void)sha256; + (void)data; + return BAD_FUNC_ARG; +} + +static void wb_update_transform_err(void) +{ + /* Three whole blocks: the loop breaks on the first transform (len is + * decremented before it), leaving len == 2 * block size at the guard -- + * immaterial, since idx1 is short-circuited when idx0 is FALSE. */ + byte buf[3 * WC_SHA256_BLOCK_SIZE]; + byte hash[WC_SHA256_DIGEST_SIZE]; + int (*saved_p)(wc_Sha256*, const byte*) = Transform_Sha256_p; + int (*saved_len_p)(wc_Sha256*, const byte*, word32) = + Transform_Sha256_Len_p; + cpuid_flags_t saved_flags = intel_flags; + wc_Sha256 sha; + int ret; + + XMEMSET(buf, 0x5a, sizeof(buf)); + + /* idx0 FALSE: the transform fails on the first block. */ + if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha256_ex failed (transform-error case skipped)"); + wb_fail = 1; + } + else { + /* Pin the per-block route, then make that block transform fail. + * intel_flags is zeroed so the C route the baselines use below is + * byte-reversal-consistent; it is restored at the end. */ + Transform_Sha256_Len_p = NULL; + Transform_Sha256_p = wb_transform_fails; + intel_flags = 0; + + ret = wc_Sha256Update(&sha, buf, (word32)sizeof(buf)); + if (ret == 0) { + WB_NOTE("faulted wc_Sha256Update unexpectedly succeeded"); + wb_fail = 1; + } + /* The context is poisoned; no digest is taken from it. */ + Transform_Sha256_p = Transform_Sha256; + wc_Sha256Free(&sha); + } + + /* idx0 TRUE, idx1 TRUE: one whole block plus a 5-byte remainder. */ + if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha256_ex failed (remainder baseline skipped)"); + wb_fail = 1; + } + else { + Transform_Sha256_Len_p = NULL; + Transform_Sha256_p = Transform_Sha256; + ret = wc_Sha256Update(&sha, buf, WC_SHA256_BLOCK_SIZE + 5); + if (ret != 0) { + WB_NOTE("wc_Sha256Update remainder baseline failed"); + wb_fail = 1; + } + (void)wc_Sha256Final(&sha, hash); + wc_Sha256Free(&sha); + } + + /* idx0 TRUE, idx1 FALSE: an exact whole number of blocks, no remainder. */ + if (wc_InitSha256_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha256_ex failed (exact-block baseline skipped)"); + wb_fail = 1; + } + else { + Transform_Sha256_Len_p = NULL; + Transform_Sha256_p = Transform_Sha256; + ret = wc_Sha256Update(&sha, buf, WC_SHA256_BLOCK_SIZE); + if (ret != 0) { + WB_NOTE("wc_Sha256Update exact-block baseline failed"); + wb_fail = 1; + } + (void)wc_Sha256Final(&sha, hash); + wc_Sha256Free(&sha); + } + + Transform_Sha256_p = saved_p; + Transform_Sha256_Len_p = saved_len_p; + intel_flags = saved_flags; + WB_NOTE("Sha256Update save-remainder ret==0 pair exercised"); +} + +#else + +static void wb_update_transform_err(void) +{ + WB_NOTE("no retargetable sha256 transform pointer in this variant; " + "save-remainder ret==0 skipped"); +} + +#endif + int main(void) { printf("sha256.c white-box MC/DC supplement\n"); @@ -126,6 +252,7 @@ int main(void) return 0; #else wb_intel_dispatch(); + wb_update_transform_err(); printf("done (%s)\n", wb_fail ? "with skips" : "ok"); /* Setup failures are surfaced as skips, not test failures: the campaign * treats a nonzero exit as a failed variant and discards its coverage. */ diff --git a/tests/unit-mcdc/test_sha512_whitebox.c b/tests/unit-mcdc/test_sha512_whitebox.c index f2b1fa76c7..dfc97c4209 100644 --- a/tests/unit-mcdc/test_sha512_whitebox.c +++ b/tests/unit-mcdc/test_sha512_whitebox.c @@ -41,6 +41,28 @@ * to the portable C transform and Transform_Sha512_Len_p to NULL (routing * every block through the per-block path that reads intel_flags) -- which is * also crash-safe: the C transform runs whatever intel_flags claims. + * + * Second supplement: Sha512Update()'s save-remainder guard (line ~2185) + * + * if (ret == 0 && len > 0) + * + * idx0 ("ret == 0") FALSE half. ret is 0 everywhere on the path to that guard + * except where the block transform writes it: the two earlier error exits + * (buffLen >= block size -> BUFFER_E, and wc_Sha512Update's argument checks) + * return before the guard is reached. In the plain C / ARMASM variants + * Transform_Sha512() resolves to a transform with no failing return, so the + * FALSE half is only reachable where the transform is dispatched through the + * file-static Transform_Sha512_p -- i.e. exactly this USE_INTEL_SPEEDUP build. + * Retargeting that pointer at a stub returning BAD_FUNC_ARG (and NULLing + * Transform_Sha512_Len_p so the per-block loop, not the Len transform, runs) + * makes the first loop iteration break with ret != 0; the guard is then + * evaluated with idx0 FALSE and idx1 short-circuited (never read). + * Vectors, all in this one binary: + * stub + 3 whole blocks -> (F, -) [the residual] + * C transform + 1 block + 5 -> (T, T) + * C transform + exactly 1 block -> (T, F) + * No digest is taken from the faulted context: it is freed and re-initialised + * before the baseline vectors run. */ #include @@ -128,6 +150,109 @@ static void wb_intel_dispatch(void) #endif +/* -------------------------------------------------------------------------- + * Sha512Update(): "ret == 0" half of the save-remainder guard (~line 2185). + * ----------------------------------------------------------------------- */ +#if !defined(NO_SHA512) && defined(WOLFSSL_SHA512) && \ + defined(WOLFSSL_X86_64_BUILD) && defined(USE_INTEL_SPEEDUP) && \ + !defined(WC_C_DYNAMIC_FALLBACK) && \ + !defined(WC_NO_INTERNAL_FUNCTION_POINTERS) && \ + (defined(HAVE_INTEL_AVX1) || defined(HAVE_INTEL_AVX2)) + +static int wb_transform_fails(wc_Sha512* sha512) +{ + (void)sha512; + return BAD_FUNC_ARG; +} + +static void wb_update_transform_err(void) +{ + /* Three whole blocks: the loop breaks on the first transform (len is + * decremented before it), leaving len == 2 * block size at the guard -- + * immaterial, since idx1 is short-circuited when idx0 is FALSE. */ + byte buf[3 * WC_SHA512_BLOCK_SIZE]; + byte hash[WC_SHA512_DIGEST_SIZE]; + int (*saved_p)(wc_Sha512*) = Transform_Sha512_p; + int (*saved_len_p)(wc_Sha512*, word32) = Transform_Sha512_Len_p; + cpuid_flags_t saved_flags = intel_flags; + wc_Sha512 sha; + int ret; + + XMEMSET(buf, 0x5a, sizeof(buf)); + + /* idx0 FALSE: the transform fails on the first block. */ + if (wc_InitSha512_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha512_ex failed (transform-error case skipped)"); + wb_fail = 1; + } + else { + /* Pin the per-block route, then make that block transform fail. + * intel_flags is zeroed so the C transform the baselines use sees a + * byte-reversed block; it is restored at the end. */ + Transform_Sha512_Len_p = NULL; + Transform_Sha512_p = wb_transform_fails; + intel_flags = 0; + + ret = wc_Sha512Update(&sha, buf, (word32)sizeof(buf)); + if (ret == 0) { + WB_NOTE("faulted wc_Sha512Update unexpectedly succeeded"); + wb_fail = 1; + } + /* The context is poisoned; no digest is taken from it. */ + Transform_Sha512_p = _Transform_Sha512; + wc_Sha512Free(&sha); + } + + /* idx0 TRUE, idx1 TRUE: one whole block plus a 5-byte remainder. */ + if (wc_InitSha512_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha512_ex failed (remainder baseline skipped)"); + wb_fail = 1; + } + else { + Transform_Sha512_Len_p = NULL; + Transform_Sha512_p = _Transform_Sha512; + ret = wc_Sha512Update(&sha, buf, WC_SHA512_BLOCK_SIZE + 5); + if (ret != 0) { + WB_NOTE("wc_Sha512Update remainder baseline failed"); + wb_fail = 1; + } + (void)wc_Sha512Final(&sha, hash); + wc_Sha512Free(&sha); + } + + /* idx0 TRUE, idx1 FALSE: an exact whole number of blocks, no remainder. */ + if (wc_InitSha512_ex(&sha, NULL, INVALID_DEVID) != 0) { + WB_NOTE("wc_InitSha512_ex failed (exact-block baseline skipped)"); + wb_fail = 1; + } + else { + Transform_Sha512_Len_p = NULL; + Transform_Sha512_p = _Transform_Sha512; + ret = wc_Sha512Update(&sha, buf, WC_SHA512_BLOCK_SIZE); + if (ret != 0) { + WB_NOTE("wc_Sha512Update exact-block baseline failed"); + wb_fail = 1; + } + (void)wc_Sha512Final(&sha, hash); + wc_Sha512Free(&sha); + } + + Transform_Sha512_p = saved_p; + Transform_Sha512_Len_p = saved_len_p; + intel_flags = saved_flags; + WB_NOTE("Sha512Update save-remainder ret==0 pair exercised"); +} + +#else + +static void wb_update_transform_err(void) +{ + WB_NOTE("no retargetable sha512 transform pointer in this variant; " + "save-remainder ret==0 skipped"); +} + +#endif + int main(void) { printf("sha512.c white-box MC/DC supplement\n"); @@ -136,6 +261,7 @@ int main(void) return 0; #else wb_intel_dispatch(); + wb_update_transform_err(); printf("done (%s)\n", wb_fail ? "with skips" : "ok"); return 0; #endif