Repository navigation
Allocate the TLS peer certificate on the heap with WOLFSSL_DYNAMIC_PEER_CERT - #11990
Conversation
|
There was a problem hiding this comment.
🟡 Changes recommended
Shared peer certificates can trigger premature ex-data cleanup while retained references remain alive, and the new test incorrectly uses PEM loading for DER fixtures.
2 open issues
What changed in this PR
Adds optional heap allocation for TLS peer certificates, reducing per-connection memory use when no certificate is kept. This extends the earlier certificate-parsing optimizations while preserving embedded storage when the feature is disabled.
Changes:
- Adds
WOLFSSL_DYNAMIC_PEER_CERT, enabled by default for eligible small-stack builds. - Centralizes certificate ownership and access, returning shared references where supported.
- Adds lifetime tests and documents reference-versus-copy behavior.
| File | Description |
|---|---|
wolfssl/wolfcrypt/settings.h |
Defines feature defaults and allocation constraints. |
wolfssl/internal.h |
Adds optional pointer storage and certificate helpers. |
tests/api/test_tls13.c |
Adapts ticket verification assertions for pointer storage. |
tests/api/test_ssl_cert.h |
Registers the new certificate lifetime test. |
tests/api/test_ssl_cert.c |
Tests ownership, reference counts, and certificate lifetime. |
src/tls13.c |
Uses certificate access helpers and checks for missing certificates. |
src/ssl.c |
Updates peer-name access and certificate clearing. |
src/ssl_api_cert.c |
Supports shared references and direct verified-chain access. |
src/internal.c |
Implements allocation, replacement, and release helpers. |
doc/dox_comments/header_files/ssl.h |
Documents ownership and required certificate cleanup. |
.wolfssl_known_macro_extras |
Registers the feature and opt-out macros. |
Review effort: Balanced
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11990
Scan targets checked: wolfcrypt-src, wolfcrypt-bugs, wolfssl-src, wolfssl-bugs
Not reviewed (no scan target covers them): .wolfssl_known_macro_extras, doc/dox_comments/header_files/ssl.h, tests/api/test_ssl_cert.h
Coverage: 7 of 9 in-scope changed file(s) opened by the reviewer; not opened: tests/api/test_ssl_cert.c, tests/api/test_tls13.c
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Lite
67dbc42 to
60ba1c6
Compare
dgarske
left a comment
There was a problem hiding this comment.
Jenkins CI seems unhappy with Valgrind and openssl
…ER_CERT With KEEP_PEER_CERT, which OPENSSL_EXTRA turns on, every WOLFSSL embeds a whole WOLFSSL_X509 for the peer's certificate (3.4 KB on Cortex-M33, 4.3 KB with OPENSSL_ALL) and InitSSL() zeroes it. It is paid for when there is no peer certificate: servers that do not ask for client authentication, PSK connections, and objects before the handshake. WOLFSSL_DYNAMIC_PEER_CERT makes ssl->peerCert a pointer instead, NULL until a certificate is kept, with the WOLFSSL holding one reference. SetPeerCert() replaces it with a new object each time instead of reusing it, so a reference handed out stays valid across renegotiation, wolfSSL_clear() and a certificate restored from a ticket, and after wolfSSL_free(). With X509 reference counting (OPENSSL_EXTRA or OPENSSL_EXTRA_X509_SMALL), wolfSSL_get_peer_certificate() then returns the kept object with a new reference, as OpenSSL does, instead of a copy that parses the certificate DER again. WOLFSSL_DYNAMIC_PEER_CERT is on by default when KEEP_PEER_CERT and WOLFSSL_SMALL_STACK are defined, as large objects then already go on the heap. Define NO_WOLFSSL_DYNAMIC_PEER_CERT to keep the embedded object, or WOLFSSL_DYNAMIC_PEER_CERT to turn it on without WOLFSSL_SMALL_STACK. It is always off with WOLFSSL_NO_MALLOC, where the certificate could not be allocated and so would never be kept. With the flag off the certificate stays embedded and behaves as before. GetPeerCert() returns the kept object in either mode, and FreePeerCert() and SetPeerCert() release and replace it. The leaf is copied, and with the flag on allocated, once per handshake (peerCertSet). wolfSSL_get0_verified_chain() uses the kept object directly instead of taking a reference or copy and dropping it.
ExternalFreeX509() ran the HAVE_EX_DATA_CLEANUP_HOOKS clean-up on every wolfSSL_X509_free(), before the reference count was dropped, so freeing one reference cleared the ex_data, and ran its clean-up routine, for every other holder. With WOLFSSL_DYNAMIC_PEER_CERT this is reachable through the peer certificate: freeing one returned reference, wolfSSL_clear() or wolfSSL_free() cleared the ex_data of a certificate still in use. Run the clean-up with the final free, as WOLFSSL_RSA, WOLFSSL_BIO and WOLFSSL_X509_STORE do. The peer certificate test now checks that ex_data survives an extra reference being freed and wolfSSL_clear(), and is cleaned up once at the last free. It also loads the server certificate with CERT_FILETYPE, to match the certificate files test_memio_setup() selects.
60ba1c6 to
85ef826
Compare
|
Jenkins: retest this please |


Summary
With
KEEP_PEER_CERT, whichOPENSSL_EXTRA,OPENSSL_ALLand theapplication recipes turn on, every
WOLFSSLembeds a wholeWOLFSSL_X509for the peer's certificate (3.4 KB on Cortex-M33, 4.3 KB with
OPENSSL_ALL), andInitSSL()zeroes it. It is paid for even when there isno peer certificate: servers that do not ask for client authentication, PSK
connections, and every object before its handshake.
This adds
WOLFSSL_DYNAMIC_PEER_CERT. With it,ssl->peerCertis a pointer,NULL until a certificate is kept, and the
WOLFSSLholds one reference. Anew certificate (renegotiation,
wolfSSL_clear()and a new handshake, acertificate restored from a ticket) replaces the object instead of reusing
it, so a reference handed out stays valid, also after
wolfSSL_free().With X509 reference counting (
OPENSSL_EXTRAorOPENSSL_EXTRA_X509_SMALL),wolfSSL_get_peer_certificate()returns the kept object with a newreference, as OpenSSL does, instead of a copy that parses the DER again.
Gating and defaults
KEEP_PEER_CERTandWOLFSSL_SMALL_STACKare defined,as large objects then already go on the heap (
--enable-smallstack, orWOLFSSL_SMALL_STACKinuser_settings.h). Default configure builds areunchanged.
NO_WOLFSSL_DYNAMIC_PEER_CERTkeeps the embedded object.WOLFSSL_DYNAMIC_PEER_CERTturns it on withoutWOLFSSL_SMALL_STACK.WOLFSSL_NO_MALLOC, even if defined, as the certificatecould not be allocated and so would never be kept. Undefined without
KEEP_PEER_CERT.including
ReinitX509()andwolfSSL_get_peer_certificate()returning acopy.
The names (
WOLFSSL_DYNAMIC_PEER_CERT,NO_WOLFSSL_DYNAMIC_PEER_CERT,GetPeerCert(),FreePeerCert(),SetPeerCert()) match dgarske'sossl_lowmembranch. Internal code reaches the certificate throughGetPeerCert(ssl)in either mode.Other changes
in
SetupStoreCtxCallback()orProcessPeerCerts()(peerCertSet).wolfSSL_get0_verified_chain()uses the kept certificate directlyinstead of taking a reference or copy and dropping it.
PEER_KEY_ERRORwhen nopeer certificate is kept.
wolfSSL_get_peer_certificate()Doxygen describes both modes.Measurements
Memory harness, STM32U585 (Cortex-M33), ECC, no client auth on the server,
heap in bytes, before -> after. The harness settings define
WOLFSSL_SMALL_STACK, so the flag is on. Measured on the first version ofthis change (#11925), which always used the heap pointer and matches this
PR with the flag on.
Client handshake and resumption times are unchanged. On x86_64,
sizeof(WOLFSSL) goes 5632 -> 1552 (EXTRA) and 6976 -> 1752 (ALL), and the
resident server connection 7986 -> 3906 and 13898 -> 8674.
Testing
make checkwith the flag on and off:--enable-opensslextra,--enable-opensslall,--enable-all, and--enable-opensslextra=x509smallwith-DKEEP_PEER_CERT.--enable-staticmemorywithWOLFSSL_NO_MALLOCand theflag requested (it resolves off),
--enable-smallstack(on by default),and
NO_WOLFSSL_DYNAMIC_PEER_CERT(off).test_wolfSSL_get_peer_certificate_refcovers both modes: nocertificate before the handshake, none kept by a server without client
auth, reference counts with the flag on and separate copies with it off,
and a returned certificate that outlives
wolfSSL_clear()plus a newhandshake and
wolfSSL_free(). It needsOPENSSL_EXTRA, so the x509smallbuilds skip it.
Related
anything.
merged.
CreateTicket()now reads the certificate throughGetPeerCert(ssl)in Size session ticket buffers for tickets without a peer cert #11991's sizing code.