diff --git a/ChangeLog.md b/ChangeLog.md index f3107ffb0bf..b18278cb0c8 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,11 @@ # wolfSSL Release (unreleased) ## Behavioral Changes +* **Behavioral change (`ForceZero()` issues no CPU fences)**: the wipe is + kept alive by a compiler barrier that takes the buffer address, which also + keeps it from being optimized away for buffers that never leave the inlined + code. A caller that needs the zeroed memory to be visible to another core + must order it itself with a lock or an atomic release. * **Behavioral change (`wc_PufReadSram` health tests the raw SRAM readout)**: the raw readout is now health tested before the context accepts it, and a diff --git a/wolfcrypt/src/misc.c b/wolfcrypt/src/misc.c index 066967d10eb..f4b81fb8373 100644 --- a/wolfcrypt/src/misc.c +++ b/wolfcrypt/src/misc.c @@ -791,11 +791,14 @@ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) byte *zb = (byte *)mem; unsigned long *zl; - XFENCE(); - - while ((wc_ptr_t)zb & (wc_ptr_t)(sizeof(unsigned long) - 1U)) { - if (len == 0) - return; + /* Make the compiler put the buffer's current contents at mem, so the + * wipe below hits the memory that holds them and not a copy. */ + WC_BARRIER_DATA(mem); + + /* No early return here: a short unaligned buffer must still reach the + * trailing barrier, or its wipe can be dropped as a dead store. */ + while ((len != 0) && + ((wc_ptr_t)zb & (wc_ptr_t)(sizeof(unsigned long) - 1U))) { *zb++ = 0; --len; } @@ -814,7 +817,10 @@ WC_MISC_STATIC WC_INLINE void ForceZero(void* mem, size_t len) --len; } - XFENCE(); + /* The caller is done with the buffer, so the compiler may drop the + * stores above as dead. The barrier makes the buffer look read by + * opaque code. No CPU fence is needed for that. */ + WC_BARRIER_DATA(mem); } #endif diff --git a/wolfssl/wolfcrypt/wc_port.h b/wolfssl/wolfcrypt/wc_port.h index bdaa0f2117c..f0a99c951a2 100644 --- a/wolfssl/wolfcrypt/wc_port.h +++ b/wolfssl/wolfcrypt/wc_port.h @@ -2017,6 +2017,19 @@ WOLFSSL_ABI WOLFSSL_API int wolfCrypt_Cleanup(void); } while(0) #endif +/* Compiler barrier that also treats the memory at ptr as read, so a wipe of + * that memory cannot be dropped as a dead store. The GNU form emits no CPU + * fence; cross-thread ordering is the caller's job. Without GNU asm (other + * compilers, or WOLFSSL_NO_ASM) it falls back to WC_BARRIER(). */ +#ifdef WC_BARRIER_DATA + /* use user-supplied WC_BARRIER_DATA() definition. */ +#elif defined(__GNUC__) && !defined(WOLFSSL_NO_ASM) + #define WC_BARRIER_DATA(ptr) \ + __asm__ __volatile__("" : : "r"(ptr) : "memory") +#else + #define WC_BARRIER_DATA(ptr) do { (void)(ptr); WC_BARRIER(); } while (0) +#endif + /* AFTER user_settings.h is loaded, ** determine if POSIX multi-threaded: HAVE_PTHREAD */