sniffer: copy handshake hashes with the wc_*Copy APIs - #11389
Conversation
- HashCopy() calls wc_ShaCopy(), wc_Md5Copy(), wc_Sha256Copy() and wc_Sha384Copy() under the existing hash guards, accumulating into a ret it now returns, in place of XMEMCPY over each context. - New HashFree() frees the wc_Sha, wc_Md5, wc_Sha256 and wc_Sha384 members of an HsHashes, and tolerates a NULL argument. It is forward declared above FreeSnifferSession(). - FreeSnifferSession(), ProcessServerHello(), the client_key_exchange case in DoHandShake(), and the HashInit() failure path in CreateSession() call HashFree() before XFREE'ing the wrapper. Issue: f-13332
There was a problem hiding this comment.
🟢 Approval recommended
The changes match existing internal patterns (e.g., InitHandshakeHashesAndCopy() in src/internal.c) and correctly address confirmed memory-ownership hazards without introducing new unchecked failure paths.
Pull request overview
This pull request fixes unsafe copying and teardown of handshake hash contexts in the TLS sniffer when Extended Master Secret (EMS) is negotiated, replacing raw struct XMEMCPY with the wc_*Copy() APIs and adding proper hash-context cleanup to prevent leaks and double-frees.
Changes:
- Replace
HashCopy()’s rawXMEMCPYof hash contexts withwc_ShaCopy(),wc_Md5Copy(),wc_Sha256Copy(), andwc_Sha384Copy()and propagate copy errors via a return code. - Add
HashFree()to properly release the sniffer’s privateHsHashesmembers, and call it on all session teardown paths that previously onlyXFREE’d the wrapper. - Preserve the existing post-copy
XMEMSETscrub while ensuring heap-owned hash state is freed correctly first.
File summaries
| File | Description |
|---|---|
src/sniffer.c |
Reworks EMS handshake-hash copy/free logic to use wc_*Copy() and wc_*Free() for correct ownership and teardown of hash contexts. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11389
Scan targets checked: wolfssl-bugs, wolfssl-src
Findings: 1
Required changes (1)
CreateSession error paths after session->hash is set still leak the hash contexts
File: src/sniffer.c:5619
Function: CreateSession
Category: Resource leaks
The PR adds HashFree() only to the HashInit() failure path. The three later bailouts in the same function (session->context == NULL at 5617, and the two wolfSSL_new() failures at 5624/5630) run after session->hash = newHash and XFREE the session without freeing the wrapper or its hash contexts, leaking the SMALL_STACK_CACHE W and HASH_KEEP msg allocations. With an empty ServerList, every client SYN leaks. Adjacent to known finding #13333, which this PR otherwise closes.
Related known finding #13333 (similar but distinct): Both omit cleanup of initialized hash contexts, but #13333 is in FreeSnifferSession and frees only the wrapper during teardown, while this finding is in CreateSession bailouts that free neither the wrapper nor its contexts. The faulting operations and error paths differ, and fixing #13333 alone does not cover these direct XFREE(session) returns.
Recommendation: Call HashFree(session->hash) and XFREE(session->hash, NULL, DYNAMIC_TYPE_HASHES) on all three bailouts, or route them through FreeSnifferSession().
Referenced code: src/sniffer.c:5619-5623 (5 lines)
This review was generated automatically by Fenrir. Reported findings require changes before merge.
- The GetSnifferServer() and two wolfSSL_new() failure paths in CreateSession() call FreeSnifferSession() in place of a direct XFREE() of the session. - The sslClient failure path drops its own wolfSSL_free() of session->sslServer. Issue: F-13333
Fenrir's latest completed scan found no issues; clearing the prior automated change request.
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #11389
Scan targets checked: wolfssl-bugs, wolfssl-src
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Problem
HashCopy()insrc/sniffer.ccopied handshake hash contexts with rawXMEMCPY. Hash contexts own heap state in several configurations — theWscratch buffer under
WOLFSSL_SMALL_STACK_CACHE, themsgbuffer underWOLFSSL_HASH_KEEP, plus async, crypto-callback and hardware contexts. Twodefects followed:
session->sslServer->hsHashesandsession->sslClient->hsHashes) come fromwolfSSL_new()→InitHandshakeHashes()and already own allocated state.XMEMCPYleakedboth and replaced the pointers with the source's, so one buffer was aliased
by two independently freed
WOLFSSLobjects — a double free once both arereleased.
HsHasheswrapper wasXMEMSETandXFREEd without everrunning
wc_*Free, discarding the pointers it owned.Reachable by any sniffer processing a TLS 1.2-or-below handshake that
negotiates extended master secret.
src/internal.calready documents this exact hazard forInitHandshakeHashesAndCopy(). The sniffer's privateHsHashesis adifferent struct and needed the same treatment applied locally.
Fix (
src/sniffer.c)HashCopy()now callswc_ShaCopy(),wc_Md5Copy(),wc_Sha256Copy()andwc_Sha384Copy(), accumulating into aretitreturns. These APIs free the destination internally and re-allocate
destination-owned state, so source and both destinations end up disjoint.
HashFree(), a new counterpart toHashInit(), releases the wrapper'shash members and tolerates a
NULLargument. It is called on everysession->hashteardown path, each of which previously ran a bareXFREE:FreeSnifferSession()ProcessServerHello()DoHandShake(),client_key_exchangeCreateSession()HashInit()failureThe
XMEMSETscrub at theclient_key_exchangesite is retained and now runsafter
HashFree():wc_ShaFree()andwc_Md5Free()do not zero theirstructs, so it still wipes the SHA-1 and MD5 transcript state.
Closes f-13332 and f-13333.
Verification
--enable-sniffer --enable-smallstackcache -DWOLFSSL_HASH_KEEPand with plain--enable-sniffer.scripts/sniffer-testsuite.testpasses in both configs;make check6passed, 5 skipped, 0 failed.
HashCopy()toXMEMCPYmakes ASan reporta heap use-after-free on the SHA-256
Wbuffer viaTransform_Sha256,confirming both the aliasing and that the captures reach the EMS path.
Test coverage
The existing TLS 1.2 captures do reach the changed code on the success path:
HashCopy()runs twice per session (server and client) and theclient_key_exchangeHashFree()runs, both confirmed under ASan. The TLS 1.3captures do not reach
HashCopy()at all.Three branches remain uncovered and are not addressed here:
HashCopy()nonzero returnwc_*Copy()failure; theelseinDoHandShake()was dead before this change and is now liveHashFree()inCreateSession()HashInit()failsHashFree()inProcessServerHello()The first two need allocation-failure injection the sniffer has no harness for;
the third needs a TLS 1.2 capture generated with EMS disabled on both peers.