Falcon: save/restore vector/FP registers around signing and keygen

Every fpr backend except the default integer-emulated one runs on the FP/vector
register file: WOLFSSL_FALCON_FPR_DOUBLE (C double -> SSE) and the generated
x86-64 fpr asm use xmm, and the folded AVX2/NEON FFT uses ymm / Q registers. All
of them must save and restore those registers around the work that uses fpr --
kernel FPU state under WOLFSSL_LINUXKM and the DEBUG_VECTOR_REGISTER_ACCESS
check. A single gate, WOLFSSL_FALCON_SAVE_VREGS (set for FPR_DOUBLE, FPR_ASM,
FFT_AVX2 or FFT_NEON), drives the wrapping so the emulated integer build stays
untouched.

The saves sit at the two int-returning orchestrators (falcon_native_make_key
around falcon_keygen; falcon_native_sign_msg around the tree build + ffSampling
loop) and read like every other fallible step: "ret = SAVE_VECTOR_REGISTERS2();
if (ret != 0) goto out;", propagating a save failure through normal cleanup.
Nested saves inside SHAKE256 are reference-counted. Verified with
DEBUG_VECTOR_REGISTER_ACCESS (+ABORT_ON_FAIL and fuzzing) on the double, asm and
avx2 backends: balanced counts, no unsaved FP/SIMD, graceful failure path.
This commit is contained in:
Daniele Lacamera
2026-07-20 19:48:49 +02:00
parent f652db1739
commit c03d48ea93
+45
View File
@@ -267,6 +267,19 @@ WOLFSSL_LOCAL void falcon_poly_merge_fft_avx2(fpr* f, const fpr* f0,
#include <wolfcrypt/src/misc.c> #include <wolfcrypt/src/misc.c>
#endif #endif
/* Every fpr backend except the default integer-emulated one runs on the
* FP/vector register file: the scalar-double backend and the generated x86-64
* fpr asm use x87/SSE (xmm), and the AVX2/NEON FFT uses ymm / Q vector
* registers. Such a build must save and restore those registers around signing
* and keygen -- for kernel FPU state under WOLFSSL_LINUXKM and for the
* DEBUG_VECTOR_REGISTER_ACCESS check. The emulated backend is integer-only and
* needs none of this. (AVX2/NEON imply FPR_DOUBLE, but list them for builds
* that set the FFT backend directly via user_settings.h.) */
#if defined(WOLFSSL_FALCON_FPR_DOUBLE) || defined(WOLFSSL_FALCON_FPR_ASM) || \
defined(WOLFSSL_FALCON_FFT_AVX2) || defined(WOLFSSL_FALCON_FFT_NEON)
#define WOLFSSL_FALCON_SAVE_VREGS
#endif
/* ==== Native Falcon core (merged from the former wc_falcon_*.c). The AVX2 and /* ==== Native Falcon core (merged from the former wc_falcon_*.c). The AVX2 and
NEON FFT backends are folded in at the end of this file (gated by NEON FFT backends are folded in at the end of this file (gated by
WOLFSSL_FALCON_FFT_AVX2 / _NEON); only the generated fpr x86-64 asm WOLFSSL_FALCON_FFT_AVX2 / _NEON); only the generated fpr x86-64 asm
@@ -8170,7 +8183,19 @@ int falcon_native_make_key(falcon_key* key, WC_RNG* rng)
goto out; goto out;
} }
/* falcon_keygen runs the vectorized (AVX2/NEON) FFT for the Gram/norm
* checks; hold the vector registers just for that call (kernel FPU state
* and the vector-register access check). The encoding below is integer. */
#ifdef WOLFSSL_FALCON_SAVE_VREGS
ret = SAVE_VECTOR_REGISTERS2();
if (ret != 0) {
goto out;
}
#endif
ret = falcon_keygen(rng, f, g, F, G, h, logn); ret = falcon_keygen(rng, f, g, F, G, h, logn);
#ifdef WOLFSSL_FALCON_SAVE_VREGS
RESTORE_VECTOR_REGISTERS();
#endif
if (ret != 0) { if (ret != 0) {
goto out; goto out;
} }
@@ -8222,6 +8247,9 @@ int falcon_native_sign_msg(const byte* in, word32 inLen, byte* out, word32* outL
void* heap; void* heap;
int attempt, haveSpc = 0; int attempt, haveSpc = 0;
size_t compLen = 0; size_t compLen = 0;
#ifdef WOLFSSL_FALCON_SAVE_VREGS
int svr = 0; /* vector registers held for the FFT path */
#endif
if ((in == NULL && inLen != 0) || out == NULL || outLen == NULL || if ((in == NULL && inLen != 0) || out == NULL || outLen == NULL ||
key == NULL || rng == NULL) { key == NULL || rng == NULL) {
@@ -8291,6 +8319,18 @@ int falcon_native_sign_msg(const byte* in, word32 inLen, byte* out, word32* outL
if (ret != 0) { if (ret != 0) {
goto out; goto out;
} }
#ifdef WOLFSSL_FALCON_SAVE_VREGS
/* The key completion, ffLDL tree build and ffSampling below run the
* vectorized (AVX2/NEON) FFT and pointwise kernels. Hold the vector
* registers for the whole region: required for kernel FPU state and for the
* vector-register access check. Nested saves inside SHAKE256 are
* reference-counted, so this outer save is the only real one. */
ret = SAVE_VECTOR_REGISTERS2();
if (ret != 0) {
goto out;
}
svr = 1;
#endif
ret = falcon_complete_private(G, f, g, F, logn, heap); ret = falcon_complete_private(G, f, g, F, logn, heap);
if (ret != 0) { if (ret != 0) {
goto out; goto out;
@@ -8350,6 +8390,11 @@ int falcon_native_sign_msg(const byte* in, word32 inLen, byte* out, word32* outL
*outLen = (word32)(1 + FALCON_NONCE_SIZE + compLen); *outLen = (word32)(1 + FALCON_NONCE_SIZE + compLen);
out: out:
#ifdef WOLFSSL_FALCON_SAVE_VREGS
if (svr) {
RESTORE_VECTOR_REGISTERS();
}
#endif
/* Free the sampler's SHAKE256 context before zeroizing. wc_Shake256_Free /* Free the sampler's SHAKE256 context before zeroizing. wc_Shake256_Free
* releases the async device context allocated by wc_InitShake256 in * releases the async device context allocated by wc_InitShake256 in
* WOLFSSL_ASYNC_CRYPT builds; without it that context leaks on every sign. * WOLFSSL_ASYNC_CRYPT builds; without it that context leaks on every sign.