Under WC_C_DYNAMIC_FALLBACK, SAVE_VECTOR_REGISTERS2() can fail on any call, so
two calls on the same object can dispatch differently. Each of these
algorithms had state that silently assumed a single dispatch for its lifetime.
wolfcrypt/src/wc_mldsa.c: add MLDSA_NTT_AVX2()/MLDSA_INVNTT_AVX2() selecting
the "full" AVX2 NTT/invNTT under WC_C_DYNAMIC_FALLBACK. The non-full variants
leave NTT-domain coefficients in a permuted, lane-interleaved order that only
their matching consumers understand, whereas the full variants and the C
implementations use standard order. NTT-domain data at rest (cached s1/s2/t0
vectors, the challenge polynomial) can be produced and consumed by
differently-dispatched calls, so its representation must be dispatch-invariant.
Without fallback, dispatch is invariant and the ~2%/~4% faster permuted-order
variants are kept. Both pipelines are bit-identical end to end.
wolfcrypt/src/wc_mlkem_poly.c: in mlkem_derive_secret(), re-initialize the
shared SHAKE-256 object under WC_C_DYNAMIC_FALLBACK. The buffer-stuffing
shortcut assumes a freshly initialized (zeroed) sponge, which no longer holds
once the C fallback legs of mlkem_gen_matrix()/mlkem_get_noise() drive the XOF
on that object and leave it mid-squeeze.
wolfcrypt/src/wc_slhdsa.c: in slhdsakey_fors_sign(), replace the
CAN_SAVE_VECTOR_REGISTERS() test with an actual SAVE_VECTOR_REGISTERS2() == 0
acquisition and a matching RESTORE_VECTOR_REGISTERS(), so the region is held
rather than merely predicted to be available.
wolfcrypt/src/wc_frodokem_mat.c: in the AES row kernels of
frodokem_mul_add_as_plus_e_aes() and frodokem_mul_add_sa_plus_e_aes(), re-key
with wc_AesSetKeyDirect() when IS_INTEL_AESNI() but !aes->use_aesni. The
kernels consume aes->key directly, which holds an AES-NI-layout schedule only
if SetKey ran with vector registers available; under fallback a failed
SAVE_VECTOR_REGISTERS2() inside SetKey returns success having keyed only the
C-fallback schedule. Re-keying happens inside the held region, where the
nested save always succeeds. Loop conditions gain (ret == 0) so a re-key
failure stops the run.
wolfssl/wolfcrypt/settings.h: with the above, ML-KEM, ML-DSA, SLH-DSA and
FrodoKEM are fuzzer-clean, so the DEBUG_VECTOR_REGISTER_ACCESS_FUZZING
exclusion narrows from the _WC_BUILDING_WC_MLKEM_POLY_C / _WC_BUILDING_WC_MLDSA_C
/ _WC_BUILDING_WC_SLHDSA_C set to _WC_BUILDING_FALCON_C alone. Falcon stays
excluded because it uses FP or vector registers in all of its asm
implementations and there is no option yet to build the C-no-FP implementation
alongside them.
tests/api/test_mldsa.c: in test_mldsa_encode_w1_large_values(), pin dispatch to
the C path with WC_DEBUG_SET_VECTOR_REGISTERS_RETVAL() for the duration of the
test and restore it afterward. The two calls being compared are only specified
-- and only equal -- on the valid input domain, so letting the fuzzer send them
down different (AVX2 vs C) implementations is not a meaningful comparison.