From 142f15651a9c87b8c545f979314ee028aefc106c Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Wed, 4 Nov 2026 11:01:23 -0700 Subject: [PATCH 1/5] F-9964 use global buffer for error info rather than internal node buffer --- tests/api.c | 26 ++++++++++++++++++---- wolfcrypt/src/logging.c | 48 +++++++++++++++++++++++++++-------------- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/tests/api.c b/tests/api.c index d03a32080f4..f4993bf6a6b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22230,13 +22230,31 @@ static int test_WOLFSSL_ERROR_MSG(void) */ static int test_wc_ERR_remove_state(void) { - int res = TEST_SKIPPED; + EXPECT_DECLS; #if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE) - wc_ERR_remove_state(); + ExpectIntEQ(wc_ERR_remove_state(), 0); - res = TEST_SUCCESS; +#if !defined(NO_ERROR_QUEUE) && defined(OPENSSL_EXTRA) && \ + defined(DEBUG_WOLFSSL) && !defined(ERROR_QUEUE_PER_THREAD) + { + /* with the global queue the returned file string must not point into + * the node, which is freed when the error is removed */ + const char* file = NULL; + int line = 0; + + ERR_clear_error(); + ERR_put_error(0, SYS_F_BIND, -275, "asn.c", 100); + ExpectIntNE(ERR_peek_last_error_line(&file, &line), 0); + ExpectIntNE(ERR_get_error(), 0); + ExpectNotNull(file); + if (file != NULL) { + ExpectIntEQ(XSTRCMP(file, "asn.c"), 0); + } + ExpectIntEQ(line, 100); + } #endif - return res; +#endif + return EXPECT_RESULT(); } /* End test_wc_ERR_remove_state */ /* * Testing wc_ERR_print_errors_fp diff --git a/wolfcrypt/src/logging.c b/wolfcrypt/src/logging.c index 77f439727b6..77d96d08dc6 100644 --- a/wolfcrypt/src/logging.c +++ b/wolfcrypt/src/logging.c @@ -1070,6 +1070,15 @@ static struct wc_error_queue* wc_current_node; /* heap info currently used for allocation of entries */ static void* wc_error_heap; +/* Copies of the file and reason strings of the last node peeked or pulled. + * Pointers returned to the caller point here rather than into a node, so a + * remove or clear from another thread after the mutex is released can not + * leave the caller with a pointer to freed memory. The contents are only valid + * until the next peek or pull. Only written with the mutex held and the last + * byte is never written, so the strings are always terminated. */ +static char wc_errq_file[WOLFSSL_MAX_ERROR_SZ]; +static char wc_errq_reason[WOLFSSL_MAX_ERROR_SZ]; + /* mutex for list operation protection */ static wolfSSL_Mutex wc_error_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(wc_error_mutex); #define ERRQ_MUTEX_INIT() wc_InitMutex(&wc_error_mutex) @@ -1109,6 +1118,21 @@ int wc_LoggingCleanup(void) return 0; } +/* Copy the node strings asked for to the static buffers and return those. + * Must be called with the mutex held. */ +static void exportErrorNodeStrings(const struct wc_error_queue* err, + const char **file, const char **reason) +{ + if (file != NULL) { + XMEMCPY(wc_errq_file, err->file, WOLFSSL_MAX_ERROR_SZ - 1); + *file = wc_errq_file; + } + if (reason != NULL) { + XMEMCPY(wc_errq_reason, err->error, WOLFSSL_MAX_ERROR_SZ - 1); + *reason = wc_errq_reason; + } +} + static int peekErrorNode(int idx, const char **file, const char **reason, int *line) { @@ -1135,12 +1159,7 @@ static int peekErrorNode(int idx, const char **file, const char **reason, return BAD_STATE_E; } - if (file != NULL) { - *file = err->file; - } - if (reason != NULL) { - *reason = err->error; - } + exportErrorNodeStrings(err, file, reason); if (line != NULL) { *line = err->line; } @@ -1153,8 +1172,10 @@ static int peekErrorNode(int idx, const char **file, const char **reason, * idx : if -1 then the most recent node is looked at, * otherwise search through queue for node at the given index starting * from the absolute head wc_errors - * file : pointer to internal file string - * reason : pointer to internal error reason + * file : pointer to a copy of the file string, valid until the next peek or + * pull + * reason : pointer to a copy of the error reason, valid until the next peek + * or pull * line : line number that error happened at * * Returns a negative value in error case, on success returns the nodes error @@ -1185,12 +1206,7 @@ static int pullErrorNode(const char **file, const char **reason, int *line) return BAD_STATE_E; } - if (file != NULL) { - *file = err->file; - } - if (reason != NULL) { - *reason = err->error; - } + exportErrorNodeStrings(err, file, reason); if (line != NULL) { *line = err->line; } @@ -1201,8 +1217,8 @@ static int pullErrorNode(const char **file, const char **reason, int *line) } /* Pulls the current node from error queue and increments current state. - * Note: this does not delete nodes because input arguments are pointing to - * node buffers. + * Note: this does not delete nodes. The file and reason pointers returned are + * to copies that are valid until the next peek or pull. * * file pointer to file that error was in. Can be NULL to return no file. * reason error string giving reason for error. Can be NULL to return no reason. From a3b9bd53dc8fced129fd84ccbe7a520e0abe182d Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Wed, 4 Nov 2026 11:13:58 -0700 Subject: [PATCH 2/5] F-15096 reset error node pointer on free --- tests/api.c | 14 ++++++++++++++ wolfcrypt/src/logging.c | 18 ++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/api.c b/tests/api.c index f4993bf6a6b..603dbcf1499 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22203,6 +22203,9 @@ static int test_wolfSSL_ERR_print_errors_cb(void) "wolfSSL error occurred, error = 275 line:100 file:asn.c", buf + 53, 55), 0); ExpectIntEQ(BIO_gets(bio, buf, sizeof(buf)), 0); + /* queue and its read cursor were reset, nothing left to pull */ + ExpectIntEQ(ERR_get_error_line(NULL, NULL), 0); + ExpectIntEQ(ERR_get_error(), 0); BIO_free(bio); #endif @@ -22234,6 +22237,17 @@ static int test_wc_ERR_remove_state(void) #if defined(OPENSSL_EXTRA) || defined(DEBUG_WOLFSSL_VERBOSE) ExpectIntEQ(wc_ERR_remove_state(), 0); +#if !defined(NO_ERROR_QUEUE) && defined(OPENSSL_EXTRA) && \ + defined(DEBUG_WOLFSSL) + /* the read cursor must be reset with the nodes it points into */ + ERR_clear_error(); + ERR_put_error(0, SYS_F_ACCEPT, -173, "ssl.c", 0); + ERR_put_error(0, SYS_F_BIND, -275, "asn.c", 100); + ExpectIntEQ(wc_ERR_remove_state(), 0); + ExpectIntEQ(ERR_get_error_line(NULL, NULL), 0); + ExpectIntEQ(ERR_get_error(), 0); +#endif + #if !defined(NO_ERROR_QUEUE) && defined(OPENSSL_EXTRA) && \ defined(DEBUG_WOLFSSL) && !defined(ERROR_QUEUE_PER_THREAD) { diff --git a/wolfcrypt/src/logging.c b/wolfcrypt/src/logging.c index 77d96d08dc6..73bf8956d2c 100644 --- a/wolfcrypt/src/logging.c +++ b/wolfcrypt/src/logging.c @@ -1447,25 +1447,14 @@ int wc_SetLoggingHeap(void* h) */ int wc_ERR_remove_state(void) { - struct wc_error_queue* current; - struct wc_error_queue* next; - if (ERRQ_LOCK() != 0) { WOLFSSL_MSG("Lock debug mutex failed"); return BAD_MUTEX_E; } - /* free all nodes from error queue */ - current = (struct wc_error_queue*)wc_errors; - while (current != NULL) { - next = current->next; - XFREE(current, current->heap, DYNAMIC_TYPE_LOG); - current = next; - } - - wc_errors_count = 0; - wc_errors = NULL; - wc_last_node = NULL; + /* free all nodes from error queue and reset all list pointers, + * including the wc_current_node cursor */ + clearErrorNodes(); ERRQ_UNLOCK(); @@ -1600,6 +1589,7 @@ void wc_ERR_print_errors_cb(int (*cb)(const char *str, size_t len, void *u), wc_errors_count = 0; wc_errors = NULL; wc_last_node = NULL; + wc_current_node = NULL; ERRQ_UNLOCK(); } From 7563fa352992628987a89681a9bbe9e4cef78f91 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Wed, 4 Nov 2026 13:37:56 -0700 Subject: [PATCH 3/5] F-11178 harden AES GMULT cases --- wolfcrypt/src/aes.c | 53 ++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/wolfcrypt/src/aes.c b/wolfcrypt/src/aes.c index 8659e65f12f..4ee0fe3e103 100644 --- a/wolfcrypt/src/aes.c +++ b/wolfcrypt/src/aes.c @@ -9650,12 +9650,16 @@ static void GMULT(byte* X, byte* Y, GcmScratch* WC_RESTRICT ws) byte y = Y[i]; for (j = 0; j < 8; j++) { - if (y & 0x80) { - xorbuf(Z, V, WC_AES_BLOCK_SIZE); + byte mask; + int k; + + mask = (byte)(0U - ((y >> 7) & 0x01U)); + for (k = 0; k < WC_AES_BLOCK_SIZE; k++) { + Z[k] ^= (byte)(V[k] & mask); } RIGHTSHIFTX(V); - y = y << 1; + y = (byte)(y << 1); } } XMEMCPY(X, Z, WC_AES_BLOCK_SIZE); @@ -11023,31 +11027,18 @@ static void GMULT(word32* X, word32* Y, GcmScratch* WC_RESTRICT ws) word32 y = Y[i]; for (j = 0; j < 32; j++) { - if (y & 0x80000000) { - Z[0] ^= V[0]; - Z[1] ^= V[1]; - Z[2] ^= V[2]; - Z[3] ^= V[3]; - } + word32 mask = (word32)0U - (y >> 31); + word32 carry = (word32)0U - (V[3] & 0x00000001); - if (V[3] & 0x00000001) { - V[3] >>= 1; - V[3] |= ((V[2] & 0x00000001) ? 0x80000000 : 0); - V[2] >>= 1; - V[2] |= ((V[1] & 0x00000001) ? 0x80000000 : 0); - V[1] >>= 1; - V[1] |= ((V[0] & 0x00000001) ? 0x80000000 : 0); - V[0] >>= 1; - V[0] ^= 0xE1000000; - } else { - V[3] >>= 1; - V[3] |= ((V[2] & 0x00000001) ? 0x80000000 : 0); - V[2] >>= 1; - V[2] |= ((V[1] & 0x00000001) ? 0x80000000 : 0); - V[1] >>= 1; - V[1] |= ((V[0] & 0x00000001) ? 0x80000000 : 0); - V[0] >>= 1; - } + Z[0] ^= V[0] & mask; + Z[1] ^= V[1] & mask; + Z[2] ^= V[2] & mask; + Z[3] ^= V[3] & mask; + + V[3] = (V[3] >> 1) | (V[2] << 31); + V[2] = (V[2] >> 1) | (V[1] << 31); + V[1] = (V[1] >> 1) | (V[0] << 31); + V[0] = (V[0] >> 1) ^ (0xE1000000 & carry); y <<= 1; } } @@ -21620,8 +21611,12 @@ static void AesGcmSivGMult(AesGcmSivPolyval* poly) for (i = 0; i < WC_AES_BLOCK_SIZE; i++) { byte y = poly->h[i]; for (j = 0; j < 8; j++) { - if (y & 0x80) { - xorbuf(Z, V, WC_AES_BLOCK_SIZE); + byte mask; + int k; + + mask = (byte)(0U - ((y >> 7) & 0x01U)); + for (k = 0; k < WC_AES_BLOCK_SIZE; k++) { + Z[k] ^= (byte)(V[k] & mask); } AesGcmSivMulX(V); y = (byte)(y << 1); From 2660014daa0c33b977b0895cd647c359d76e3d15 Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Wed, 4 Nov 2026 13:40:16 -0700 Subject: [PATCH 4/5] F-8328 force zero on buffer with ColdFire DES/3DES port --- wolfcrypt/src/des3.c | 1 + 1 file changed, 1 insertion(+) diff --git a/wolfcrypt/src/des3.c b/wolfcrypt/src/des3.c index 7775a5bdae6..1771d7e2aaf 100644 --- a/wolfcrypt/src/des3.c +++ b/wolfcrypt/src/des3.c @@ -571,6 +571,7 @@ static WC_INLINE void wc_Stm32_CrypDesBlock(const byte* in, byte* out) out += size; } + ForceZero(secKey, secDesc->length3); wc_UnLockMutex(&Mutex_DesSEC) ; } From b3ab09534e8f974569c36040d3c3900552054e6c Mon Sep 17 00:00:00 2001 From: JacobBarthelmeh Date: Fri, 6 Nov 2026 09:22:32 -0700 Subject: [PATCH 5/5] add thread local storage to error queue buffer and update macro guard on tests --- tests/api.c | 5 +++-- wolfcrypt/src/logging.c | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/api.c b/tests/api.c index 603dbcf1499..70f14bfeb2c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -22248,8 +22248,9 @@ static int test_wc_ERR_remove_state(void) ExpectIntEQ(ERR_get_error(), 0); #endif -#if !defined(NO_ERROR_QUEUE) && defined(OPENSSL_EXTRA) && \ - defined(DEBUG_WOLFSSL) && !defined(ERROR_QUEUE_PER_THREAD) +#if defined(WOLFSSL_HAVE_ERROR_QUEUE) && !defined(NO_ERROR_QUEUE) && \ + defined(OPENSSL_EXTRA) && defined(DEBUG_WOLFSSL) && \ + !defined(ERROR_QUEUE_PER_THREAD) { /* with the global queue the returned file string must not point into * the node, which is freed when the error is removed */ diff --git a/wolfcrypt/src/logging.c b/wolfcrypt/src/logging.c index 73bf8956d2c..8fae24ceea7 100644 --- a/wolfcrypt/src/logging.c +++ b/wolfcrypt/src/logging.c @@ -1074,10 +1074,11 @@ static void* wc_error_heap; * Pointers returned to the caller point here rather than into a node, so a * remove or clear from another thread after the mutex is released can not * leave the caller with a pointer to freed memory. The contents are only valid - * until the next peek or pull. Only written with the mutex held and the last - * byte is never written, so the strings are always terminated. */ -static char wc_errq_file[WOLFSSL_MAX_ERROR_SZ]; -static char wc_errq_reason[WOLFSSL_MAX_ERROR_SZ]; + * until the next peek or pull by the same thread. Only written with the mutex + * held and the last byte is never written, so the strings are always + * terminated. */ +static THREAD_LS_T char wc_errq_file[WOLFSSL_MAX_ERROR_SZ]; +static THREAD_LS_T char wc_errq_reason[WOLFSSL_MAX_ERROR_SZ]; /* mutex for list operation protection */ static wolfSSL_Mutex wc_error_mutex WOLFSSL_MUTEX_INITIALIZER_CLAUSE(wc_error_mutex);