Skip to content

Mcdc test coverage campaign - part 2#10876

Open
danielinux wants to merge 22 commits into
wolfSSL:masterfrom
danielinux:mcdc-test-coverage
Open

Mcdc test coverage campaign - part 2#10876
danielinux wants to merge 22 commits into
wolfSSL:masterfrom
danielinux:mcdc-test-coverage

Conversation

@danielinux

Copy link
Copy Markdown
Member

Description

Added new test cases to existing tests, added more white-box unit tests for mc-dc coverage campaign.

Modules under tests

AES

test_aes.c (+14): AES-EAX streaming Update authIn argument-check pair. test_aes_whitebox.c (+215): Intel-dispatch white-box forcing the file-static cpuid mask over the AES-NI vs C transform selection, the AES-NI internal pointer guards, plus an aarch64 hw-crypto section (use_aes_hw_crypto/PMULL, run in the qemu-aarch64 lane).

SHA

three white-box files: test_sha256_whitebox.c (+134), test_sha512_whitebox.c (+142), test_sha3_whitebox.c (+237), all exercising the Intel AVX/SHA-NI/BMI dispatch decisions (and sha3's aarch64 twin) by pinning cpuid flags while keeping the C transform. The source change (sha.c/sha3.c, guard reorder in wc_ShaUpdate/wc_Sha3Update/Shake*_Update) makes the data==NULL && len==0 empty-update decision reachable.

RSA

test_rsa.c (+208): DecisionCoverage over the public RSA surface. test_rsa_whitebox.c (+452): white-box for internal guards not reachable from the API.

SP-math (sp_int.c)

test_wolfmath.c (+776): six DecisionCoverage functions across the mp_/sp_ families — allocation (sp_init_size/sp_grow/sp_copy/sp_exch), shift (sp_set_bit/sp_2expt/sp_lshd/sp_rshb), single-digit and multi-precision arithmetic, conversion (sp_to_unsigned_bin_len/sp_tohex/sp_read_radix), and sp_invmod/sp_exptmod/sp_gcd/sp_prime_is_prime argument/degenerate-input checks. test_sp_int_whitebox.c (+150): closes the sp_count_bits/sp_cnt_lsb non-normalized-digit loops.

ECC

test_ecc.c (+683): test_wc_EccDecisionCoverage{,2,3,4} (split into four after a -fcoverage-mcdc -O0 stack-corruption crash) covering curve lookup, point-at-infinity, gen_k, init id/label, sign/verify length, DER point import/export, is_point, raw export, custom curves, X9.63-KDF. test_ecc_whitebox.c (+336) for the file-static helpers.

ChaCha20 / Poly1305

test_chacha.c (+88): NULL-arg independence pairs for SetIV/SetKey/Process (both C and Intel physical copies), leftover-block decision, unaligned/half-key cases, wc_XChacha_SetKey. test_poly1305.c (+26): wc_Poly1305Update compound-condition operands. White-boxes test_chacha_whitebox.c (+143) / test_poly1305_whitebox.c (+124): the Intel cpuidFlags/intel_flags dispatch decisions.

HMAC / CMAC

test_hmac.c (+259): HmacSizeByType, HmacCopy, HmacInit_Id/_Label, HmacFree cleanup, HKDF NULL edge cases (drove hmac.c to 100%). test_cmac.c (+414): empty-message / NULL-outSz / small-tag pairs plus CMAC_Grow, InitCmac_Id/_Label, AesCmacGenerate/Verify DecisionCoverage. test_cmac_whitebox.c (+204): the static _InitCmac_common id/label paths, each paired with a same-binary all-false baseline (the cross-binary MC/DC-merge gotcha).

Curve25519 / Ed25519

test_curve25519.c (+453): make_priv, import_public_ex, check_public LE/BE boundaries, generic/generic_blind, set_rng, the full WC_X25519_NONBLOCK state machine. test_ed25519.c (+473): ctx/ph sign+verify, streaming verify_init/update/final incl. the S-order boundary, check_key, import variants, make_public arg-checks. White-boxes test_curve25519_whitebox.c (+246) / test_ed25519_whitebox.c (+148) for internal static guards.

DH / DSA

test_dh.c (+850): 12 DecisionCoverage functions — SetKey family, named-group helpers, GenerateKeyPair/GeneratePublic plus a full multi-group generate+agree(+CT) round trip, the WC_DH_NONBLOCK state machine, import/export key-pair, CheckPubKey/CheckPrivKey/CheckKeyPair (+WOLFSSL_VALIDATE_DH_KEYGEN), GenerateParams/ExportParamsRaw, CheckDhLN divLen pair. test_dsa.c (+305): Sign/Verify digest-size bad-args, per-arg NULL isolation for the import/export-params/export-key-raw calls, CheckDsaLN divLen pair. (No white-box needed — all decisions API-reachable.)

Random (random.c)

test_random.c (+404): RNG_HealthTest_SHA256/512_Ext (the ACVP _ex/_ex2 entry points), RNG_SeedCb, RNG_CustomRandBlock, RNG_DrbgDisable, extended HealthTest bad-parameter cases. test_random_whitebox.c (+224): the Hash_gen/Hash512_gen and array_add internal false-sides.

Net campaign result

union MC/DC of 1,875 / 2,609 conditions (71.9%) across those 14 files, with four modules (chacha, hmac, cmac, sha3) at 100%. The tests/unit-mcdc/ binaries are the reusable white-box machinery; the tests/api/ additions integrate into wolfSSL's normal test groups.

Commit recap:

02441c4 tests: MC/DC decision coverage for random.c (Hash_DRBG + seed layer)
bae034a tests: MC/DC decision coverage for dh.c and dsa.c
8fc164c tests: MC/DC decision coverage for curve25519.c and ed25519.c
7da1a94 tests: MC/DC decision coverage for hmac.c and cmac.c
617d8fa tests: MC/DC decision coverage for chacha.c and poly1305.c
000bc45 tests/ecc: MC/DC decision coverage + white-box for wolfcrypt/src/ecc.c
f75d0d2 tests: add sp_int.c (sp-math module) MC/DC DecisionCoverage + white-box
c7c9dba tests/rsa: MC/DC decision + white-box coverage for wolfcrypt/src/rsa.c
35a257a tests/unit-mcdc: aarch64 hw-crypto white-box supplements (qemu lane)
4cb8ccc sha: reorder update arg guards to make the empty-update decision coverable
befc8db tests: cover the AES-EAX streaming Update authIn arg-check
3102b88 tests/unit-mcdc: add SHA intel-dispatch white-box supplements

Surfaced defects

See PR #10875

Copilot AI review requested due to automatic review settings July 10, 2026 07:07
@danielinux danielinux self-assigned this Jul 10, 2026
@danielinux danielinux mentioned this pull request Jul 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR continues the MC/DC coverage campaign by adding a large set of decision/edge-path tests (including “white-box” unit-mcdc binaries that #include implementation .c files) across multiple wolfCrypt modules, plus a small source tweak in SHA/SHA3 update paths to make “empty update” decisions coverable.

Changes:

  • Reorders SHA-1 and SHA-3/SHAKE update argument guards so the data == NULL && len == 0 empty-update path is explicitly reachable for coverage.
  • Adds extensive API-level DecisionCoverage tests across RSA, RNG, CMAC/HMAC, ChaCha/Poly1305, Curve25519/Ed25519, ECC, DSA, and SP-math.
  • Introduces multiple tests/unit-mcdc/*_whitebox.c translation units that directly include wolfCrypt .c sources to reach file-static/internal decisions.

Reviewed changes

Copilot reviewed 38 out of 38 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
wolfcrypt/src/sha.c Reorders wc_ShaUpdate() argument checks to allow explicit empty-update coverage.
wolfcrypt/src/sha3.c Reorders SHA3/SHAKE update argument checks similarly for empty-update coverage.
tests/unit-mcdc/test_sp_int_whitebox.c New white-box MC/DC supplement for non-normalized sp_int internal loops.
tests/unit-mcdc/test_sha256_whitebox.c New white-box supplement forcing x86_64 SHA-256 dispatch conditions.
tests/unit-mcdc/test_sha512_whitebox.c New white-box supplement forcing x86_64 SHA-512 dispatch conditions.
tests/unit-mcdc/test_sha3_whitebox.c New white-box supplement forcing SHA-3 dispatch decisions (x86_64 + aarch64 lanes).
tests/unit-mcdc/test_random_whitebox.c New white-box supplement covering structurally unreachable random.c internal guards.
tests/unit-mcdc/test_chacha_whitebox.c New white-box supplement exercising Intel ChaCha dispatch non-AVX2 sides.
tests/unit-mcdc/test_poly1305_whitebox.c New white-box supplement exercising Intel Poly1305 AVX1-only side.
tests/unit-mcdc/test_cmac_whitebox.c New white-box supplement driving unreachable CMAC id/label cross-combinations.
tests/unit-mcdc/test_curve25519_whitebox.c New white-box supplement reaching WC_X25519_NONBLOCK internal guards.
tests/unit-mcdc/test_ed25519_whitebox.c New white-box supplement calling file-static ed25519_hash() directly.
tests/unit-mcdc/test_ecc_whitebox.c New white-box supplement calling ECC file-static helpers directly.
tests/unit-mcdc/test_aes_whitebox.c Extends AES white-box coverage (AES-NI + aarch64 hw-crypto/GCM-streaming decisions).
tests/api/test_wolfmath.h Registers new SP-math DecisionCoverage test entry points.
tests/api/test_rsa.c Adds RSA API DecisionCoverage and extra feature-coverage paths.
tests/api/test_random.h Registers new RNG DecisionCoverage test entry points.
tests/api/test_random.c Adds extended RNG health-test, callback, custom-block, and DRBG-disable coverage.
tests/api/test_poly1305.c Adds Poly1305 operand-independence and padding/AD edge-case coverage.
tests/api/test_hmac.h Registers new HMAC/HKDF DecisionCoverage test entry points.
tests/api/test_hmac.c Adds new HMAC/HKDF DecisionCoverage tests and CryptoCb cleanup coverage.
tests/api/test_ed25519.h Registers new Ed25519 DecisionCoverage test entry points.
tests/api/test_ed25519.c Adds multiple Ed25519 DecisionCoverage tests (ctx/ph, streaming verify, import/make_public arg checks).
tests/api/test_ecc.h Registers ECC DecisionCoverage test entry points (split into 4 functions).
tests/api/test_dsa.h Registers new DSA DecisionCoverage test entry points.
tests/api/test_dsa.c Adds DSA argument/edge-path tests and parameter import/export edge cases.
tests/api/test_dh.h Registers new DH DecisionCoverage test entry points.
tests/api/test_curve25519.h Registers new Curve25519 DecisionCoverage test entry points.
tests/api/test_curve25519.c Adds Curve25519 argument checks, public-key validation edge cases, and nonblock state-machine coverage.
tests/api/test_cmac.h Registers new CMAC DecisionCoverage test entry points.
tests/api/test_cmac.c Adds CMAC DecisionCoverage tests including CryptoCb mismatch path.
tests/api/test_chacha.h Registers new XChaCha20 test entry point.
tests/api/test_chacha.c Adds ChaCha arg/edge-path coverage including XChaCha20 setup.
tests/api/test_aes.c Adds AES-EAX streaming authIn argument-check independence pair coverage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/api/test_hmac.c
Comment thread tests/api/test_hmac.c
Comment thread tests/api/test_hmac.c
@danielinux

Copy link
Copy Markdown
Member Author

depends on #10875

MC/DC white-box supplements for the sha module of the ISO 26262 campaign,
one per involved file that has an x86-64 USE_INTEL_SPEEDUP runtime
dispatch: test_sha256_whitebox.c, test_sha512_whitebox.c,
test_sha3_whitebox.c. Each #includes its wolfcrypt/src/shaXXX.c so the
file-static cpuid mask (intel_flags / cpuid_flags) and transform function
pointers are in scope.

On an AVX2-capable host the runtime only ever takes the AVX2 branch of the
IS_INTEL_AVX1/AVX2/SHA/BMI dispatch decisions, so their not-taken
conditions are unreachable from tests/api (the aesni-class residual). The
supplements force the mask to each of {0, AVX1, AVX2, SHA, BMI1|BMI2, ...}
to show every condition's independence pair, while pinning the transform
pointer to the portable C path so a claimed-but-absent feature (e.g.
SHA-NI) never executes its asm -- crash-safe on any host.

Two mechanics were needed for full coverage: the host's multi-block 'Len'
transform bypasses the per-block byte-reverse decision, so the pointer is
set NULL to route through the per-block path; and the byte-reverse
decisions live in several update sub-paths (buffered-block completion,
bulk loop, final on/over the padding boundary) that are each driven with
the matching update granularity. The sha3 multi-block fast-path decision
needs both the NULL and non-NULL block-pointer rows in the same binary, so
both are exercised.

Closes the Intel dispatch false-sides in the union: sha256.c 19->25/26,
sha512.c 25->35/36, sha3.c 41->45/49. Remaining gaps are structural/lane
residuals (guard ordering, transform-failure, an AArch64-only twin);
documented in the campaign's reports/sha/RESIDUALS.md.
test_wc_AesEaxArgMcdc exercised the eax/out/in operands of
wc_AesEaxEncryptUpdate / wc_AesEaxDecryptUpdate but always passed
(authIn=NULL, authInSz=0), so the guard's authInSz>0 && authIn==NULL
term was never evaluated with authInSz>0 -- leaving those two
conditions (and their decrypt twins) uncovered in the MC/DC union.

Add, for both Update functions, the (authIn==NULL, authInSz>0) row
(rejected with BAD_FUNC_ARG) and the (authIn!=NULL, authInSz>0) row
(accepted), completing both conditions' independence pairs.

These four conditions were the only uncovered code the recent
master merge (AES-GCM-SIV, AES-OFB/CFB callbacks) added to aes.c that
was reachable from tests/api; closes them so the aes.c union returns
to its residual-only gap (410/445, gap 35).
…rable

wc_ShaUpdate, wc_Sha3Update, wc_Shake128_Update and wc_Shake256_Update
guarded inputs as:
    if (obj == NULL || (data == NULL && len > 0)) return BAD_FUNC_ARG;
    if (data == NULL && len == 0) return 0;
The first guard rejected (data==NULL, len>0) before the second decision,
so that decision's len==0 condition could only ever be observed true --
its MC/DC independence pair was structurally unreachable.

Reorder to the same idiom sha256.c/sha512.c already use:
    if (obj == NULL) return BAD_FUNC_ARG;
    if (data == NULL && len == 0) return 0;   /* (NULL,len>0) now reaches: len==0 false */
    if (data == NULL) return BAD_FUNC_ARG;
Behavior is identical for every input; the existing DIGEST_UPDATE_TEST
cases wc_*Update(&dgst, NULL, 1) and (&dgst, NULL, 0) now exercise both
sides of the decision. Closes the four guard-ordering MC/DC residuals in
the sha campaign module (sha.c and sha3.c).
Adds #if defined(__aarch64__) && defined(WOLFSSL_ARMASM) sections to
test_aes_whitebox.c and test_sha3_whitebox.c that close the AArch64-only
MC/DC residuals the qemu-aarch64 emulator lane compiles but a native
white-box cannot reach (qemu -cpu max always advertises AES+PMULL, so the
runtime hw-crypto flags are always true on that lane):

- aes.c: the 9-site `aes->use_aes_hw_crypto && aes->use_pmull_hw_crypto`
  dispatch (wc_AesGcmSetKey's H generation, the static GHASH_INIT helper,
  the one-shot Encrypt/Decrypt, and the streaming Init/EncryptUpdate/
  EncryptFinal/DecryptUpdate/DecryptFinal quintet), forced via the
  Check_CPU_support_HwCrypto() cpuid_flags cache, plus the 4 internal
  AesGcm*Update_AARCH64 pointer guards, called directly.
- sha3.c: the aarch64 twin of the Intel InitSha3 cached-dispatch decision
  (line ~759), cond1 (SHA3_BLOCK != NULL), mirroring the existing
  Intel-737 wb_init_with idiom.

Both sections reduce to a no-op stub outside the qemu-aarch64 lane, so the
files still compile+run natively unchanged. Verified against real
qemu-aarch64 -cpu max: all 9 aes.c hw-crypto sites show independence pairs
on both conditions, the 4 pointer-guard conditions' targeted halves show
covered, and the sha3.c:759 cond1 pair closes.
Extend test_wc_RsaDecisionCoverage / test_wc_RsaFeatureCoverage and add a
tests/unit-mcdc white-box supplement (test_rsa_whitebox.c) for the per-module
MC/DC campaign, raising rsa.c union MC/DC from 67/268 to 156/283.

DecisionCoverage/FeatureCoverage additions (API-reachable argument and feature
paths): wc_RsaFunction 7-way arg check, wc_RsaDirect, wc_InitRsaKey_Id/Label,
wc_MakeRsaKey size check, wc_CheckProbablePrime, wc_RsaPSS_CheckPadding, OAEP
label mismatch (encrypt + decrypt), and a WC_RSA_NO_PADDING raw round trip.
A PSS-SHA512-on-1024-bit-key case is staged behind if(TEST_RSA_BITS==1024).

White-box supplement (file-static helpers shielded by a public pre-guard, so
their argument checks are unreachable from the API): _NewRsaKey_common,
_RsaExportKey, _RsaFlattenPublicKey, wc_CompareDiffPQ, _RsaPrivateKeyDecodeRaw,
RsaPad, RsaUnPad, _CheckProbablePrime. Both halves of each independence pair
are exercised within the white-box binary. Modeled on test_aes_whitebox.c;
main() always returns 0 so a setup failure is a skip, not a discarded variant.

Selftest/FIPS-sensitive assertions stay inside the existing
!defined(HAVE_SELFTEST) guard and the OAEP cases stay under !defined(HAVE_FIPS),
matching the committed idiom for this frozen-boundary file.
tests/api/test_wolfmath.c gains six DecisionCoverage test functions
covering sp_int.c's mp_*/sp_* API: the allocation family (sp_init_size,
sp_grow, sp_copy, sp_exch), the shift family (sp_set_bit, sp_2expt,
sp_lshd, sp_rshb), the single-digit and multi-precision arithmetic
families (sp_add_d/sp_sub_d/sp_mul_d/sp_div_d/sp_mod_d/sp_div_2/
sp_div_2_mod_ct/sp_add/sp_sub/sp_addmod_ct/sp_submod_ct/sp_div,
including their WOLFSSL_SP_INT_NEGATIVE sign-path counterparts), the
conversion family (sp_div_2d/sp_mod_2d/sp_mul_2d/sp_sqrmod/
sp_mont_red_ex/sp_to_unsigned_bin_len(_ct)/sp_tohex/sp_read_radix), and
the sp_invmod/sp_exptmod_ex/sp_gcd/sp_prime_is_prime(_ex) top-level
argument and degenerate-input checks. Each exercises an internal
size/capacity guard or argument check via a deliberately undersized
destination (sp_init_size with a small size) or an out-of-range
argument - legitimate, public ways to reach decisions valid-sized RSA/
ECC/DH usage never trips.

tests/unit-mcdc/test_sp_int_whitebox.c is a new white-box supplement
(compiles sp_int.c in directly) closing the sp_count_bits/sp_cnt_lsb
non-normalized-digit trim loops: no public caller can produce that
state since every public mutator normalizes via sp_clamp before
returning.

Part of the ISO 26262 per-module MC/DC campaign's sp-math module
(wolfcrypt/src/sp_int.c, Phase 1): 412/547 (75.32%) MC/DC across 6
build-variant axes (WOLFSSL_SP_MATH_ALL vs bare WOLFSSL_SP_MATH,
WOLFSSL_SP_SMALL, WOLFSSL_SP_INT_NEGATIVE, WOLFSSL_SP_DIV_WORD_HALF,
WOLFSSL_SMALL_STACK) + the white-box, up from a 318/547 baseline. The
remaining gaps are deep invmod/exptmod/prime/gcd internal state-machine
internals, the SP-accelerated-backend-entangled RSA/DH key-size
dispatch, and other structural residuals; campaign-side files (config,
module registry, baseline) live in the separate testing repo.
Adds test_wc_EccDecisionCoverage{,2,3,4} to tests/api/test_ecc.c (split
into four functions after a single giant one triggered a -fcoverage-mcdc
+ -O0 stack-corruption crash under this campaign's build, reproduced and
root-caused with gdb: a plain on-stack mp_int's used/size fields were
already garbage immediately after its own mp_init()) and a new
tests/unit-mcdc/test_ecc_whitebox.c white-box supplement, closing curve
lookup, point-is-at-infinity, gen_k, init_id/init_label, sign/verify
length, point DER import/export, is_point, export_public_raw/private_raw,
rs_raw_to_sig, ctx_set_kdf_salt, set_custom_curve, X963_KDF, curve_load,
_ecc_import_private_key_ex, and ecEncCtx protocol-guard decisions.

ecc.c union MC/DC across all 6 campaign variants: 217/597 -> 287/597.
Per-module ISO 26262 MC/DC campaign (iso26262-mcdc-per-module). Adds
DecisionCoverage-style cases to tests/api/test_chacha.c and
test_poly1305.c, plus intel-dispatch white-box supplements mirroring
the aes/sha3 technique.

chacha.c: BEFORE 6/11 (54.55%) -> AFTER 13/13 (100%). Closed
wc_Chacha_SetIV/SetKey's NULL-argument independence pairs,
wc_Chacha_Process's input/output NULL-argument pairs (both the
portable-C and USE_INTEL_CHACHA_SPEEDUP physical copies), and the
(msglen>0 && ctx->left>0) leftover-block decision's msglen==0 side.
Also adds an unaligned-key SetKey case (XSTREAM_ALIGN), a half-length
key case, and a new test_wc_Chacha_XChachaSetKey covering
wc_XChacha_SetKey. Total rose from 11 to 13 once the intelasm variant
was fixed (see campaign notes): it carries its own physical copy of
the leftover decision that the portable-only union previously missed.

poly1305.c: BEFORE 10/14 (71.43%) -> AFTER 12/14 (85.71%). Closed
wc_Poly1305Update's (m==NULL && bytes>0) inner AND, both operands.
Also adds an addSz==0 case to wc_Poly1305_MAC and a
lenToPad==WC_POLY1305_PAD_SZ case to wc_Poly1305_Pad for API
robustness. The 2 remaining conditions are structural/dead-code
residuals: wc_Poly1305SetKey's compound key==NULL check is shadowed
by an earlier unconditional key==NULL guard a few lines above (dead
code); wc_Poly1305_Pad's paddingLen<WC_POLY1305_PAD_SZ operand is
always true whenever paddingLen>0 (paddingLen is a mod-16 value
bounded to 0..15). See campaign baselines.json for full detail.
hmac.c: BEFORE 19/37 (51.35%) -> AFTER 37/37 (100%). Closed
wc_HmacSizeByType's own hash-type compound (separate physical decision
from wc_HmacSetKey_ex's copy), wc_HmacCopy's NULL guard,
wc_HmacInit_Id/wc_HmacInit_Label's length/NULL guards, wc_HmacFree's
devId/devCtx cleanup guard, and wc_HKDF_Extract_ex/wc_HKDF_Expand_ex's
NULL-inKey guard at both physical locations. No white-box needed; every
decision is reachable through the public API once WOLF_PRIVATE_KEY_ID /
WOLF_CRYPTO_CB are exercised.

cmac.c: BEFORE 19/58 (32.76%) -> AFTER 58/58 (100%). Closed independence
pairs across wc_CmacUpdate/wc_CMAC_Grow/wc_AesCmacGenerate's NULL+len
guards, wc_CmacFinalNoFree's outSz/tag-size guards, new
wc_InitCmac_Id/wc_InitCmac_Label tests plus a new
tests/unit-mcdc/test_cmac_whitebox.c supplement for _InitCmac_common's
id/label cross-combination leaves that are structurally unreachable
through the public wrappers, new DecisionCoverage tests driving
wc_AesCmacGenerate_ex/wc_AesCmacVerify_ex directly, and a WOLF_CRYPTO_CB
test reaching wc_AesCmacVerify_ex's aSz-mismatch guard (unreachable in
native software builds without a callback that violates the length
contract).

Both modules verified across all native variants (including
WOLF_PRIVATE_KEY_ID and WOLF_CRYPTO_CB axes) with zero build/test
failures.
Per-module MC/DC campaign (iso26262-mcdc-per-module). Adds decision-
coverage test cases to tests/api/test_curve25519.c and test_ed25519.c,
plus tests/unit-mcdc white-box supplements for file-static helpers
unreachable through any public wrapper (all callers pre-validate
identically before reaching them).

curve25519.c: BEFORE 26/64 (40.62%) -> AFTER 62/64 (96.88%).
  Remaining 2: curve25519_smul_blind's RNG-retry loop (needs a
  mockable RNG). Found and documented (not fixed - test-only
  campaign) a source asymmetry bug in wc_curve25519_check_public's
  BIG_ENDIAN branch (checks pub[i]!=0 where the mirrored
  LITTLE_ENDIAN branch checks pub[i]!=0xff), and a build-blocking gap
  in curve25519.c's WOLFSSL_CURVE25519_NOT_USE_ED25519+CURVED25519_X64
  path (curve25519_base() has no header prototype anywhere in the
  tree; fails under a strict C17 compiler).

ed25519.c: BEFORE 45/89 (50.56%) -> AFTER 81/89 (91.01%).
  Remaining 8: 5 need a mockable hash/malloc failure to reach a
  ret==0 FALSE side after a successful ed25519_hash() call; 3 are
  WOLFSSL_CHECK_VER_FAULTS's redundant post-verify ConstantCompare,
  a deterministic double-call on identical inputs that cannot
  diverge without memory corruption in between.

Both modules build and pass across every native variant (backend
axis, blinding/non-blinding, WC_X25519_NONBLOCK, USE_INTEL_SPEEDUP,
WOLFSSL_ED25519_PERSISTENT_SHA/STREAMING_VERIFY) with zero variant
failures.
Adds tests/api DecisionCoverage functions closing MC/DC gaps identified
by the per-module campaign (iso26262/mcdc-per-module):

dh.c:    BEFORE 51/173 (29.48%) -> AFTER 107/173 (61.85%), union across
         6 native variants (sp_default, sp_dh, sp_dh_nonblock,
         small_stack, no_dh186, validate_keygen). New coverage: wc_DhSetKey
         family bad-args and the FFDHE-table primality fast-path, named-key
         helpers (SetNamedKey/GetNamedKeyParamSize/CopyNamedKey/CmpNamedKey),
         wc_DhGenerateKeyPair/GeneratePublic bad-args plus a multi-group
         (2048/3072/4096) generate+agree(+ct) round trip, WC_DH_NONBLOCK's
         incremental state machine, wc_DhImportKeyPair/ExportKeyPair,
         wc_DhCheckPubKey(_ex)/wc_DhCheckPrivKey(_ex)/wc_DhCheckKeyPair
         (+ WOLFSSL_VALIDATE_DH_KEYGEN), wc_DhGenerateParams/
         wc_DhExportParamsRaw, and CheckDhLN's divLen==224/256 MC/DC pair.

dsa.c:   BEFORE 34/110 (30.91%) -> AFTER 51/110 (46.36%), union across
         4 native variants (default, invmod_ct, fastmath, small_stack).
         New coverage: wc_DsaSign_ex/wc_DsaVerify_ex digestSz bad-argument
         checks and the q==1 qMinus1-iszero guard, individual single-operand
         NULL-argument combinations for ImportParamsRaw/ExportParamsRaw/
         ExportKeyRaw, and CheckDsaLN's case-2048 divLen==224/256 MC/DC pair.

Both modules build and pass their tests/api group + KATs across every
native variant with zero failures (10/10 variant runs).
BEFORE 15/65 (23.08%) -> AFTER 55/65 (84.62%) MC/DC on wolfcrypt/src/random.c,
measured across 5 native user_settings.h variants (default, WOLFSSL_SMALL_STACK,
WOLFSSL_SMALL_STACK_CACHE, WC_RNG_SEED_CB, CUSTOM_RAND_GENERATE_BLOCK) plus a
new tests/unit-mcdc/test_random_whitebox.c white-box supplement, in the
ISO 26262 per-module MC/DC campaign.

tests/api/test_random.c / test_random.h:
- Add test_wc_RNG_HealthTest_SHA256_Ext / test_wc_RNG_HealthTest_SHA512_Ext,
  exercising the previously-untested ACVP-oriented extended health-test entry
  points (wc_RNG_HealthTest_SHA256_ex, wc_RNG_HealthTest_SHA512_ex/_ex2):
  nonce/personalization-string/additional-input/reseed-entropy presence and
  absence, in both standard and prediction-resistance modes, including
  "valid pointer + zero size" calls needed to isolate each leaf's size
  operand independently of its pointer operand for MC/DC.
- Add test_wc_RNG_SeedCb (WC_RNG_SEED_CB custom seed callback: success,
  failing callback, and no-callback-installed paths).
- Add test_wc_RNG_CustomRandBlock (CUSTOM_RAND_GENERATE_BLOCK bypass path).
- Add test_wc_RNG_DrbgDisable (wc_Sha256Drbg_Disable/Enable/IsDisabled and
  the wc_Sha512Drbg_* equivalents: drbgType selection and the "can't disable
  both" BAD_STATE_E guard).
- Extend existing HealthTest bad-parameter coverage (reseed-without-seedB on
  wc_RNG_HealthTest_ex / wc_RNG_HealthTest_SHA512 / wc_RNG_HealthTest_SHA512_ex,
  and the untested wc_RNG_HealthTest_SHA512_ex2 3-operand bad-parameter guard).
- Guard test_wc_GenerateSeed against CUSTOM_RAND_GENERATE_BLOCK, whose
  wc_GenerateSeed() ladder intentionally has no implementation in that
  configuration (would otherwise be a link error, not a test failure).

tests/unit-mcdc/test_random_whitebox.c (new):
- White-box #include of random.c closing two structurally-unreachable-via-API
  leaves: Hash_gen()/Hash512_gen()'s "out != NULL && outSz != 0" false side,
  and array_add()'s "dLen > 0 && sLen > 0 && dLen >= sLen" false sides.

Residuals (10 of 65, documented in db/modules.json / baselines.json in the
paired testing-repo change): 4 WOLFSSL_SMALL_STACK allocation-failure
branches (no fault injection); 4 Hash_DRBG_Init/Hash512_DRBG_Init chained
Hash_df(...)==DRBG_SUCCESS conditions (transform-failure, SHA-256/512 never
fail on valid input); 2 Hash_gen()/Hash512_gen() "outSz != 0" leaves that are
structurally unsatisfiable given the caller's own outSz normalization and
loop-bound arithmetic (not merely hard to reach).

Bugs/interactions found while bringing up the CUSTOM_RAND_GENERATE_BLOCK
build variant (reported, not source-fixed): (1) random.c's PollAndReSeed()
is guarded only by "#ifdef HAVE_HASHDRBG" (not also
"!defined(CUSTOM_RAND_GENERATE_BLOCK)" like _InitRng()) and its non-callback
path unconditionally calls wc_GenerateSeed(), whose implementation ladder has
an intentionally empty CUSTOM_RAND_GENERATE_BLOCK arm -- an undefined-symbol
link error if a user_settings.h ever forces HAVE_HASHDRBG on together with
CUSTOM_RAND_GENERATE_BLOCK (this campaign's config now avoids the
combination instead of forcing it). (2) on this host, glibc's vDSO-
accelerated getrandom() does not validate the output buffer before writing
to it: wc_GenerateSeed(non-NULL os, NULL output, sz) segfaults inside
getrandom_vdso() instead of returning an error, so
TEST_WC_GENERATE_SEED_PARAMS is deliberately left undefined in the campaign
config for this test-only bad-parameter block (documented in
configs/random/user_settings.base.h).
test_wc_EccDecisionCoverage2 broke the unit.test link in several CI
configs (net-snmp, C#/Rust wrappers, make check/analyze, etc.):

- The wc_ecc_check_r_s_range block calls mp_init/mp_read_radix/mp_copy/
  mp_clear, which resolve to sp_* under WOLFSSL_SP_MATH_ALL and are only
  exported with WOLFSSL_PUBLIC_MP. Add defined(WOLFSSL_PUBLIC_MP) to its
  guard (matching the other mp_*-using blocks in this file).
- wc_ecc_export_point_der_compressed is WOLFSSL_LOCAL (hidden in a shared
  library) so it is not linkable from the shared-library unit test. Drop
  the four direct calls; its decision coverage is driven by the ecc
  white-box (which includes ecc.c). The public compressed path
  wc_ecc_export_x963_ex(...,1) is retained.

Verified: --enable-net-snmp and --enable-all (+WOLFSSL_PUBLIC_MP) both
build and link; unit.test passes.
Three more config-specific failures surfaced once the ecc link error was
resolved:

- test_wolfmath.c: test_wc_SpIntExptGcdDecisionCoverage called sp_gcd, whose
  definition (sp_int.c) is guarded by !NO_RSA && WOLFSSL_KEY_GEN - narrower
  than its sp_int.h prototype (|| ). Guard the sp_gcd block with the same
  condition so builds like --enable-curl (RSA on, key-gen off) link.
- test_cmac.c: the crypto-cb badlen callback dereferences wc_CryptoInfo's
  cmac member (WOLFSSL_CMAC only) and uses the Cmac type, but was guarded by
  WOLF_CRYPTO_CB alone; configs with the callback framework but no CMAC
  (e.g. --enable-wolftpm) failed to compile. Match the callback's guard to
  its only caller (WOLFSSL_CMAC && !NO_AES && WOLFSSL_AES_128 &&
  WOLF_CRYPTO_CB).
- test_dsa.c: test_wc_DsaExportKeyRaw_individual_args re-initialized WC_RNG
  three times but freed it once, leaking two DRBGs (caught by LeakSanitizer
  in the --enable-all sanitizer build). Free the RNG before each re-init,
  mirroring the existing DsaKey free-before-reinit.

Verified: --enable-curl and --enable-wolftpm build/link; --enable-all with
-fsanitize=leak runs unit.test leak-free (test_wc_DsaExportKeyRaw_individual_args
passes).
…lity

The sp_int helper functions these tests call have varied, narrow definition
guards in sp_int.c (e.g. sp_div_2d/sp_mod_2d/sp_mul_2d/sp_tohex need
WOLFSSL_SP_MATH_ALL && !WOLFSSL_RSA_VERIFY_ONLY; the ct helpers need HAVE_ECC;
sp_gcd needs !NO_RSA && WOLFSSL_KEY_GEN). The previous
(WOLFSSL_SP_MATH_ALL || WOLFSSL_SP_MATH) && WOLFSSL_PUBLIC_MP guard let the
tests compile in configs where some of those helpers are not built, producing
undefined-reference link errors (sp_gcd, sp_div_2d, ...) in builds like
--enable-curl and the pq-small matrix configs. Replace it with the union of
the helpers' requirements, which the campaign sp-math config satisfies so
coverage is unchanged, and drop the now-redundant per-call sp_gcd guard.
The all-check-mem-zero CI config (--enable-all -DWOLFSSL_CHECK_MEM_ZERO)
aborted unit.test (exit 134) in two of the new decision-coverage tests.
Both stem from wolfSSL mem-zero-tracking gaps that these tests are the
first to exercise; the underlying decisions are covered in every normal
build, so guard the specific triggers out of the instrumented build:

- test_rsa.c (test_wc_RsaDecisionCoverage): calling wc_MakeRsaKey() with an
  out-of-range size makes it 'goto out' and run mp_memzero_check() over its
  not-yet-initialized local temporaries, which over-scans the stack and
  false-positives on the still-registered, legitimately non-zero key->d of
  the key made earlier in the test. Skip the two bad-size calls under the
  instrumented build.
- test_dh.c (test_wc_DhImportExportKeyPair): wc_DhImportKeyPair() registers
  key->priv via mp_memzero_add(), but wc_FreeDhKey() clears it with
  mp_forcezero() (which does not deregister) and has no wc_MemZero_Check()
  like wc_FreeRsaKey() does, so the registration leaks into later tests.
  Skip this import/export test under the instrumented build.

Verified: --enable-all -DWOLFSSL_CHECK_MEM_ZERO builds and unit.test passes
(exit 0, zero mem-zero violations).
test_wc_DhGenerateParams_and_ExportRaw asserted wc_DhGenerateParams()==0,
but the bare WOLFSSL_SP_MATH backend cannot generate DH domain parameters
(returns PRIME_GEN_E), so the all-pq-sp-math CI config (--enable-sp-math)
failed the assertion. Guard the test on !defined(WOLFSSL_SP_MATH); the
generate/export decisions are covered with WOLFSSL_SP_MATH_ALL, fastmath
and heapmath. Verified: --enable-sp-math unit.test passes (test skipped);
the test still runs under --enable-all.
The mem-zero registration leak this guard worked around is fixed at the
library level in wc_FreeDhKey() on the sibling branch (fixes-2026-07-10 /
PR 10875). Drop the !WOLFSSL_CHECK_MEM_ZERO guard so the import/export test
runs and validates that fix. This test therefore depends on PR 10875 to
pass the all-check-mem-zero CI config.
…ZERO

The mem-zero false-positive these calls tripped is fixed at the library
level in wc_MakeRsaKey() on the sibling branch (fixes-2026-07-10 / PR
10875), which zero-initializes its stack temporaries so the early-out
mp_memzero_check() is safe. Drop the !WOLFSSL_CHECK_MEM_ZERO guard so the
RsaSizeCheck decision is exercised in the instrumented build too. Depends
on PR 10875 for the all-check-mem-zero config.
Two MC/DC tests asserted pre-fix behavior; the corresponding library
fixes changed the observable result:

- test_wc_DsaImportParamsRaw_individual_args: the untrusted-import
  primality rejection now surfaces DH_CHECK_PUB_E, since CheckDsaLN is
  gated on err==MP_OKAY (commit 30ceba0) and no longer overwrites it
  with BAD_FUNC_ARG.

- test_wc_curve25519_check_public_be: the big-endian "order or higher"
  loop now compares pub[i] != 0xff (symmetric with the little-endian
  branch, commit 600880a), so the rejection input needs pub[1..30]
  == 0xff, not 0x00.

Updated the assertions and the now-stale explanatory comments. Both
groups pass under --enable-all --enable-intelasm; full make check green.
@danielinux danielinux force-pushed the mcdc-test-coverage branch from 3c8a444 to 0d62149 Compare July 10, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants