Skip to content

Fixes for static memory builds with WOLFSSL_NO_MALLOC - #11432

Open
Frauschi wants to merge 3 commits into
wolfSSL:masterfrom
Frauschi:rsa-ca-pubkey-static-memory
Open

Fixes for static memory builds with WOLFSSL_NO_MALLOC#11432
Frauschi wants to merge 3 commits into
wolfSSL:masterfrom
Frauschi:rsa-ca-pubkey-static-memory

Conversation

@Frauschi

@Frauschi Frauschi commented Sep 10, 2026

Copy link
Copy Markdown
Member

WOLFSSL_NO_MALLOC removes only the stdio malloc fall-back, so a build that also has --enable-staticmemory still has a working allocator. Two places treat that configuration as having no allocator at all: one makes every RSA CA useless for chain verification, the other stops any TLS connection being established. They are independent and sit in different files. A third commit sizes the wolfCrypt test pool so make check exercises the configuration.

Found while running wolfCert's CI matrix, which builds it.

1. RSA public key never reaches the CA Signer

ParseCert() guarded its RSA public-key copy with !defined(WOLFSSL_NO_MALLOC), while StoreKey() guards the equivalent copy for every non-RSA key with !defined(WC_ASN_NO_HEAP). Only the latter means "genuinely no allocator", which is what the copy depends on, so a static memory build skipped it despite having somewhere to copy to.

cert->pubKeyStored therefore stayed 0, FillSigner() never populated signer->publicKey / pubKeySize, and ConfirmSignature() was handed a NULL key and zero size, which it rejects with BAD_FUNC_ARG. No certificate issued by an RSA CA could be verified against it, through wolfSSL_CertManagerVerifyBuffer() or TLS peer validation alike. ECC, Ed25519, Ed448 and ML-DSA CAs were unaffected, because those keys travel through StoreKey().

The fix uses WC_ASN_NO_HEAP in all three guards in ParseCert(), including the one on the char* ptr declaration. FreeDecodedCert() and FreeSigner() already key off pubKeyStored, so ownership is unchanged.

2. Loading a static pool never set the global heap hint

An allocation made outside any WOLFSSL_CTX or WOLFSSL object can only be served from the global heap hint, and loading a pool never set it. wolfSSL_Init() makes exactly such an allocation: under OPENSSL_EXTRA it seeds the compatibility layer RNG with a NULL heap. That returned NULL, wolfSSL_Init() reported WC_INIT_E, and every wolfSSL_CTX_new_ex() that triggered the lazy init failed, so no TLS connection could be established at all regardless of pool size.

The two bundled examples reach this from different entry points, the server through wolfSSL_CTX_load_static_memory() and the client through a direct wc_LoadStaticMemory(), so the fix goes in wc_LoadStaticMemory_ex(), which both funnel through. It applies only when no hint has been set yet, so an application managing the hint itself keeps control.

Pools loaded with WOLFMEM_IO_POOL or WOLFMEM_IO_POOL_FIXED are skipped. wc_partition_static_memory() puts every chunk of such a pool on the heap's io list and leaves the general buckets empty, so adopting one would give a global allocator that fails every general request, and would then stop a general pool loaded afterwards from taking the hint. Load order does not matter.

wc_UnloadStaticMemory() now clears the hint when it refers to the heap being unloaded. The hint lives inside the caller's pool buffer, which the caller may reuse once the pool is gone, and the mutex it reaches through has just been destroyed. That part is not gated on WOLFSSL_NO_MALLOC: an application setting the global hint by hand has always been able to leave it dangling the same way.

Reviewers may recall 98a19f9, which removed commented-out wolfSSL_SetGlobalHeapHint() calls from both examples because the pool "does not handle all memory used on default build". That concern does not apply here: the change is confined to WOLFSSL_NO_MALLOC, where the alternative to serving those allocations from the pool is failing them outright. Builds with a system heap are unaffected.

3. wolfCrypt test pool sized for the configuration

Once the pool serves the compatibility layer's NULL-heap allocations, gTestMemory was too small and openssl_test() failed on exhaustion. The WOLFSSL_NO_MALLOC and OPENSSL_EXTRA combination now gets 1 MB; every other configuration keeps the size it had.

Testing

With --enable-staticmemory -DWOLFSSL_NO_MALLOC, make check goes from 0 of 5 passing to 3 of 5: resume.test, tls13.test and testsuite all move from fail to pass. A standalone wolfSSL_CertManagerVerifyBuffer() on an RSA CA in that build goes from -173 to WOLFSSL_SUCCESS, and wolfCert's integration suite passes against it with its local workaround for defect 1 removed.

--enable-staticmemory without WOLFSSL_NO_MALLOC - the configuration the unload change newly touches - passes make check 5 of 5. The default configuration passes make check and testwolfcrypt, and a true no-allocator build (-DWOLFSSL_NO_MALLOC -DNO_WOLFSSL_MEMORY) compiles with behaviour unchanged by design. Both predicates in 1 and 2 are unchanged for those two cases, so only WOLFSSL_NO_MALLOC builds that do have an allocator change behaviour.

Two failures deliberately left in place

unit.test reaches a structural limit rather than a sizing one: test_wc_ChaCha20Poly1305_Encrypt_Decrypt_Big() asks for a single 16384 byte buffer while LARGEST_MEM_BUCKET is 16128 for this feature set. A static pool is partitioned into fixed size buckets, so no pool size satisfies that request, and raising the ceiling changes the layout for every static memory user.

pem.test fails because examples/pem/pem.c never loads a pool at all, so its XMALLOC(..., NULL, ...) has no source in a build with no system heap.

Neither is a regression.

@Frauschi Frauschi self-assigned this Sep 10, 2026

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11432

Scan targets checked: none
Failed targets: wolfcrypt-src, wolfcrypt-bugs

⚠️ Review incomplete — one or more scan targets failed before findings could be produced. See the Fenrir PR review detail page for logs.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11432

⚠️ An internal error occurred during the automated review. This error has been logged. Please contact the Fenrir team if you need assistance.

Error: CalledProcessError

@Frauschi
Frauschi force-pushed the rsa-ca-pubkey-static-memory branch from 6c11cd5 to 5a03400 Compare September 11, 2026 07:48

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11432

Scan targets checked: wolfcrypt-src, wolfcrypt-bugs

Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread wolfcrypt/src/memory.c Outdated
Comment thread wolfcrypt/src/memory.c
@Frauschi
Frauschi force-pushed the rsa-ca-pubkey-static-memory branch from 5a03400 to 667312f Compare September 11, 2026 13:18

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11432

Scan targets checked: wolfcrypt-src, wolfcrypt-bugs

Findings: 2
2 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread wolfcrypt/src/memory.c Outdated
Comment thread wolfcrypt/src/memory.c
@Frauschi
Frauschi force-pushed the rsa-ca-pubkey-static-memory branch from 667312f to 8b7f6a8 Compare September 11, 2026 13:49

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #11432

Scan targets checked: wolfcrypt-src, wolfcrypt-bugs

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread wolfcrypt/src/memory.c
@Frauschi

Copy link
Copy Markdown
Member Author

Jenkins retest this please

@Frauschi Frauschi assigned wolfSSL-Bot and unassigned Frauschi Sep 11, 2026
@Frauschi Frauschi added the For This Release Release version 5.9.4 label Sep 13, 2026
ParseCert() guarded its RSA public-key copy with !WOLFSSL_NO_MALLOC,
while StoreKey() guards the equivalent copy for every non-RSA key with
!WC_ASN_NO_HEAP. Those are not the same condition: WC_ASN_NO_HEAP is
auto-defined only when WOLFSSL_NO_MALLOC and NO_WOLFSSL_MEMORY are set
without XMALLOC_USER or WOLFSSL_STATIC_MEMORY, so a static-memory build
defines WOLFSSL_NO_MALLOC yet still has a working allocator.

In such a build the copy was skipped, cert->pubKeyStored stayed 0, and
FillSigner() therefore never populated signer->publicKey/pubKeySize.
ParseCertRelative() then passed a NULL key and a zero key size to
ConfirmSignature(), which rejects them with BAD_FUNC_ARG before its
WOLFSSL_ENTER. The effect was that no certificate issued by an RSA CA
could be verified against it - wolfSSL_CertManagerVerifyBuffer() and TLS
peer validation alike - while ECC, Ed25519, Ed448 and ML-DSA CAs worked,
because those keys travel through StoreKey().

Use WC_ASN_NO_HEAP in all three guards, including the one on the ptr
declaration. FreeDecodedCert() and FreeSigner() already key off
pubKeyStored, so ownership and freeing are unchanged.
…uilds

With WOLFSSL_NO_MALLOC there is no allocator behind a NULL-heap XMALLOC,
so an allocation made outside any CTX or SSL object can only be served
from the global heap hint. Loading a static memory pool never set that
hint, and nothing else does either, so the hint stayed NULL for an
application that had in fact handed wolfSSL a pool.

wolfSSL_Init() makes such an allocation: under OPENSSL_EXTRA it seeds the
compatibility-layer RNG, whose _InitRng() call allocates with a NULL
heap. That returned NULL, wolfSSL_Init() reported WC_INIT_E, and every
wolfSSL_CTX_new_ex() that triggered the lazy init failed - so no TLS
connection could be established at all in a static-memory build, even
though the pool was large enough. The example client and server both hit
this, from their two different entry points: the server through
wolfSSL_CTX_load_static_memory() and the client through a direct
wc_LoadStaticMemory() call.

Set the hint in wc_LoadStaticMemory_ex(), which both paths funnel
through, and only when no hint has been set yet, so an application that
manages the global hint itself keeps control.

Adopt a pool only when it holds at least one general bucket.
wc_partition_static_memory() places every chunk of a WOLFMEM_IO_POOL or
WOLFMEM_IO_POOL_FIXED pool on the heap's io list and leaves the general
buckets empty, and it also reports success for a buffer with room for the
tracking structures but not for a single bucket. Adopting either one
produces a global allocator that fails every general request and then
blocks a usable pool loaded afterwards from taking the hint. Testing the
bucket lists rather than the flag covers both cases, and is also right
under WOLFSSL_STATIC_MEMORY_LEAN, where the IO flag is ignored and such a
pool does get general buckets. An earlier version of this commit claimed
an IO pool could not displace the general one; that only held when the
general pool happened to load first.

Clear the hint in wc_UnloadStaticMemory() when it refers to the heap
being unloaded. The hint lives inside the caller's pool buffer, which the
caller may reuse once the pool is gone, and the mutex it reaches through
has just been destroyed. This part is not restricted to
WOLFSSL_NO_MALLOC: an application setting the global hint by hand has
always been able to leave it dangling the same way.

scripts/resume.test and scripts/tls13.test go from failing to passing
with --enable-staticmemory -DWOLFSSL_NO_MALLOC. The remaining failures in
that configuration are unrelated: they are test programs that read certs
from files without ever loading a pool.
With WOLFSSL_NO_MALLOC there is no system heap to fall back on, so an
allocation the compatibility layer makes with a NULL heap is served from
the static pool the test loads rather than by malloc. gTestMemory was
sized for the wolfCrypt tests alone, so those extra allocations exhausted
it and openssl_test() failed - first in wolfSSL_CRYPTO_malloc(), then, as
the pool was enlarged, further along in
wolfSSL_X509_load_certificate_file().

Give that combination its own 1 MB pool. Every other configuration keeps
the size it had.

testsuite/testsuite.test now passes with --enable-staticmemory
-DWOLFSSL_NO_MALLOC. scripts/unit.test still fails there for an unrelated
reason: it asks for a single 16384 byte buffer while LARGEST_MEM_BUCKET
is 16128 in this configuration, which no pool size can satisfy.
@Frauschi
Frauschi force-pushed the rsa-ca-pubkey-static-memory branch from 8b7f6a8 to e364887 Compare September 14, 2026 06:26
@Frauschi

Copy link
Copy Markdown
Member Author

Jenkins retest this please

Comment thread wolfcrypt/src/memory.c

for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) {
if (heap->ava[i] != NULL) {
(void)wolfSSL_SetGlobalHeapHint(hint);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of the automatic setting and unsetting of the global heap hint. The setting of the global heap hint itself is not mutex protected so there could be obscure edge race cases here on which heap hint gets set. It's meant for an application to manage setting the global heap hint if wanted otherwise the build should fail on malloc when not set and no heap hint has been used.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like the WC_ASN_NO_HEAP fix in asn.c in this PR though!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For This Release Release version 5.9.4

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants