Skip to content

Retain shared certificate and key buffers across CTX reloads - #12062

Open
ageprocpp wants to merge 2 commits into
wolfSSL:masterfrom
ageprocpp:fix/fenrir-16791
Open

ageprocpp wants to merge 2 commits into
wolfSSL:masterfrom
ageprocpp:fix/fenrir-16791

Conversation

@ageprocpp

@ageprocpp ageprocpp commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

Fixes Fenrir #16791.

When WOLFSSL_COPY_CERT or WOLFSSL_COPY_KEY is disabled, an SSL object borrows the certificate, chain, or key buffer from its CTX. Replacing one of those buffers on the CTX after wolfSSL_new() can leave the existing SSL with a dangling pointer.

#12021 introduced reference-counted DER sharing for configurations where copying was enabled. This PR extends the lifetime guarantee to configurations where copying is disabled:

  • The SSL retains the CTX certificate, chain, and key using ShareDer(). Where DER reference counting is unavailable, ShareDer() falls back to copying.
  • Existing SSL objects keep their original credentials when the CTX is updated. SSL objects created afterward use the updated credentials.
  • Alternative keys are retained independently, including when private-key blinding is enabled.
  • CTX-derived credentials that were previously borrowed remain available after automatic handshake cleanup, so wolfSSL_clear() can reuse the SSL. Explicit unloading still releases them.
  • OCSP request caching checks whether the SSL certificate matches the current CTX certificate before using the CTX cache.

Applications must synchronize credential updates on a CTX with SSL creation from that CTX. If the certificate and key are updated separately, applications must also prevent SSL creation between those updates.

Background

This PR builds on #12021 and retains the part that was not covered there: buffer lifetime when certificate or key copying is disabled.

The reference-counting approach was previously proposed in #11109. The callback restrictions in #11191 address a different case; the lifetime issue can also occur when a CTX is updated after wolfSSL_new() and before the SSL uses its credentials.

Copilot AI balanced review requested due to automatic review settings November 9, 2026 06:38

Copilot AI left a comment

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.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@github-actions

github-actions Bot commented Nov 9, 2026 •

Copy link
Copy Markdown

MemBrowse Memory Report · Explore what changed →

gcc-arm-cortex-m0plus

  • FLASH: .text +12 B (+0.0%, 71,207 B / 262,144 B, total: 27% used)

gcc-arm-cortex-m3

  • FLASH: .text +268 B (+0.2%, 132,497 B / 262,144 B, total: 51% used)

gcc-arm-cortex-m4

  • FLASH: .text +256 B (+0.1%, 209,804 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m4-dtls13

  • FLASH: .text +256 B (+0.1%, 196,760 B / 1,048,576 B, total: 19% used)

gcc-arm-cortex-m4-openssl-compat

  • FLASH: .text +64 B (+0.0%, 802,244 B / 1,048,576 B, total: 77% used)

gcc-arm-cortex-m4-pq

  • FLASH: .text +256 B (+0.1%, 313,680 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m4-rsa-only

  • FLASH: .text +256 B (+0.1%, 344,208 B / 1,048,576 B, total: 33% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .text +256 B (+0.2%, 133,265 B / 262,144 B, total: 51% used)

gcc-arm-cortex-m4-tls13

  • FLASH: .text +256 B (+0.1%, 250,402 B / 262,144 B, total: 96% used)

gcc-arm-cortex-m7

  • FLASH: .text +256 B (+0.1%, 209,804 B / 262,144 B, total: 80% used)

gcc-arm-cortex-m7-pq

  • FLASH: .text +256 B (+0.1%, 314,512 B / 1,048,576 B, total: 30% used)

gcc-arm-cortex-m7-tls13

  • FLASH: .text +256 B (+0.1%, 250,402 B / 262,144 B, total: 96% used)

No memory changes detected for:


Catch memory regressions in CI with MemBrowse. Free for open source.

@ageprocpp
ageprocpp force-pushed the fix/fenrir-16791 branch 4 times, most recently from 142c6f6 to 8e11d4f Compare November 9, 2026 08:54
@ageprocpp ageprocpp assigned ageprocpp and wolfSSL-Bot and unassigned ageprocpp Nov 9, 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 #12062

Scan targets checked: wolfcrypt-src, wolfcrypt-bugs, wolfcrypt-port-bugs, wolfssl-src, wolfssl-bugs
Not reviewed (no scan target covers them): doc/dox_comments/header_files/asn_public.h
Coverage: 10 of 12 in-scope changed file(s) opened by the reviewer; not opened: wolfssl/internal.h, wolfssl/wolfcrypt/asn.h

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.

Review tier: Max

@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 #12062

Scan targets checked: wolfssl-src, wolfssl-bugs
Unchanged since last review (not re-run): wolfcrypt-src, wolfcrypt-bugs, wolfcrypt-port-bugs
Not reviewed (no scan target covers them): doc/dox_comments/header_files/asn_public.h
Coverage: 2 of 3 in-scope changed file(s) opened by the reviewer; not opened: tests/api.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

@wolfSSL-Fenrir-bot
wolfSSL-Fenrir-bot dismissed their stale review November 10, 2026 08:19

Fenrir's latest completed scan found no issues; clearing the prior automated change request.

@ageprocpp ageprocpp assigned wolfSSL-Bot and unassigned ageprocpp Nov 10, 2026
@JacobBarthelmeh

Copy link
Copy Markdown
Contributor

@ageprocpp I believe this overlaps some with the older #12021 PR. Can you verify and pull out portions that do not or close this one please.

@ageprocpp
ageprocpp force-pushed the fix/fenrir-16791 branch 2 times, most recently from bb2d1e5 to 2a32313 Compare November 11, 2026 20:06
@ageprocpp

Copy link
Copy Markdown
Contributor Author

@JacobBarthelmeh
Thanks, you’re right that this overlapped with #12021. I’ve rebased this PR onto master and removed the overlapping changes.

#12021 uses reference-counted sharing when WOLFSSL_COPY_CERT or WOLFSSL_COPY_KEY is enabled. When copying is disabled, however, an SSL still borrows the CTX buffers, so replacing them while that SSL exists can leave it with dangling pointers and cause use-after-free. This is the core finding by Fenrir and it's still left for taking care of.

This PR extends the lifetime guarantee to those configurations. It reuses #12021’s reference counting where available and falls back to copying when reference counting is unavailable. It also handles alternative keys and preserves CTX-derived credentials across automatic handshake cleanup so that an SSL can be reused.

@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 #12062

Scan targets checked: wolfcrypt-src, wolfcrypt-bugs, wolfssl-src, wolfssl-bugs
Coverage: 6 of 10 in-scope changed file(s) opened by the reviewer; not opened: tests/api.c, tests/api/test_tls.c, wolfssl/internal.h, wolfssl/wolfcrypt/settings.h

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.

Review tier: Lite

Comment thread src/internal.c Outdated
{
#if !defined(WOLFSSL_COPY_CERT)
const DerBuffer* cert = ssl->buffers.certificate;
const DerBuffer* ctxCert = ssl->ctx != NULL ? ssl->ctx->certificate : NULL;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

OCSP cache eligibility check reads ctx-certificate without synchronization during handshakes · Race conditions

SslCanUseCtxOcspCache reads ssl->ctx->certificate mid-handshake with no lock. A concurrent CTX reload (ProcessBufferCertHandleDer FreeDer(&ctx->certificate)) causes a use-after-free in XMEMCMP in copy builds, or stapling a cached OCSP request for the new CTX cert. Related known finding F-8917 (similar but distinct): that finding covers SSLs borrowing CTX buffers; this new code dereferences the CTX buffer itself.

Suggested fix: Fixing this needs a design decision: tie the CTX OCSP cache entry to a specific CTX certificate (for example under ctx->ocspLock) rather than reading ctx->certificate unlocked.

Related known findings (similar but distinct; listed for context, not part of this finding)

  • F-15606 (open): File/function: the candidate is SslCanUseCtxOcspCache, while F-15606 is DefTicketEncCb. Both are in src/internal.c but they are different functions, so this does not match. Faulting operation: the candidate reads ssl->ctx->certificate and dereferences it (XMEMCMP) while a CTX reload frees it, a use-after-free or stale-cert race. F-15606 reads the expirary[0] sentinel before taking the mutex and then consumes ticket key material, an initialization-publication race. Root cause: both read shared stat

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants