From 9b617fa2511ddba30b70c9422f2420b539108de8 Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Mon, 26 Oct 2026 14:26:56 -0600 Subject: [PATCH 1/3] Reference count per-SSL static memory heap hints With WOLFSSL_STATIC_MEMORY, each SSL gets its own heap hint, and wolfSSL_free() releases it. Some objects allocated from that hint can live longer than the SSL: a session kept with wolfSSL_get1_session(), the server's peer cert stored in the session, or an X509 returned by wolfSSL_get_peer_certificate(). After wolfSSL_free(), these objects point at a hint that no longer exists. Update the static heap session, ticket and peer cert chain tests to check the hint's refCount as the SSL, sessions and X509s are freed. Add a case where an X509 is the last holder. Check that curAlloc returns to its baseline (except with WOLFSSL_STATIC_MEMORY_LEAN). Fix test_wolfSSL_CTX_add_session_ext without WOLFSSL_TICKET_HAVE_ID: a TLS 1.2 ticket session has no session ID, so it can't be added to the cache, and the remove callback isn't called on resumption. --- src/internal.c | 53 +++-- src/ssl_sess.c | 12 +- src/x509.c | 7 + tests/api.c | 388 +++++++++++++++++++++++++++++++++++++ tests/api/test_session.c | 10 +- wolfcrypt/src/memory.c | 87 +++++++++ wolfssl/wolfcrypt/memory.h | 4 + 7 files changed, 539 insertions(+), 22 deletions(-) diff --git a/src/internal.c b/src/internal.c index 0c54d776a0d..2129a5572f4 100644 --- a/src/internal.c +++ b/src/internal.c @@ -5927,6 +5927,13 @@ void InitX509(WOLFSSL_X509* x509, int dynamicFlag, void* heap) (void)ret; } #endif +#ifdef WOLFSSL_STATIC_MEMORY + /* A dynamic X509 can outlive the SSL whose heap hint it uses; dropped in + * ExternalFreeX509() */ + if (dynamicFlag) { + wc_HeapHint_Ref(heap); + } +#endif } @@ -8911,6 +8918,8 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ctx_hint = ((WOLFSSL_HEAP_HINT*)(ctx->heap)); ssl_hint->memory = ctx_hint->memory; + ssl_hint->refCount = 1; /* the SSL's own reference */ + ssl_hint->sslOwned = 1; #ifndef WOLFSSL_STATIC_MEMORY_LEAN #ifndef SINGLE_THREADED /* lock and check IO count / handshake count */ @@ -10287,18 +10296,23 @@ static void FreeSSL_StaticMemory(WOLFSSL* ssl) /* avoid dereferencing a test value */ if (ssl->heap != (void*)WOLFSSL_HEAP_TEST) { #endif - void* heap = ssl->ctx ? ssl->ctx->heap : ssl->heap; #ifndef WOLFSSL_STATIC_MEMORY_LEAN WOLFSSL_HEAP_HINT* ssl_hint = (WOLFSSL_HEAP_HINT*)ssl->heap; - WOLFSSL_HEAP* ctx_heap; - WOLFSSL_MEM_CONN_STATS* stats; + WOLFSSL_HEAP* ctx_heap = ssl_hint->memory; + void* heap = ssl->ctx ? ssl->ctx->heap : ssl->heap; + WOLFSSL_MEM_CONN_STATS* stats = NULL; - ctx_heap = ssl_hint->memory; #ifndef SINGLE_THREADED if (wc_LockMutex(&(ctx_heap->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); } #endif + /* detach stats under the lock: sessions and X509s holding a reference + * may still allocate through this hint from other threads */ + if (ctx_heap->flag & WOLFMEM_TRACK_STATS) { + stats = ssl_hint->stats; + ssl_hint->stats = NULL; + } ctx_heap->curIO--; if (FreeFixedIO(ctx_heap, &(ssl_hint->outBuf)) != 1) { WOLFSSL_MSG("Error freeing fixed output buffer"); @@ -10314,16 +10328,13 @@ static void FreeSSL_StaticMemory(WOLFSSL* ssl) #ifndef SINGLE_THREADED wc_UnLockMutex(&(ctx_heap->memory_mutex)); #endif - - /* check if tracking stats */ - if (ctx_heap->flag & WOLFMEM_TRACK_STATS) { - /* with ssl->ctx NULL, heap is ssl->heap and XFREE updates stats */ - stats = ssl_hint->stats; - ssl_hint->stats = NULL; - XFREE(stats, heap, DYNAMIC_TYPE_SSL); - } - #endif /* !WOLFSSL_STATIC_MEMORY_LEAN */ - XFREE(ssl->heap, heap, DYNAMIC_TYPE_SSL); + /* unlocked, XFREE takes memory_mutex; with ssl->ctx NULL, heap is + * ssl->heap, which the SSL's reference still keeps alive here */ + XFREE(stats, heap, DYNAMIC_TYPE_SSL); + #endif + /* drop the SSL's reference; other holders may keep the hint alive */ + wc_HeapHint_Unref(ssl->heap); + ssl->heap = NULL; #ifdef WOLFSSL_HEAP_TEST } #endif @@ -21185,7 +21196,7 @@ int DoFinished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size, !EchRejected(ssl)) { ForceZero(ssl->session->ticket, ssl->session->ticketLen); if (ssl->session->ticketLenAlloc > 0) { - XFREE(ssl->session->ticket, ssl->heap, + XFREE(ssl->session->ticket, ssl->session->heap, DYNAMIC_TYPE_SESSION_TICK); ssl->session->ticket = ssl->session->staticTicket; ssl->session->ticketLenAlloc = 0; @@ -40512,14 +40523,15 @@ int SetTicket(WOLFSSL* ssl, const byte* ticket, word32 length) /* Free old dynamic ticket if we already had one */ if (ssl->session->ticketLenAlloc > 0) { - XFREE(ssl->session->ticket, ssl->heap, DYNAMIC_TYPE_SESSION_TICK); + XFREE(ssl->session->ticket, ssl->session->heap, + DYNAMIC_TYPE_SESSION_TICK); ssl->session->ticket = ssl->session->staticTicket; ssl->session->ticketLenAlloc = 0; } if (length > sizeof(ssl->session->staticTicket)) { - byte* sessionTicket = - (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_SESSION_TICK); + byte* sessionTicket = (byte*)XMALLOC(length, ssl->session->heap, + DYNAMIC_TYPE_SESSION_TICK); if (sessionTicket == NULL) return MEMORY_E; ssl->session->ticket = sessionTicket; @@ -45056,7 +45068,8 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) if (ssl->session->ticket != ssl->session->staticTicket) { /* Always use the static ticket buffer */ - XFREE(ssl->session->ticket, NULL, DYNAMIC_TYPE_SESSION_TICK); + XFREE(ssl->session->ticket, ssl->session->heap, + DYNAMIC_TYPE_SESSION_TICK); ssl->session->ticket = ssl->session->staticTicket; ssl->session->ticketLenAlloc = 0; } @@ -45704,7 +45717,7 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl) (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3))) if (ssl->session->ticketNonce.data != ssl->session->ticketNonce.dataStatic) { - XFREE(ssl->session->ticketNonce.data, ssl->heap, + XFREE(ssl->session->ticketNonce.data, ssl->session->heap, DYNAMIC_TYPE_SESSION_TICK); ssl->session->ticketNonce.data = ssl->session->ticketNonce.dataStatic; diff --git a/src/ssl_sess.c b/src/ssl_sess.c index 3a932c37cf4..9cb22c4a9c1 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -4121,7 +4121,7 @@ WOLFSSL_SESSION* wolfSSL_NewSession(void* heap) #ifdef WOLFSSL_REFCNT_ERROR_RETURN if (err != 0) { WOLFSSL_MSG("Error setting up session reference mutex"); - XFREE(ret, ret->heap, DYNAMIC_TYPE_SESSION); + XFREE(ret, heap, DYNAMIC_TYPE_SESSION); return NULL; } #else @@ -4132,6 +4132,9 @@ WOLFSSL_SESSION* wolfSSL_NewSession(void* heap) #endif ret->type = WOLFSSL_SESSION_TYPE_HEAP; ret->heap = heap; +#ifdef WOLFSSL_STATIC_MEMORY + wc_HeapHint_Ref(heap); +#endif #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("SESSION master secret", ret->masterSecret, SECRET_LEN); wc_MemZero_Add("SESSION id", ret->sessionID, ID_LEN); @@ -4581,8 +4584,15 @@ void wolfSSL_FreeSession(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session) ForceZero(session->sessionID, ID_LEN); if (session->type == WOLFSSL_SESSION_TYPE_HEAP) { + #ifdef WOLFSSL_STATIC_MEMORY + void* heap = session->heap; + #endif /* // NOLINTNEXTLINE(clang-analyzer-unix.Malloc) */ XFREE(session, session->heap, DYNAMIC_TYPE_SESSION); + #ifdef WOLFSSL_STATIC_MEMORY + /* after the XFREE: it may free the hint the session came from */ + wc_HeapHint_Unref(heap); + #endif } } diff --git a/src/x509.c b/src/x509.c index d1b66a6bcb0..ca407a25ad9 100644 --- a/src/x509.c +++ b/src/x509.c @@ -4281,8 +4281,15 @@ static void ExternalFreeX509(WOLFSSL_X509* x509) if (doFree) #endif /* OPENSSL_EXTRA_X509_SMALL || OPENSSL_EXTRA */ { + #ifdef WOLFSSL_STATIC_MEMORY + void* heap = x509->heap; + #endif FreeX509(x509); XFREE(x509, x509->heap, DYNAMIC_TYPE_X509); + #ifdef WOLFSSL_STATIC_MEMORY + /* after the XFREE: it may free the hint x509 came from */ + wc_HeapHint_Unref(heap); + #endif } } else { diff --git a/tests/api.c b/tests/api.c index d03a32080f4..04a06651ed9 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39742,6 +39742,391 @@ static int test_wolfSSL_set_SSL_CTX_static_heap(void) return EXPECT_RESULT(); } +/* A session retained past wolfSSL_free() keeps using the per-connection + * static-memory heap hint, which stays alive until its last holder is freed. */ +static int test_wolfSSL_get1_session_static_heap(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFCRYPT_ONLY) && \ + !defined(NO_TLS) && !defined(NO_SESSION_CACHE) && \ + !defined(NO_WOLFSSL_CLIENT) + byte mem[TEST_TLS_STATIC_MEMSZ]; + WOLFSSL_HEAP_HINT* heap = NULL; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_SESSION* sess = NULL; + WOLFSSL_SESSION* dup = NULL; + WOLFSSL_HEAP_HINT* hint = NULL; + WOLFSSL_MEM_STATS before; + WOLFSSL_MEM_STATS after; + + XMEMSET(&before, 0, sizeof(before)); + XMEMSET(&after, 0, sizeof(after)); + ExpectIntEQ(wc_LoadStaticMemory(&heap, mem, sizeof(mem), + WOLFMEM_GENERAL | WOLFMEM_TRACK_STATS, 1), 0); + ExpectNotNull(ctx = wolfSSL_CTX_new_ex(wolfSSLv23_client_method_ex(heap), + heap)); + /* The first SSL makes a one-time allocation for the CTX; take the + * baseline after it. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + wolfSSL_free(ssl); + ssl = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx, &before), 1); + + ExpectNotNull(ssl = wolfSSL_new(ctx)); + /* The per-SSL hint is held by the SSL and by ssl->session. */ + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl->heap); + ExpectIntEQ(hint->sslOwned, 1); + ExpectIntEQ(hint->refCount, 2); + ExpectNotNull(sess = wolfSSL_get1_session(ssl)); + ExpectPtrEq(sess->heap, hint); + /* A session reference is not a heap hint reference. */ + ExpectIntEQ(hint->refCount, 2); + wolfSSL_SESSION_free(sess); + sess = NULL; + ExpectIntEQ(hint->refCount, 2); + /* No handshake done, so clear replaces the session. */ + ExpectIntEQ(wolfSSL_clear(ssl), WOLFSSL_SUCCESS); + ExpectIntEQ(hint->refCount, 2); + ExpectNotNull(sess = wolfSSL_get1_session(ssl)); + wolfSSL_free(ssl); + ssl = NULL; + /* Only the retained session keeps the hint alive now. */ + ExpectPtrEq(sess->heap, hint); + ExpectIntEQ(hint->refCount, 1); + + /* Both operations go through sess->heap after the SSL is gone. */ + ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); + ExpectPtrEq(dup->heap, hint); + ExpectIntEQ(hint->refCount, 2); + wolfSSL_SESSION_free(dup); + ExpectIntEQ(hint->refCount, 1); + /* The last reference: the hint goes back to the pool (checked below). */ + wolfSSL_SESSION_free(sess); + + /* Everything allocated for the SSL went back to the pool. Stats are not + * filled in with WOLFSSL_STATIC_MEMORY_LEAN. */ + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx, &after), 1); +#ifndef WOLFSSL_STATIC_MEMORY_LEAN + ExpectIntEQ(after.curAlloc, before.curAlloc); +#endif + + wolfSSL_CTX_free(ctx); + wc_UnloadStaticMemory(heap); +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) +static int test_session_static_heap_new_pair(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s, + WOLFSSL_SESSION* sess) +{ + EXPECT_DECLS; + char buf[1]; + + test_memio_clear_buffer(test_ctx, 1); + test_memio_clear_buffer(test_ctx, 0); + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + NULL, NULL), 0); + if (sess != NULL) { + ExpectIntEQ(wolfSSL_set_session(*ssl_c, sess), WOLFSSL_SUCCESS); + } + ExpectIntEQ(test_memio_do_handshake(*ssl_c, *ssl_s, 10, NULL), 0); + /* Process the NewSessionTicket sent after the handshake. */ + ExpectIntEQ(wolfSSL_read(*ssl_c, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(*ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + + return EXPECT_RESULT(); +} +#endif + +/* Session tickets held by a session retained past wolfSSL_free() must come + * from, and go back to, the session's heap. */ +static int test_wolfSSL_session_ticket_static_heap(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(WOLFSSL_TLS13) && defined(HAVE_SESSION_TICKET) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_RSA) && \ + !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) + byte cliMem[TEST_TLS_STATIC_MEMSZ]; + byte svrMem[TEST_TLS_STATIC_MEMSZ]; + byte bigTicket[SESSION_TICKET_LEN + 1]; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + WOLFSSL_SESSION* dup = NULL; + WOLFSSL_HEAP_HINT* hint = NULL; + WOLFSSL_MEM_STATS before; + WOLFSSL_MEM_STATS after; + char buf[1]; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(&before, 0, sizeof(before)); + XMEMSET(&after, 0, sizeof(after)); + XMEMSET(bigTicket, 0xA5, sizeof(bigTicket)); + + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_c, + wolfTLSv1_3_client_method_ex, cliMem, sizeof(cliMem), + WOLFMEM_GENERAL | WOLFMEM_TRACK_STATS, 2), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_s, + wolfTLSv1_3_server_method_ex, svrMem, sizeof(svrMem), + WOLFMEM_GENERAL, 2), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_SetIORecv(ctx_c, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_c, test_memio_write_cb); + wolfSSL_SetIORecv(ctx_s, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_s, test_memio_write_cb); + } + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + + /* A first connection makes the one-time allocations for the CTXs; take + * the baseline after it. */ + ExpectIntEQ(test_session_static_heap_new_pair(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, NULL), TEST_SUCCESS); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &before), 1); + + /* Full handshake: keep the session with its ticket after the SSL. */ + ExpectIntEQ(test_session_static_heap_new_pair(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, NULL), TEST_SUCCESS); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectIntGT(sess->ticketLen, 0); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); + ExpectPtrEq(sess->heap, hint); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + /* The retained session keeps the client's per-SSL hint alive. */ + ExpectIntEQ(hint->refCount, 1); + + /* Resume with the session on a new SSL; a new ticket replaces the old. */ + ExpectIntEQ(test_session_static_heap_new_pair(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s, sess), TEST_SUCCESS); + ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1); + /* The new SSL took sess, then copied it (from sess->heap) before storing + * the new ticket, so the first SSL's hint is held twice. */ + ExpectPtrNE(ssl_c->heap, hint); + ExpectPtrNE(ssl_c->session, sess); + ExpectPtrEq(ssl_c->session->heap, hint); + ExpectIntEQ(hint->refCount, 2); + wolfSSL_SESSION_free(sess); + sess = NULL; + ExpectIntEQ(hint->refCount, 1); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + /* The hint has outlived both SSLs. */ + ExpectIntEQ(hint->refCount, 1); + wolfSSL_SESSION_free(sess); + sess = NULL; + + /* A ticket too big for the static buffer is allocated dynamically. */ + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectIntEQ(wolfSSL_set_SessionTicket(ssl_c, bigTicket, + (word32)sizeof(bigTicket)), WOLFSSL_SUCCESS); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectIntEQ(sess->ticketLenAlloc, sizeof(bigTicket)); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); + wolfSSL_free(ssl_c); + ssl_c = NULL; + ExpectIntEQ(hint->refCount, 1); + ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); + ExpectIntEQ(hint->refCount, 2); + wolfSSL_SESSION_free(dup); + dup = NULL; + ExpectIntEQ(hint->refCount, 1); + wolfSSL_SESSION_free(sess); + sess = NULL; + + /* A full handshake replaces the dynamic ticket with the new, smaller one, + * freeing the dynamic buffer through the session's heap. */ + test_memio_clear_buffer(&test_ctx, 1); + test_memio_clear_buffer(&test_ctx, 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + NULL, NULL), 0); + ExpectIntEQ(wolfSSL_set_SessionTicket(ssl_c, bigTicket, + (word32)sizeof(bigTicket)), WOLFSSL_SUCCESS); + ExpectIntEQ(ssl_c->session->ticketLenAlloc, sizeof(bigTicket)); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectIntEQ(wolfSSL_read(ssl_c, buf, sizeof(buf)), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + ExpectIntEQ(ssl_c->session->ticketLenAlloc, 0); + ExpectIntGT(ssl_c->session->ticketLen, 0); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + wolfSSL_SESSION_free(sess); + + /* Everything allocated for the connections went back to the pool. */ + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &after), 1); + ExpectIntEQ(after.curAlloc, before.curAlloc); + + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + +#if defined(WOLFSSL_STATIC_MEMORY) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_RSA) && \ + !defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH) +static int test_peer_cert_static_heap_connect(struct test_memio_ctx* test_ctx, + WOLFSSL_CTX** ctx_c, WOLFSSL_CTX** ctx_s, WOLFSSL** ssl_c, WOLFSSL** ssl_s) +{ + EXPECT_DECLS; + + test_memio_clear_buffer(test_ctx, 1); + test_memio_clear_buffer(test_ctx, 0); + ExpectIntEQ(test_memio_setup(test_ctx, ctx_c, ctx_s, ssl_c, ssl_s, + wolfSSLv23_client_method, NULL), 0); + ExpectIntEQ(wolfSSL_use_certificate_file(*ssl_c, cliCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_use_PrivateKey_file(*ssl_c, cliKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(test_memio_do_handshake(*ssl_c, *ssl_s, 10, NULL), 0); + + return EXPECT_RESULT(); +} +#endif + +/* On the server, wolfSSL_get_peer_cert_chain() moves the leaf certificate into + * the session as its peer. Like any dynamic X509 on the per-connection + * static-memory hint, it holds a reference that keeps the hint alive after + * wolfSSL_free() until the last holder, session or X509, is freed. */ +static int test_wolfSSL_peer_cert_chain_static_heap(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_STATIC_MEMORY) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_RSA) && \ + !defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH) + byte svrMem[TEST_TLS_STATIC_MEMSZ]; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + WOLFSSL_HEAP_HINT* hint = NULL; +#ifdef KEEP_PEER_CERT + WOLFSSL_X509* peer = NULL; +#endif + WOLFSSL_MEM_STATS before; + WOLFSSL_MEM_STATS after; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(&before, 0, sizeof(before)); + XMEMSET(&after, 0, sizeof(after)); + + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_s, + wolfSSLv23_server_method_ex, svrMem, sizeof(svrMem), + WOLFMEM_GENERAL | WOLFMEM_TRACK_STATS, 1), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + wolfSSL_SetIORecv(ctx_s, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_s, test_memio_write_cb); + wolfSSL_CTX_set_verify(ctx_s, WOLFSSL_VERIFY_PEER | + WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL); + } + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL), + WOLFSSL_SUCCESS); + + /* A first connection makes the one-time allocations for the server CTX; + * take the baseline after it. */ + ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s), TEST_SUCCESS); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_s, &before), 1); + + /* The session is the last holder. */ + ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s), TEST_SUCCESS); + /* The server's per-SSL hint is held by the SSL and by ssl->session. */ + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); + ExpectIntEQ(hint->refCount, 2); + + ExpectNotNull(wolfSSL_get_peer_cert_chain(ssl_s)); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_s)); + ExpectNotNull(sess->peer); + /* The leaf moved into the session is a dynamic X509 on the SSL's hint + * and holds its own reference. */ + ExpectPtrEq(sess->peer->heap, hint); + ExpectIntGE(hint->refCount, 3); + wolfSSL_free(ssl_s); + ssl_s = NULL; + /* Only the session and its peer keep the hint alive now. */ + ExpectIntEQ(hint->refCount, 2); + /* The peer is still usable once the SSL is gone. */ + ExpectNotNull(wolfSSL_SESSION_get0_peer(sess)); + wolfSSL_SESSION_free(sess); + sess = NULL; + wolfSSL_free(ssl_c); + ssl_c = NULL; + +#ifdef KEEP_PEER_CERT + /* An X509 is the last holder. */ + ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, + &ssl_c, &ssl_s), TEST_SUCCESS); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); + ExpectIntEQ(hint->refCount, 2); + /* A copy of ssl->peerCert, so on the SSL's hint. */ + ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s)); + ExpectPtrEq(peer->heap, hint); + ExpectIntEQ(hint->refCount, 3); + wolfSSL_free(ssl_s); + ssl_s = NULL; + /* The SSL and its session are gone; only the X509 holds the hint. */ + ExpectIntEQ(hint->refCount, 1); + ExpectNotNull(wolfSSL_X509_get_subject_name(peer)); + /* The last reference: the hint goes back to the pool (checked below). */ + wolfSSL_X509_free(peer); + peer = NULL; + wolfSSL_free(ssl_c); + ssl_c = NULL; +#endif + + /* Everything allocated for the connections went back to the pool. Stats + * are not filled in with WOLFSSL_STATIC_MEMORY_LEAN. */ + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_s, &after), 1); +#ifndef WOLFSSL_STATIC_MEMORY_LEAN + ExpectIntEQ(after.curAlloc, before.curAlloc); +#endif + + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); +#endif + return EXPECT_RESULT(); +} + static int test_openssl_FIPS_drbg(void) { EXPECT_DECLS; @@ -47301,6 +47686,9 @@ TEST_CASE testCases[] = { /* Can't memory test as client/server hangs. */ TEST_DECL(test_wolfSSL_CTX_StaticMemory), TEST_DECL(test_wolfSSL_set_SSL_CTX_static_heap), + TEST_DECL(test_wolfSSL_get1_session_static_heap), + TEST_DECL(test_wolfSSL_session_ticket_static_heap), + TEST_DECL(test_wolfSSL_peer_cert_chain_static_heap), #if !defined(NO_FILESYSTEM) && \ defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) diff --git a/tests/api/test_session.c b/tests/api/test_session.c index ee2c769424c..5bfb57644f2 100644 --- a/tests/api/test_session.c +++ b/tests/api/test_session.c @@ -513,11 +513,13 @@ static int twcase_server_sess_ctx_pre_shutdown(WOLFSSL* ssl) /* Now save the session in the internal store to make it available * for lookup. For TLS 1.3, we can't save the session without * WOLFSSL_TICKET_HAVE_ID because there is no way to retrieve the - * session from cache. */ + * session from cache. The same holds for a TLS 1.2 ticket session, + * which has no session ID without WOLFSSL_TICKET_HAVE_ID. */ if (wolfSSL_is_server(ssl) #ifndef WOLFSSL_TICKET_HAVE_ID && wolfSSL_version(ssl) != TLS1_3_VERSION && wolfSSL_version(ssl) != DTLS1_3_VERSION + && *sess != NULL && (*sess)->sessionIDSz > 0 #endif ) { ExpectIntEQ(wolfSSL_CTX_add_session(wolfSSL_get_SSL_CTX(ssl), @@ -870,7 +872,13 @@ static WC_MAYBE_UNUSED int test_wolfSSL_CTX_add_session_ext( else { /* The ticket is reused, no new session */ ExpectIntEQ(twcase_new_session_called, 0); + #ifdef WOLFSSL_TICKET_HAVE_ID ExpectIntEQ(twcase_remove_session_called, 1); + #else + /* No session ID, so the session was not cached in + * twcase_server_sess_ctx_pre_shutdown to be removed */ + ExpectIntEQ(twcase_remove_session_called, 0); + #endif } break; #ifdef WOLFSSL_EARLY_DATA diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index 5f4c2d9c6ce..98a6a6af8fa 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -846,6 +846,93 @@ void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap) #endif } + +/* Returns 1 if heap is a per-SSL hint created by InitSSL_StaticMemory. Only + * these are reference counted; CTX, user and global hints are left alone. */ +static int wc_HeapHint_IsRefCounted(const WOLFSSL_HEAP_HINT* hint) +{ + if (hint == NULL) { + return 0; + } +#ifdef WOLFSSL_HEAP_TEST + /* avoid dereferencing a test value */ + if (hint == (const WOLFSSL_HEAP_HINT*)WOLFSSL_HEAP_TEST) { + return 0; + } +#endif + return hint->sslOwned && hint->memory != NULL; +} + +/* Take a reference on a per-SSL heap hint so it stays valid after + * wolfSSL_free() for an object (e.g. a session) allocated from it. */ +void wc_HeapHint_Ref(void* heap) +{ + WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; + + int locked = 1; + + if (!wc_HeapHint_IsRefCounted(hint)) { + return; + } +#ifndef SINGLE_THREADED + if (wc_LockMutex(&hint->memory->memory_mutex) != 0) { + /* still take the reference: the caller's object is created either + * way and drops it when freed, so skipping it would free the hint + * early */ + WOLFSSL_MSG("Bad memory_mutex lock"); + locked = 0; + } +#endif + hint->refCount++; +#ifndef SINGLE_THREADED + if (locked) { + wc_UnLockMutex(&hint->memory->memory_mutex); + } +#endif + (void)locked; +} + +/* Drop a reference on a per-SSL heap hint, freeing it back to its pool when + * the last one is gone. The hint must not be used by the caller after this. */ +void wc_HeapHint_Unref(void* heap) +{ + WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; + WOLFSSL_HEAP_HINT tmp; + WOLFSSL_HEAP* mem; + word32 refCount; + + if (!wc_HeapHint_IsRefCounted(hint)) { + return; + } + mem = hint->memory; +#ifndef SINGLE_THREADED + if (wc_LockMutex(&mem->memory_mutex) != 0) { + WOLFSSL_MSG("Bad memory_mutex lock"); + return; + } +#endif + if (hint->refCount == 0) { + /* unbalanced unref; leave the hint alone rather than wrap */ + WOLFSSL_MSG("Heap hint reference count underflow"); + #ifndef SINGLE_THREADED + wc_UnLockMutex(&mem->memory_mutex); + #endif + return; + } + refCount = --hint->refCount; +#ifndef SINGLE_THREADED + /* unlock before XFREE, which takes memory_mutex */ + wc_UnLockMutex(&mem->memory_mutex); +#endif + if (refCount == 0) { + /* free through a temporary hint on the same pool so neither the CTX + * nor the hint being freed is needed */ + XMEMSET(&tmp, 0, sizeof(tmp)); + tmp.memory = mem; + XFREE(hint, &tmp, DYNAMIC_TYPE_SSL); + } +} + #ifndef WOLFSSL_STATIC_MEMORY_LEAN /* returns the size of management memory needed for each bucket. * This is memory that is used to keep track of and align memory buckets. */ diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 91200566acb..91a9686927e 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -277,6 +277,8 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf, wc_Memory* inBuf; byte haFlag; /* flag used for checking handshake count */ #endif + word32 refCount; /* SSL ref + other refs */ + byte sslOwned; /* created by InitSSL_StaticMemory */ } WOLFSSL_HEAP_HINT; WOLFSSL_API void* wolfSSL_SetGlobalHeapHint(void* heap); @@ -298,6 +300,8 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf, WOLFSSL_API int wc_LoadStaticMemory(WOLFSSL_HEAP_HINT** pHint, unsigned char* buf, unsigned int sz, int flag, int max); WOLFSSL_API void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap); + WOLFSSL_LOCAL void wc_HeapHint_Ref(void* heap); + WOLFSSL_LOCAL void wc_HeapHint_Unref(void* heap); WOLFSSL_API int wolfSSL_GetMemStats(WOLFSSL_HEAP* heap, WOLFSSL_MEM_STATS* stats); From 03b24361e56e3ecc64f2b8a28b8d0c1c0d7e2e1e Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Wed, 28 Oct 2026 10:33:40 -0600 Subject: [PATCH 2/3] HEAP_HINT has refcount in free and alloc Static blocks now keep a ref count and only free when all holders are done holding. --- src/internal.c | 30 ++-- src/ssl_sess.c | 10 -- src/x509.c | 7 - tests/api.c | 276 +++++++++++++++++++++++++++++++------ wolfcrypt/src/memory.c | 149 +++++++++++--------- wolfssl/wolfcrypt/memory.h | 5 +- 6 files changed, 343 insertions(+), 134 deletions(-) diff --git a/src/internal.c b/src/internal.c index 2129a5572f4..e83678759be 100644 --- a/src/internal.c +++ b/src/internal.c @@ -5927,13 +5927,6 @@ void InitX509(WOLFSSL_X509* x509, int dynamicFlag, void* heap) (void)ret; } #endif -#ifdef WOLFSSL_STATIC_MEMORY - /* A dynamic X509 can outlive the SSL whose heap hint it uses; dropped in - * ExternalFreeX509() */ - if (dynamicFlag) { - wc_HeapHint_Ref(heap); - } -#endif } @@ -8900,6 +8893,7 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ctx->heap != NULL) { WOLFSSL_HEAP_HINT* ssl_hint; WOLFSSL_HEAP_HINT* ctx_hint; + int ret = 0; /* avoid dereferencing a test value */ #ifdef WOLFSSL_HEAP_TEST @@ -8918,13 +8912,24 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ctx_hint = ((WOLFSSL_HEAP_HINT*)(ctx->heap)); ssl_hint->memory = ctx_hint->memory; - ssl_hint->refCount = 1; /* the SSL's own reference */ + /* the SSL's ref; each allocated block holds another */ + wolfSSL_RefInit(&ssl_hint->refCount, &ret); + #ifdef WOLFSSL_REFCNT_ERROR_RETURN + if (ret != 0) { + WOLFSSL_MSG("Error setting up heap hint reference mutex"); + XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); + ssl->heap = NULL; + return ret; + } + #endif + (void)ret; ssl_hint->sslOwned = 1; #ifndef WOLFSSL_STATIC_MEMORY_LEAN #ifndef SINGLE_THREADED /* lock and check IO count / handshake count */ if (wc_LockMutex(&(ctx_hint->memory->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); + wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E); @@ -8937,6 +8942,7 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #ifndef SINGLE_THREADED wc_UnLockMutex(&(ctx_hint->memory->memory_mutex)); #endif + wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ return MEMORY_E; @@ -8948,6 +8954,7 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #ifndef SINGLE_THREADED wc_UnLockMutex(&(ctx_hint->memory->memory_mutex)); #endif + wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ return MEMORY_E; @@ -10307,8 +10314,7 @@ static void FreeSSL_StaticMemory(WOLFSSL* ssl) WOLFSSL_MSG("Bad memory_mutex lock"); } #endif - /* detach stats under the lock: sessions and X509s holding a reference - * may still allocate through this hint from other threads */ + /* detach under the lock: live blocks may still use the hint */ if (ctx_heap->flag & WOLFMEM_TRACK_STATS) { stats = ssl_hint->stats; ssl_hint->stats = NULL; @@ -10332,8 +10338,8 @@ static void FreeSSL_StaticMemory(WOLFSSL* ssl) * ssl->heap, which the SSL's reference still keeps alive here */ XFREE(stats, heap, DYNAMIC_TYPE_SSL); #endif - /* drop the SSL's reference; other holders may keep the hint alive */ - wc_HeapHint_Unref(ssl->heap); + /* drop the SSL's ref; the last block's free may free the hint */ + wc_HeapHint_Release(ssl->heap); ssl->heap = NULL; #ifdef WOLFSSL_HEAP_TEST } diff --git a/src/ssl_sess.c b/src/ssl_sess.c index 9cb22c4a9c1..0acc8a16ad8 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -4132,9 +4132,6 @@ WOLFSSL_SESSION* wolfSSL_NewSession(void* heap) #endif ret->type = WOLFSSL_SESSION_TYPE_HEAP; ret->heap = heap; -#ifdef WOLFSSL_STATIC_MEMORY - wc_HeapHint_Ref(heap); -#endif #ifdef WOLFSSL_CHECK_MEM_ZERO wc_MemZero_Add("SESSION master secret", ret->masterSecret, SECRET_LEN); wc_MemZero_Add("SESSION id", ret->sessionID, ID_LEN); @@ -4584,15 +4581,8 @@ void wolfSSL_FreeSession(WOLFSSL_CTX* ctx, WOLFSSL_SESSION* session) ForceZero(session->sessionID, ID_LEN); if (session->type == WOLFSSL_SESSION_TYPE_HEAP) { - #ifdef WOLFSSL_STATIC_MEMORY - void* heap = session->heap; - #endif /* // NOLINTNEXTLINE(clang-analyzer-unix.Malloc) */ XFREE(session, session->heap, DYNAMIC_TYPE_SESSION); - #ifdef WOLFSSL_STATIC_MEMORY - /* after the XFREE: it may free the hint the session came from */ - wc_HeapHint_Unref(heap); - #endif } } diff --git a/src/x509.c b/src/x509.c index ca407a25ad9..d1b66a6bcb0 100644 --- a/src/x509.c +++ b/src/x509.c @@ -4281,15 +4281,8 @@ static void ExternalFreeX509(WOLFSSL_X509* x509) if (doFree) #endif /* OPENSSL_EXTRA_X509_SMALL || OPENSSL_EXTRA */ { - #ifdef WOLFSSL_STATIC_MEMORY - void* heap = x509->heap; - #endif FreeX509(x509); XFREE(x509, x509->heap, DYNAMIC_TYPE_X509); - #ifdef WOLFSSL_STATIC_MEMORY - /* after the XFREE: it may free the hint x509 came from */ - wc_HeapHint_Unref(heap); - #endif } } else { diff --git a/tests/api.c b/tests/api.c index 04a06651ed9..5b67868e95e 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39742,8 +39742,8 @@ static int test_wolfSSL_set_SSL_CTX_static_heap(void) return EXPECT_RESULT(); } -/* A session retained past wolfSSL_free() keeps using the per-connection - * static-memory heap hint, which stays alive until its last holder is freed. */ +/* A session retained past wolfSSL_free() keeps the per-SSL static heap hint + * alive. */ static int test_wolfSSL_get1_session_static_heap(void) { EXPECT_DECLS; @@ -39757,6 +39757,7 @@ static int test_wolfSSL_get1_session_static_heap(void) WOLFSSL_SESSION* sess = NULL; WOLFSSL_SESSION* dup = NULL; WOLFSSL_HEAP_HINT* hint = NULL; + int refs = 0; WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS after; @@ -39774,34 +39775,33 @@ static int test_wolfSSL_get1_session_static_heap(void) ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx, &before), 1); ExpectNotNull(ssl = wolfSSL_new(ctx)); - /* The per-SSL hint is held by the SSL and by ssl->session. */ + /* Held by the SSL and each allocated block. */ ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl->heap); ExpectIntEQ(hint->sslOwned, 1); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); ExpectNotNull(sess = wolfSSL_get1_session(ssl)); ExpectPtrEq(sess->heap, hint); - /* A session reference is not a heap hint reference. */ - ExpectIntEQ(hint->refCount, 2); + /* A session ref allocates nothing. */ + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_SESSION_free(sess); sess = NULL; - ExpectIntEQ(hint->refCount, 2); + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); /* No handshake done, so clear replaces the session. */ ExpectIntEQ(wolfSSL_clear(ssl), WOLFSSL_SUCCESS); - ExpectIntEQ(hint->refCount, 2); ExpectNotNull(sess = wolfSSL_get1_session(ssl)); + ExpectPtrEq(sess->heap, hint); wolfSSL_free(ssl); ssl = NULL; - /* Only the retained session keeps the hint alive now. */ - ExpectPtrEq(sess->heap, hint); - ExpectIntEQ(hint->refCount, 1); + /* Only the session holds the hint now. */ + ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); /* Both operations go through sess->heap after the SSL is gone. */ ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); ExpectPtrEq(dup->heap, hint); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_SESSION_free(dup); - ExpectIntEQ(hint->refCount, 1); - /* The last reference: the hint goes back to the pool (checked below). */ + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + /* Last holder: the hint goes back to the pool. */ wolfSSL_SESSION_free(sess); /* Everything allocated for the SSL went back to the pool. Stats are not @@ -39866,6 +39866,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) WOLFSSL_SESSION* sess = NULL; WOLFSSL_SESSION* dup = NULL; WOLFSSL_HEAP_HINT* hint = NULL; + int refs = 0; WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS after; char buf[1]; @@ -39914,29 +39915,27 @@ static int test_wolfSSL_session_ticket_static_heap(void) ssl_c = NULL; wolfSSL_free(ssl_s); ssl_s = NULL; - /* The retained session keeps the client's per-SSL hint alive. */ - ExpectIntEQ(hint->refCount, 1); + /* The session keeps the client's hint alive. */ + ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); /* Resume with the session on a new SSL; a new ticket replaces the old. */ ExpectIntEQ(test_session_static_heap_new_pair(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, sess), TEST_SUCCESS); ExpectIntEQ(wolfSSL_session_reused(ssl_c), 1); - /* The new SSL took sess, then copied it (from sess->heap) before storing - * the new ticket, so the first SSL's hint is held twice. */ + /* sess was copied from sess->heap, so both are on the first hint. */ ExpectPtrNE(ssl_c->heap, hint); ExpectPtrNE(ssl_c->session, sess); ExpectPtrEq(ssl_c->session->heap, hint); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_SESSION_free(sess); sess = NULL; - ExpectIntEQ(hint->refCount, 1); ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); wolfSSL_free(ssl_c); ssl_c = NULL; wolfSSL_free(ssl_s); ssl_s = NULL; /* The hint has outlived both SSLs. */ - ExpectIntEQ(hint->refCount, 1); + ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); wolfSSL_SESSION_free(sess); sess = NULL; @@ -39949,12 +39948,12 @@ static int test_wolfSSL_session_ticket_static_heap(void) ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); wolfSSL_free(ssl_c); ssl_c = NULL; - ExpectIntEQ(hint->refCount, 1); + ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_SESSION_free(dup); dup = NULL; - ExpectIntEQ(hint->refCount, 1); + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_SESSION_free(sess); sess = NULL; @@ -40013,9 +40012,8 @@ static int test_peer_cert_static_heap_connect(struct test_memio_ctx* test_ctx, #endif /* On the server, wolfSSL_get_peer_cert_chain() moves the leaf certificate into - * the session as its peer. Like any dynamic X509 on the per-connection - * static-memory hint, it holds a reference that keeps the hint alive after - * wolfSSL_free() until the last holder, session or X509, is freed. */ + * the session as its peer. Session and X509 keep the per-SSL hint alive past + * wolfSSL_free(). */ static int test_wolfSSL_peer_cert_chain_static_heap(void) { EXPECT_DECLS; @@ -40031,6 +40029,7 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) WOLFSSL* ssl_s = NULL; WOLFSSL_SESSION* sess = NULL; WOLFSSL_HEAP_HINT* hint = NULL; + int refs = 0; #ifdef KEEP_PEER_CERT WOLFSSL_X509* peer = NULL; #endif @@ -40070,21 +40069,20 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) /* The session is the last holder. */ ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s), TEST_SUCCESS); - /* The server's per-SSL hint is held by the SSL and by ssl->session. */ + /* Held by the SSL and each allocated block. */ ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); ExpectNotNull(wolfSSL_get_peer_cert_chain(ssl_s)); ExpectNotNull(sess = wolfSSL_get1_session(ssl_s)); ExpectNotNull(sess->peer); - /* The leaf moved into the session is a dynamic X509 on the SSL's hint - * and holds its own reference. */ + /* The peer is on the SSL's hint too. */ ExpectPtrEq(sess->peer->heap, hint); - ExpectIntGE(hint->refCount, 3); + ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_free(ssl_s); ssl_s = NULL; - /* Only the session and its peer keep the hint alive now. */ - ExpectIntEQ(hint->refCount, 2); + /* Only the session and peer hold the hint now. */ + ExpectIntGE(wolfSSL_RefCur(hint->refCount), 2); /* The peer is still usable once the SSL is gone. */ ExpectNotNull(wolfSSL_SESSION_get0_peer(sess)); wolfSSL_SESSION_free(sess); @@ -40097,17 +40095,17 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s), TEST_SUCCESS); ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); - ExpectIntEQ(hint->refCount, 2); + ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); /* A copy of ssl->peerCert, so on the SSL's hint. */ ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s)); ExpectPtrEq(peer->heap, hint); - ExpectIntEQ(hint->refCount, 3); + ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); wolfSSL_free(ssl_s); ssl_s = NULL; - /* The SSL and its session are gone; only the X509 holds the hint. */ - ExpectIntEQ(hint->refCount, 1); + /* Only the X509 holds the hint now. */ + ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); ExpectNotNull(wolfSSL_X509_get_subject_name(peer)); - /* The last reference: the hint goes back to the pool (checked below). */ + /* Last holder: the hint goes back to the pool. */ wolfSSL_X509_free(peer); peer = NULL; wolfSSL_free(ssl_c); @@ -40127,6 +40125,204 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) return EXPECT_RESULT(); } +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_RSA) +/* ioFlag is WOLFMEM_IO_POOL or WOLFMEM_IO_POOL_FIXED. */ +static int test_static_heap_io_refs(int ioFlag) +{ + EXPECT_DECLS; + byte cliMem[TEST_TLS_STATIC_MEMSZ]; + byte cliIoMem[4 * (WOLFMEM_IO_SZ + WOLFSSL_STATIC_ALIGN + 64)]; + byte svrMem[TEST_TLS_STATIC_MEMSZ]; + struct test_memio_ctx test_ctx; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + WOLFSSL_SESSION* sess = NULL; + WOLFSSL_HEAP_HINT* hint = NULL; + void* io = NULL; + void* tmp = NULL; + int refs = 0; + int fixed = (ioFlag == WOLFMEM_IO_POOL_FIXED); + WOLFSSL_MEM_STATS before; + WOLFSSL_MEM_STATS stats; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + XMEMSET(&before, 0, sizeof(before)); + XMEMSET(&stats, 0, sizeof(stats)); + + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_c, + wolfSSLv23_client_method_ex, cliMem, sizeof(cliMem), + WOLFMEM_GENERAL | WOLFMEM_TRACK_STATS, 1), WOLFSSL_SUCCESS); + /* IO buffers go into the same CTX hint. */ + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_c, NULL, cliIoMem, + sizeof(cliIoMem), ioFlag, 1), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_load_static_memory(&ctx_s, + wolfSSLv23_server_method_ex, svrMem, sizeof(svrMem), + WOLFMEM_GENERAL, 1), WOLFSSL_SUCCESS); + if (EXPECT_SUCCESS()) { + wolfSSL_CTX_set_verify(ctx_c, WOLFSSL_VERIFY_NONE, NULL); + wolfSSL_SetIORecv(ctx_c, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_c, test_memio_write_cb); + wolfSSL_SetIORecv(ctx_s, test_memio_read_cb); + wolfSSL_SetIOSend(ctx_s, test_memio_write_cb); + } + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx_s, svrCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx_s, svrKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + + /* A first connection makes the one-time allocations for the CTXs; take + * the baseline after it. */ + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + NULL, NULL), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &before), 1); + ExpectIntGT(before.avaIO, 0); + + /* The session outlives the SSL whose IO buffers came from the pool. */ + test_memio_clear_buffer(&test_ctx, 1); + test_memio_clear_buffer(&test_ctx, 0); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s, + NULL, NULL), 0); + ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); + ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); + ExpectPtrEq(sess->heap, hint); + wolfSSL_free(ssl_c); + ssl_c = NULL; + wolfSSL_free(ssl_s); + ssl_s = NULL; + ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); + /* All IO buffers are back in the pool while the hint lives on. */ + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &stats), 1); + ExpectIntEQ(stats.avaIO, before.avaIO); + /* Last holder: the hint goes back to the pool. */ + wolfSSL_SESSION_free(sess); + sess = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &stats), 1); + ExpectIntEQ(stats.curAlloc, before.curAlloc); + ExpectIntEQ(stats.avaIO, before.avaIO); + + /* IO buffers taken directly from the per-SSL hint. Fixed ones belong to + * the SSL and hold no ref; pool ones hold one, and realloc keeps it. */ + ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); + if (EXPECT_SUCCESS()) + refs = wolfSSL_RefCur(hint->refCount); + ExpectNotNull(io = XMALLOC(64, hint, DYNAMIC_TYPE_IN_BUFFER)); + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), fixed ? refs : refs + 1); +#ifndef WOLFSSL_NO_REALLOC + ExpectNotNull(tmp = XREALLOC(io, 128, hint, DYNAMIC_TYPE_IN_BUFFER)); + /* IO buffers are fixed size, so resized in place. */ + ExpectPtrEq(tmp, io); + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), fixed ? refs : refs + 1); +#else + (void)tmp; +#endif + XFREE(io, hint, DYNAMIC_TYPE_IN_BUFFER); + io = NULL; + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + wolfSSL_free(ssl_c); + ssl_c = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &stats), 1); + ExpectIntEQ(stats.curAlloc, before.curAlloc); + ExpectIntEQ(stats.avaIO, before.avaIO); + + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + return EXPECT_RESULT(); +} +#endif + +/* IO pool and fixed IO blocks keep the per-SSL hint's ref count balanced. */ +static int test_wolfSSL_static_heap_io_refs(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_STATIC_MEMORY_LEAN) && \ + defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + !defined(NO_SESSION_CACHE) && !defined(NO_RSA) + ExpectIntEQ(test_static_heap_io_refs(WOLFMEM_IO_POOL_FIXED), TEST_SUCCESS); + ExpectIntEQ(test_static_heap_io_refs(WOLFMEM_IO_POOL), TEST_SUCCESS); +#endif + return EXPECT_RESULT(); +} + +/* A reallocated block keeps its ref on the per-SSL hint, even as the last + * holder after wolfSSL_free(). */ +static int test_wolfSSL_static_heap_realloc_refs(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFCRYPT_ONLY) && \ + !defined(NO_TLS) && !defined(NO_WOLFSSL_CLIENT) && \ + !defined(WOLFSSL_NO_REALLOC) + byte mem[TEST_TLS_STATIC_MEMSZ]; + WOLFSSL_HEAP_HINT* heap = NULL; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + WOLFSSL_HEAP_HINT* hint = NULL; + void* p = NULL; + void* tmp = NULL; + int refs = 0; + WOLFSSL_MEM_STATS before; + WOLFSSL_MEM_STATS after; + + XMEMSET(&before, 0, sizeof(before)); + XMEMSET(&after, 0, sizeof(after)); + ExpectIntEQ(wc_LoadStaticMemory(&heap, mem, sizeof(mem), + WOLFMEM_GENERAL | WOLFMEM_TRACK_STATS, 1), 0); + ExpectNotNull(ctx = wolfSSL_CTX_new_ex(wolfSSLv23_client_method_ex(heap), + heap)); + /* The first SSL makes a one-time allocation for the CTX; take the + * baseline after it. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + wolfSSL_free(ssl); + ssl = NULL; + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx, &before), 1); + + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl->heap); + if (EXPECT_SUCCESS()) + refs = wolfSSL_RefCur(hint->refCount); + ExpectNotNull(p = XMALLOC(64, hint, DYNAMIC_TYPE_TMP_BUFFER)); + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs + 1); + /* A bigger bucket: the new block takes the ref the old one drops. */ + ExpectNotNull(tmp = XREALLOC(p, 1000, hint, DYNAMIC_TYPE_TMP_BUFFER)); + if (tmp != NULL) { + p = tmp; + } + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs + 1); + wolfSSL_free(ssl); + ssl = NULL; + /* Only the block holds the hint now. */ + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), 1); + /* The new block is held before the old one drops the last ref. */ + ExpectNotNull(tmp = XREALLOC(p, 2000, hint, DYNAMIC_TYPE_TMP_BUFFER)); + if (tmp != NULL) { + p = tmp; + } + ExpectIntEQ(wolfSSL_RefCur(hint->refCount), 1); + /* Last holder: the hint goes back to the pool. */ + XFREE(p, hint, DYNAMIC_TYPE_TMP_BUFFER); + p = NULL; + + ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx, &after), 1); +#ifndef WOLFSSL_STATIC_MEMORY_LEAN + ExpectIntEQ(after.curAlloc, before.curAlloc); +#endif + + wolfSSL_CTX_free(ctx); + wc_UnloadStaticMemory(heap); +#endif + return EXPECT_RESULT(); +} + static int test_openssl_FIPS_drbg(void) { EXPECT_DECLS; @@ -47689,6 +47885,8 @@ TEST_CASE testCases[] = { TEST_DECL(test_wolfSSL_get1_session_static_heap), TEST_DECL(test_wolfSSL_session_ticket_static_heap), TEST_DECL(test_wolfSSL_peer_cert_chain_static_heap), + TEST_DECL(test_wolfSSL_static_heap_io_refs), + TEST_DECL(test_wolfSSL_static_heap_realloc_refs), #if !defined(NO_FILESYSTEM) && \ defined(WOLFSSL_DTLS) && !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index 98a6a6af8fa..60483db0040 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -847,7 +847,7 @@ void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap) } -/* Returns 1 if heap is a per-SSL hint created by InitSSL_StaticMemory. Only +/* Returns 1 if hint is a per-SSL hint created by InitSSL_StaticMemory. Only * these are reference counted; CTX, user and global hints are left alone. */ static int wc_HeapHint_IsRefCounted(const WOLFSSL_HEAP_HINT* hint) { @@ -863,73 +863,56 @@ static int wc_HeapHint_IsRefCounted(const WOLFSSL_HEAP_HINT* hint) return hint->sslOwned && hint->memory != NULL; } -/* Take a reference on a per-SSL heap hint so it stays valid after - * wolfSSL_free() for an object (e.g. a session) allocated from it. */ -void wc_HeapHint_Ref(void* heap) -{ - WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; - - int locked = 1; +/* An allocated block's unused next field holds its per-SSL hint (or NULL), + * on which it holds a ref. */ +#define WC_MEM_OWNER(pt) ((WOLFSSL_HEAP_HINT*)(void*)(pt)->next) +#define WC_MEM_SET_OWNER(pt, o) ((pt)->next = (wc_Memory*)(void*)(o)) - if (!wc_HeapHint_IsRefCounted(hint)) { - return; - } -#ifndef SINGLE_THREADED - if (wc_LockMutex(&hint->memory->memory_mutex) != 0) { - /* still take the reference: the caller's object is created either - * way and drops it when freed, so skipping it would free the hint - * early */ - WOLFSSL_MSG("Bad memory_mutex lock"); - locked = 0; - } -#endif - hint->refCount++; -#ifndef SINGLE_THREADED - if (locked) { - wc_UnLockMutex(&hint->memory->memory_mutex); +/* Ref hint for a new block. *owner is hint if ref counted, else NULL. */ +static int wc_HeapHint_Hold(WOLFSSL_HEAP_HINT* hint, WOLFSSL_HEAP_HINT** owner) +{ + int err = 0; + + *owner = NULL; + if (wc_HeapHint_IsRefCounted(hint)) { + wolfSSL_RefInc(&hint->refCount, &err); + if (err != 0) { + WOLFSSL_MSG("Heap hint reference increment failed"); + return err; + } + *owner = hint; } -#endif - (void)locked; + return 0; } -/* Drop a reference on a per-SSL heap hint, freeing it back to its pool when - * the last one is gone. The hint must not be used by the caller after this. */ -void wc_HeapHint_Unref(void* heap) +/* Drop a ref, freeing the hint on the last. Call without memory_mutex. */ +static void wc_HeapHint_Drop(WOLFSSL_HEAP_HINT* owner) { - WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; - WOLFSSL_HEAP_HINT tmp; - WOLFSSL_HEAP* mem; - word32 refCount; + WOLFSSL_HEAP_HINT tmp; + int isZero = 0; + int err = 0; - if (!wc_HeapHint_IsRefCounted(hint)) { - return; - } - mem = hint->memory; -#ifndef SINGLE_THREADED - if (wc_LockMutex(&mem->memory_mutex) != 0) { - WOLFSSL_MSG("Bad memory_mutex lock"); + if (owner == NULL) { return; } -#endif - if (hint->refCount == 0) { - /* unbalanced unref; leave the hint alone rather than wrap */ - WOLFSSL_MSG("Heap hint reference count underflow"); - #ifndef SINGLE_THREADED - wc_UnLockMutex(&mem->memory_mutex); - #endif - return; - } - refCount = --hint->refCount; -#ifndef SINGLE_THREADED - /* unlock before XFREE, which takes memory_mutex */ - wc_UnLockMutex(&mem->memory_mutex); -#endif - if (refCount == 0) { + wolfSSL_RefDec(&owner->refCount, &isZero, &err); + if (err == 0 && isZero) { + wolfSSL_RefFree(&owner->refCount); /* free through a temporary hint on the same pool so neither the CTX * nor the hint being freed is needed */ XMEMSET(&tmp, 0, sizeof(tmp)); - tmp.memory = mem; - XFREE(hint, &tmp, DYNAMIC_TYPE_SSL); + tmp.memory = owner->memory; + XFREE(owner, &tmp, DYNAMIC_TYPE_SSL); + } +} + +/* Drop the SSL's ref on its hint. Do not use the hint after. */ +void wc_HeapHint_Release(void* heap) +{ + WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; + + if (wc_HeapHint_IsRefCounted(hint)) { + wc_HeapHint_Drop(hint); } } @@ -1174,6 +1157,8 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) else { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; WOLFSSL_HEAP* mem; + WOLFSSL_HEAP_HINT* owner = NULL; + int fixedIO = 0; if (hint == NULL) { hint = (WOLFSSL_HEAP_HINT*)globalHeapHint; @@ -1183,18 +1168,27 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) } mem = hint->memory; + #ifndef WOLFSSL_STATIC_MEMORY_LEAN + fixedIO = (mem->flag & WOLFMEM_IO_POOL_FIXED) && + (type == DYNAMIC_TYPE_OUT_BUFFER || + type == DYNAMIC_TYPE_IN_BUFFER); + #endif + /* fixed IO buffers hold no ref */ + if (!fixedIO && wc_HeapHint_Hold(hint, &owner) != 0) { + return NULL; + } + #ifndef SINGLE_THREADED if (wc_LockMutex(&(mem->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); + wc_HeapHint_Drop(owner); return NULL; } #endif #ifndef WOLFSSL_STATIC_MEMORY_LEAN /* case of using fixed IO buffers */ - if (mem->flag & WOLFMEM_IO_POOL_FIXED && - (type == DYNAMIC_TYPE_OUT_BUFFER || - type == DYNAMIC_TYPE_IN_BUFFER)) { + if (fixedIO) { if (type == DYNAMIC_TYPE_OUT_BUFFER) { pt = hint->outBuf; } @@ -1235,6 +1229,11 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) } } } + + if (pt != NULL) { + WC_MEM_SET_OWNER(pt, owner); + owner = NULL; /* block owns the ref */ + } } if (pt != NULL) { @@ -1287,6 +1286,8 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif + /* no-op unless no block was taken */ + wc_HeapHint_Drop(owner); } #ifdef WOLFSSL_MALLOC_CHECK @@ -1355,6 +1356,7 @@ void wolfSSL_Free(void *ptr, void* heap, int type) else { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; WOLFSSL_HEAP* mem; + WOLFSSL_HEAP_HINT* owner = NULL; wc_Memory* pt; word32 padSz = -(int)sizeof(wc_Memory) & (WOLFSSL_STATIC_ALIGN - 1); @@ -1390,12 +1392,14 @@ void wolfSSL_Free(void *ptr, void* heap, int type) else if (mem->flag & WOLFMEM_IO_POOL && pt->sz == WOLFMEM_IO_SZ && (type == DYNAMIC_TYPE_OUT_BUFFER || type == DYNAMIC_TYPE_IN_BUFFER)) { + owner = WC_MEM_OWNER(pt); pt->next = mem->io; mem->io = pt; } else #endif { /* general memory free */ + owner = WC_MEM_OWNER(pt); for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) { if (pt->sz == mem->sizeList[i]) { pt->next = mem->ava[i]; @@ -1447,6 +1451,8 @@ void wolfSSL_Free(void *ptr, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif + /* may free a released hint */ + wc_HeapHint_Drop(owner); } } @@ -1485,6 +1491,8 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) else { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; WOLFSSL_HEAP* mem; + WOLFSSL_HEAP_HINT* owner = NULL; + int ioBuf = 0; word32 padSz = -(int)sizeof(wc_Memory) & (WOLFSSL_STATIC_ALIGN - 1); if (hint == NULL) { @@ -1502,18 +1510,28 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) return wolfSSL_Malloc(size, heap, type); #endif } + + #ifndef WOLFSSL_STATIC_MEMORY_LEAN + ioBuf = ((mem->flag & WOLFMEM_IO_POOL) || + (mem->flag & WOLFMEM_IO_POOL_FIXED)) && + (type == DYNAMIC_TYPE_OUT_BUFFER || + type == DYNAMIC_TYPE_IN_BUFFER); + #endif + /* IO buffers resize in place, keeping their owner */ + if (!ioBuf && wc_HeapHint_Hold(hint, &owner) != 0) { + return NULL; + } #ifndef SINGLE_THREADED if (wc_LockMutex(&(mem->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); + wc_HeapHint_Drop(owner); return NULL; } #endif #ifndef WOLFSSL_STATIC_MEMORY_LEAN /* case of using fixed IO buffers or IO pool */ - if (((mem->flag & WOLFMEM_IO_POOL)||(mem->flag & WOLFMEM_IO_POOL_FIXED)) - && (type == DYNAMIC_TYPE_OUT_BUFFER || - type == DYNAMIC_TYPE_IN_BUFFER)) { + if (ioBuf) { /* no realloc, is fixed size */ pt = (wc_Memory*)((byte*)ptr - padSz - sizeof(wc_Memory)); if (pt->sz < size) { @@ -1552,6 +1570,9 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) mem->inUse += pt->sz; mem->alloc += 1; #endif + /* before the old block's ref drops */ + WC_MEM_SET_OWNER(pt, owner); + owner = NULL; /* free memory that was previously being used */ #ifndef SINGLE_THREADED @@ -1573,6 +1594,8 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif + /* no-op unless no new block was taken */ + wc_HeapHint_Drop(owner); } #ifdef WOLFSSL_MALLOC_CHECK diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 91a9686927e..6ef301d4a34 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -277,7 +277,7 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf, wc_Memory* inBuf; byte haFlag; /* flag used for checking handshake count */ #endif - word32 refCount; /* SSL ref + other refs */ + wolfSSL_Ref refCount; /* SSL + each allocated block */ byte sslOwned; /* created by InitSSL_StaticMemory */ } WOLFSSL_HEAP_HINT; @@ -300,8 +300,7 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf, WOLFSSL_API int wc_LoadStaticMemory(WOLFSSL_HEAP_HINT** pHint, unsigned char* buf, unsigned int sz, int flag, int max); WOLFSSL_API void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap); - WOLFSSL_LOCAL void wc_HeapHint_Ref(void* heap); - WOLFSSL_LOCAL void wc_HeapHint_Unref(void* heap); + WOLFSSL_LOCAL void wc_HeapHint_Release(void* heap); WOLFSSL_API int wolfSSL_GetMemStats(WOLFSSL_HEAP* heap, WOLFSSL_MEM_STATS* stats); From 19fa954b9692b53924ee081b967d31ebd811158b Mon Sep 17 00:00:00 2001 From: Aidan Keefe Date: Thu, 29 Oct 2026 10:48:26 -0600 Subject: [PATCH 3/3] refCount now word32 not wolfSSL_RefCount --- src/internal.c | 15 +---- tests/api.c | 70 ++++++++++---------- wolfcrypt/src/memory.c | 129 +++++++++++++++++-------------------- wolfssl/wolfcrypt/memory.h | 2 +- 4 files changed, 96 insertions(+), 120 deletions(-) diff --git a/src/internal.c b/src/internal.c index e83678759be..001452a22ac 100644 --- a/src/internal.c +++ b/src/internal.c @@ -8893,7 +8893,6 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) if (ctx->heap != NULL) { WOLFSSL_HEAP_HINT* ssl_hint; WOLFSSL_HEAP_HINT* ctx_hint; - int ret = 0; /* avoid dereferencing a test value */ #ifdef WOLFSSL_HEAP_TEST @@ -8913,23 +8912,13 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) ssl_hint->memory = ctx_hint->memory; /* the SSL's ref; each allocated block holds another */ - wolfSSL_RefInit(&ssl_hint->refCount, &ret); - #ifdef WOLFSSL_REFCNT_ERROR_RETURN - if (ret != 0) { - WOLFSSL_MSG("Error setting up heap hint reference mutex"); - XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); - ssl->heap = NULL; - return ret; - } - #endif - (void)ret; + ssl_hint->refCount = 1; ssl_hint->sslOwned = 1; #ifndef WOLFSSL_STATIC_MEMORY_LEAN #ifndef SINGLE_THREADED /* lock and check IO count / handshake count */ if (wc_LockMutex(&(ctx_hint->memory->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); - wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E); @@ -8942,7 +8931,6 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #ifndef SINGLE_THREADED wc_UnLockMutex(&(ctx_hint->memory->memory_mutex)); #endif - wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ return MEMORY_E; @@ -8954,7 +8942,6 @@ static int InitSSL_StaticMemory(WOLFSSL* ssl, WOLFSSL_CTX* ctx) #ifndef SINGLE_THREADED wc_UnLockMutex(&(ctx_hint->memory->memory_mutex)); #endif - wolfSSL_RefFree(&ssl_hint->refCount); XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL); ssl->heap = NULL; /* free and set to NULL for IO counter */ return MEMORY_E; diff --git a/tests/api.c b/tests/api.c index 5b67868e95e..9ba75b77d6c 100644 --- a/tests/api.c +++ b/tests/api.c @@ -39757,7 +39757,7 @@ static int test_wolfSSL_get1_session_static_heap(void) WOLFSSL_SESSION* sess = NULL; WOLFSSL_SESSION* dup = NULL; WOLFSSL_HEAP_HINT* hint = NULL; - int refs = 0; + word32 refs = 0; WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS after; @@ -39778,14 +39778,14 @@ static int test_wolfSSL_get1_session_static_heap(void) /* Held by the SSL and each allocated block. */ ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl->heap); ExpectIntEQ(hint->sslOwned, 1); - ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGT(refs = hint->refCount, 1); ExpectNotNull(sess = wolfSSL_get1_session(ssl)); ExpectPtrEq(sess->heap, hint); /* A session ref allocates nothing. */ - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntEQ(hint->refCount, refs); wolfSSL_SESSION_free(sess); sess = NULL; - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntEQ(hint->refCount, refs); /* No handshake done, so clear replaces the session. */ ExpectIntEQ(wolfSSL_clear(ssl), WOLFSSL_SUCCESS); ExpectNotNull(sess = wolfSSL_get1_session(ssl)); @@ -39793,14 +39793,14 @@ static int test_wolfSSL_get1_session_static_heap(void) wolfSSL_free(ssl); ssl = NULL; /* Only the session holds the hint now. */ - ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(refs = hint->refCount, 1); /* Both operations go through sess->heap after the SSL is gone. */ ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); ExpectPtrEq(dup->heap, hint); - ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntGT(hint->refCount, refs); wolfSSL_SESSION_free(dup); - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntEQ(hint->refCount, refs); /* Last holder: the hint goes back to the pool. */ wolfSSL_SESSION_free(sess); @@ -39866,7 +39866,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) WOLFSSL_SESSION* sess = NULL; WOLFSSL_SESSION* dup = NULL; WOLFSSL_HEAP_HINT* hint = NULL; - int refs = 0; + word32 refs = 0; WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS after; char buf[1]; @@ -39916,7 +39916,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) wolfSSL_free(ssl_s); ssl_s = NULL; /* The session keeps the client's hint alive. */ - ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(refs = hint->refCount, 1); /* Resume with the session on a new SSL; a new ticket replaces the old. */ ExpectIntEQ(test_session_static_heap_new_pair(&test_ctx, &ctx_c, &ctx_s, @@ -39926,7 +39926,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) ExpectPtrNE(ssl_c->heap, hint); ExpectPtrNE(ssl_c->session, sess); ExpectPtrEq(ssl_c->session->heap, hint); - ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntGT(hint->refCount, refs); wolfSSL_SESSION_free(sess); sess = NULL; ExpectNotNull(sess = wolfSSL_get1_session(ssl_c)); @@ -39935,7 +39935,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) wolfSSL_free(ssl_s); ssl_s = NULL; /* The hint has outlived both SSLs. */ - ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(hint->refCount, 1); wolfSSL_SESSION_free(sess); sess = NULL; @@ -39948,12 +39948,12 @@ static int test_wolfSSL_session_ticket_static_heap(void) ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); wolfSSL_free(ssl_c); ssl_c = NULL; - ExpectIntGE(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(refs = hint->refCount, 1); ExpectNotNull(dup = wolfSSL_SESSION_dup(sess)); - ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntGT(hint->refCount, refs); wolfSSL_SESSION_free(dup); dup = NULL; - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntEQ(hint->refCount, refs); wolfSSL_SESSION_free(sess); sess = NULL; @@ -39988,7 +39988,7 @@ static int test_wolfSSL_session_ticket_static_heap(void) return EXPECT_RESULT(); } -#if defined(WOLFSSL_STATIC_MEMORY) && \ +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_RSA) && \ !defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH) @@ -40017,7 +40017,7 @@ static int test_peer_cert_static_heap_connect(struct test_memio_ctx* test_ctx, static int test_wolfSSL_peer_cert_chain_static_heap(void) { EXPECT_DECLS; -#if defined(WOLFSSL_STATIC_MEMORY) && \ +#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFSSL_NO_MALLOC) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) && !defined(NO_RSA) && \ !defined(NO_SESSION_CACHE) && !defined(WOLFSSL_NO_CLIENT_AUTH) @@ -40029,7 +40029,7 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) WOLFSSL* ssl_s = NULL; WOLFSSL_SESSION* sess = NULL; WOLFSSL_HEAP_HINT* hint = NULL; - int refs = 0; + word32 refs = 0; #ifdef KEEP_PEER_CERT WOLFSSL_X509* peer = NULL; #endif @@ -40071,18 +40071,18 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) &ssl_c, &ssl_s), TEST_SUCCESS); /* Held by the SSL and each allocated block. */ ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); - ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGT(refs = hint->refCount, 1); ExpectNotNull(wolfSSL_get_peer_cert_chain(ssl_s)); ExpectNotNull(sess = wolfSSL_get1_session(ssl_s)); ExpectNotNull(sess->peer); /* The peer is on the SSL's hint too. */ ExpectPtrEq(sess->peer->heap, hint); - ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntGT(hint->refCount, refs); wolfSSL_free(ssl_s); ssl_s = NULL; /* Only the session and peer hold the hint now. */ - ExpectIntGE(wolfSSL_RefCur(hint->refCount), 2); + ExpectIntGE(hint->refCount, 2); /* The peer is still usable once the SSL is gone. */ ExpectNotNull(wolfSSL_SESSION_get0_peer(sess)); wolfSSL_SESSION_free(sess); @@ -40095,15 +40095,15 @@ static int test_wolfSSL_peer_cert_chain_static_heap(void) ExpectIntEQ(test_peer_cert_static_heap_connect(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s), TEST_SUCCESS); ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_s->heap); - ExpectIntGT(refs = wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGT(refs = hint->refCount, 1); /* A copy of ssl->peerCert, so on the SSL's hint. */ ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s)); ExpectPtrEq(peer->heap, hint); - ExpectIntGT(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntGT(hint->refCount, refs); wolfSSL_free(ssl_s); ssl_s = NULL; /* Only the X509 holds the hint now. */ - ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(hint->refCount, 1); ExpectNotNull(wolfSSL_X509_get_subject_name(peer)); /* Last holder: the hint goes back to the pool. */ wolfSSL_X509_free(peer); @@ -40144,7 +40144,7 @@ static int test_static_heap_io_refs(int ioFlag) WOLFSSL_HEAP_HINT* hint = NULL; void* io = NULL; void* tmp = NULL; - int refs = 0; + word32 refs = 0; int fixed = (ioFlag == WOLFMEM_IO_POOL_FIXED); WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS stats; @@ -40199,7 +40199,7 @@ static int test_static_heap_io_refs(int ioFlag) ssl_c = NULL; wolfSSL_free(ssl_s); ssl_s = NULL; - ExpectIntGE(wolfSSL_RefCur(hint->refCount), 1); + ExpectIntGE(hint->refCount, 1); /* All IO buffers are back in the pool while the hint lives on. */ ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &stats), 1); ExpectIntEQ(stats.avaIO, before.avaIO); @@ -40215,20 +40215,20 @@ static int test_static_heap_io_refs(int ioFlag) ExpectNotNull(ssl_c = wolfSSL_new(ctx_c)); ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl_c->heap); if (EXPECT_SUCCESS()) - refs = wolfSSL_RefCur(hint->refCount); + refs = hint->refCount; ExpectNotNull(io = XMALLOC(64, hint, DYNAMIC_TYPE_IN_BUFFER)); - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), fixed ? refs : refs + 1); + ExpectIntEQ(hint->refCount, fixed ? refs : refs + 1); #ifndef WOLFSSL_NO_REALLOC ExpectNotNull(tmp = XREALLOC(io, 128, hint, DYNAMIC_TYPE_IN_BUFFER)); /* IO buffers are fixed size, so resized in place. */ ExpectPtrEq(tmp, io); - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), fixed ? refs : refs + 1); + ExpectIntEQ(hint->refCount, fixed ? refs : refs + 1); #else (void)tmp; #endif XFREE(io, hint, DYNAMIC_TYPE_IN_BUFFER); io = NULL; - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs); + ExpectIntEQ(hint->refCount, refs); wolfSSL_free(ssl_c); ssl_c = NULL; ExpectIntEQ(wolfSSL_CTX_is_static_memory(ctx_c, &stats), 1); @@ -40269,7 +40269,7 @@ static int test_wolfSSL_static_heap_realloc_refs(void) WOLFSSL_HEAP_HINT* hint = NULL; void* p = NULL; void* tmp = NULL; - int refs = 0; + word32 refs = 0; WOLFSSL_MEM_STATS before; WOLFSSL_MEM_STATS after; @@ -40289,25 +40289,25 @@ static int test_wolfSSL_static_heap_realloc_refs(void) ExpectNotNull(ssl = wolfSSL_new(ctx)); ExpectNotNull(hint = (WOLFSSL_HEAP_HINT*)ssl->heap); if (EXPECT_SUCCESS()) - refs = wolfSSL_RefCur(hint->refCount); + refs = hint->refCount; ExpectNotNull(p = XMALLOC(64, hint, DYNAMIC_TYPE_TMP_BUFFER)); - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs + 1); + ExpectIntEQ(hint->refCount, refs + 1); /* A bigger bucket: the new block takes the ref the old one drops. */ ExpectNotNull(tmp = XREALLOC(p, 1000, hint, DYNAMIC_TYPE_TMP_BUFFER)); if (tmp != NULL) { p = tmp; } - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), refs + 1); + ExpectIntEQ(hint->refCount, refs + 1); wolfSSL_free(ssl); ssl = NULL; /* Only the block holds the hint now. */ - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), 1); + ExpectIntEQ(hint->refCount, 1); /* The new block is held before the old one drops the last ref. */ ExpectNotNull(tmp = XREALLOC(p, 2000, hint, DYNAMIC_TYPE_TMP_BUFFER)); if (tmp != NULL) { p = tmp; } - ExpectIntEQ(wolfSSL_RefCur(hint->refCount), 1); + ExpectIntEQ(hint->refCount, 1); /* Last holder: the hint goes back to the pool. */ XFREE(p, hint, DYNAMIC_TYPE_TMP_BUFFER); p = NULL; diff --git a/wolfcrypt/src/memory.c b/wolfcrypt/src/memory.c index 60483db0040..7aa8c71115f 100644 --- a/wolfcrypt/src/memory.c +++ b/wolfcrypt/src/memory.c @@ -848,7 +848,9 @@ void wc_UnloadStaticMemory(WOLFSSL_HEAP_HINT* heap) /* Returns 1 if hint is a per-SSL hint created by InitSSL_StaticMemory. Only - * these are reference counted; CTX, user and global hints are left alone. */ + * these are reference counted; CTX, user and global hints are left alone. + * + * Needs hint to not be garbage data. */ static int wc_HeapHint_IsRefCounted(const WOLFSSL_HEAP_HINT* hint) { if (hint == NULL) { @@ -868,52 +870,65 @@ static int wc_HeapHint_IsRefCounted(const WOLFSSL_HEAP_HINT* hint) #define WC_MEM_OWNER(pt) ((WOLFSSL_HEAP_HINT*)(void*)(pt)->next) #define WC_MEM_SET_OWNER(pt, o) ((pt)->next = (wc_Memory*)(void*)(o)) -/* Ref hint for a new block. *owner is hint if ref counted, else NULL. */ -static int wc_HeapHint_Hold(WOLFSSL_HEAP_HINT* hint, WOLFSSL_HEAP_HINT** owner) +/* Ref hint for a new block. Returns hint if ref counted, else NULL. Call with + * memory_mutex held. */ +static WOLFSSL_HEAP_HINT* wc_HeapHint_Hold(WOLFSSL_HEAP_HINT* hint) { - int err = 0; - - *owner = NULL; - if (wc_HeapHint_IsRefCounted(hint)) { - wolfSSL_RefInc(&hint->refCount, &err); - if (err != 0) { - WOLFSSL_MSG("Heap hint reference increment failed"); - return err; - } - *owner = hint; + if (!wc_HeapHint_IsRefCounted(hint)) { + return NULL; } - return 0; + hint->refCount++; + return hint; +} + +/* Drop a ref. Returns owner if that was the last, to pass to wc_HeapHint_Free + * once memory_mutex is released, else NULL. Call with memory_mutex held. */ +static WOLFSSL_HEAP_HINT* wc_HeapHint_Drop(WOLFSSL_HEAP_HINT* owner) +{ + if (owner == NULL || --owner->refCount != 0) { + return NULL; + } + return owner; } -/* Drop a ref, freeing the hint on the last. Call without memory_mutex. */ -static void wc_HeapHint_Drop(WOLFSSL_HEAP_HINT* owner) +/* Free a hint whose last ref was dropped. Call without memory_mutex. */ +static void wc_HeapHint_Free(WOLFSSL_HEAP_HINT* owner) { WOLFSSL_HEAP_HINT tmp; - int isZero = 0; - int err = 0; if (owner == NULL) { return; } - wolfSSL_RefDec(&owner->refCount, &isZero, &err); - if (err == 0 && isZero) { - wolfSSL_RefFree(&owner->refCount); - /* free through a temporary hint on the same pool so neither the CTX - * nor the hint being freed is needed */ - XMEMSET(&tmp, 0, sizeof(tmp)); - tmp.memory = owner->memory; - XFREE(owner, &tmp, DYNAMIC_TYPE_SSL); - } + /* free through a temporary hint on the same pool so neither the CTX + * nor the hint being freed is needed */ + XMEMSET(&tmp, 0, sizeof(tmp)); + tmp.memory = owner->memory; + XFREE(owner, &tmp, DYNAMIC_TYPE_SSL); } /* Drop the SSL's ref on its hint. Do not use the hint after. */ void wc_HeapHint_Release(void* heap) { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; +#ifndef SINGLE_THREADED + WOLFSSL_HEAP* mem = NULL; +#endif - if (wc_HeapHint_IsRefCounted(hint)) { - wc_HeapHint_Drop(hint); + if (!wc_HeapHint_IsRefCounted(hint)) { + return; + } +#ifndef SINGLE_THREADED + mem = hint->memory; + if (wc_LockMutex(&(mem->memory_mutex)) != 0) { + WOLFSSL_MSG("Bad memory_mutex lock"); + return; } +#endif + hint = wc_HeapHint_Drop(hint); +#ifndef SINGLE_THREADED + wc_UnLockMutex(&(mem->memory_mutex)); +#endif + wc_HeapHint_Free(hint); } #ifndef WOLFSSL_STATIC_MEMORY_LEAN @@ -1157,8 +1172,6 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) else { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; WOLFSSL_HEAP* mem; - WOLFSSL_HEAP_HINT* owner = NULL; - int fixedIO = 0; if (hint == NULL) { hint = (WOLFSSL_HEAP_HINT*)globalHeapHint; @@ -1168,27 +1181,18 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) } mem = hint->memory; - #ifndef WOLFSSL_STATIC_MEMORY_LEAN - fixedIO = (mem->flag & WOLFMEM_IO_POOL_FIXED) && - (type == DYNAMIC_TYPE_OUT_BUFFER || - type == DYNAMIC_TYPE_IN_BUFFER); - #endif - /* fixed IO buffers hold no ref */ - if (!fixedIO && wc_HeapHint_Hold(hint, &owner) != 0) { - return NULL; - } - #ifndef SINGLE_THREADED if (wc_LockMutex(&(mem->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); - wc_HeapHint_Drop(owner); return NULL; } #endif #ifndef WOLFSSL_STATIC_MEMORY_LEAN /* case of using fixed IO buffers */ - if (fixedIO) { + if (mem->flag & WOLFMEM_IO_POOL_FIXED && + (type == DYNAMIC_TYPE_OUT_BUFFER || + type == DYNAMIC_TYPE_IN_BUFFER)) { if (type == DYNAMIC_TYPE_OUT_BUFFER) { pt = hint->outBuf; } @@ -1230,9 +1234,9 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) } } + /* fixed IO buffers hold no ref */ if (pt != NULL) { - WC_MEM_SET_OWNER(pt, owner); - owner = NULL; /* block owns the ref */ + WC_MEM_SET_OWNER(pt, wc_HeapHint_Hold(hint)); } } @@ -1286,8 +1290,6 @@ void* wolfSSL_Malloc(size_t size, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif - /* no-op unless no block was taken */ - wc_HeapHint_Drop(owner); } #ifdef WOLFSSL_MALLOC_CHECK @@ -1392,14 +1394,14 @@ void wolfSSL_Free(void *ptr, void* heap, int type) else if (mem->flag & WOLFMEM_IO_POOL && pt->sz == WOLFMEM_IO_SZ && (type == DYNAMIC_TYPE_OUT_BUFFER || type == DYNAMIC_TYPE_IN_BUFFER)) { - owner = WC_MEM_OWNER(pt); + owner = wc_HeapHint_Drop(WC_MEM_OWNER(pt)); pt->next = mem->io; mem->io = pt; } else #endif { /* general memory free */ - owner = WC_MEM_OWNER(pt); + owner = wc_HeapHint_Drop(WC_MEM_OWNER(pt)); for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) { if (pt->sz == mem->sizeList[i]) { pt->next = mem->ava[i]; @@ -1451,8 +1453,8 @@ void wolfSSL_Free(void *ptr, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif - /* may free a released hint */ - wc_HeapHint_Drop(owner); + /* set if this block held the last ref on a released hint */ + wc_HeapHint_Free(owner); } } @@ -1491,8 +1493,6 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) else { WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap; WOLFSSL_HEAP* mem; - WOLFSSL_HEAP_HINT* owner = NULL; - int ioBuf = 0; word32 padSz = -(int)sizeof(wc_Memory) & (WOLFSSL_STATIC_ALIGN - 1); if (hint == NULL) { @@ -1511,27 +1511,19 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) #endif } - #ifndef WOLFSSL_STATIC_MEMORY_LEAN - ioBuf = ((mem->flag & WOLFMEM_IO_POOL) || - (mem->flag & WOLFMEM_IO_POOL_FIXED)) && - (type == DYNAMIC_TYPE_OUT_BUFFER || - type == DYNAMIC_TYPE_IN_BUFFER); - #endif - /* IO buffers resize in place, keeping their owner */ - if (!ioBuf && wc_HeapHint_Hold(hint, &owner) != 0) { - return NULL; - } #ifndef SINGLE_THREADED if (wc_LockMutex(&(mem->memory_mutex)) != 0) { WOLFSSL_MSG("Bad memory_mutex lock"); - wc_HeapHint_Drop(owner); return NULL; } #endif #ifndef WOLFSSL_STATIC_MEMORY_LEAN - /* case of using fixed IO buffers or IO pool */ - if (ioBuf) { + /* case of using fixed IO buffers or IO pool, resized in place + * keeping their owner */ + if (((mem->flag & WOLFMEM_IO_POOL)||(mem->flag & WOLFMEM_IO_POOL_FIXED)) + && (type == DYNAMIC_TYPE_OUT_BUFFER || + type == DYNAMIC_TYPE_IN_BUFFER)) { /* no realloc, is fixed size */ pt = (wc_Memory*)((byte*)ptr - padSz - sizeof(wc_Memory)); if (pt->sz < size) { @@ -1571,8 +1563,7 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) mem->alloc += 1; #endif /* before the old block's ref drops */ - WC_MEM_SET_OWNER(pt, owner); - owner = NULL; + WC_MEM_SET_OWNER(pt, wc_HeapHint_Hold(hint)); /* free memory that was previously being used */ #ifndef SINGLE_THREADED @@ -1594,8 +1585,6 @@ void* wolfSSL_Realloc(void *ptr, size_t size, void* heap, int type) #ifndef SINGLE_THREADED wc_UnLockMutex(&(mem->memory_mutex)); #endif - /* no-op unless no new block was taken */ - wc_HeapHint_Drop(owner); } #ifdef WOLFSSL_MALLOC_CHECK diff --git a/wolfssl/wolfcrypt/memory.h b/wolfssl/wolfcrypt/memory.h index 6ef301d4a34..aa7fe615311 100644 --- a/wolfssl/wolfcrypt/memory.h +++ b/wolfssl/wolfcrypt/memory.h @@ -277,7 +277,7 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf, wc_Memory* inBuf; byte haFlag; /* flag used for checking handshake count */ #endif - wolfSSL_Ref refCount; /* SSL + each allocated block */ + word32 refCount; /* SSL + each allocated block */ byte sslOwned; /* created by InitSSL_StaticMemory */ } WOLFSSL_HEAP_HINT;