Release PSA crypto state in uninitNetSystem(), not per socket (fixes #612) - #613
Open
ClintonSarkar wants to merge 3 commits into
Open
ClintonSarkar wants to merge 3 commits into
ClintonSarkar wants to merge 3 commits into
Conversation
…lose Closing one SocketMbedTLS must not invalidate PSA crypto state (RNG, key slots) used by other live sockets in the same process. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…chinezone#612) SocketMbedTLS::close() called mbedtls_psa_crypto_free() for every socket. That function is process-global: it wipes all PSA key slots and the shared entropy/RNG state, so closing one TLS socket broke every other live TLS socket in the process. With several concurrent TLS WebSocket clients reconnecting on their own threads this showed up as PSA_ERROR_BAD_STATE failures and heap-corruption aborts. - close() no longer calls mbedtls_psa_crypto_free(). - uninitNetSystem() now calls mbedtls_psa_crypto_free() on all platforms when built with mbedTLS >= 3.6, mirroring the existing WSAStartup/WSACleanup pairing. It must be called only after all sockets are closed. - initMBedTLS() is guarded by a per-socket flag so the ctor + init() double call no longer re-initialises live mbedTLS contexts (which re-inits mutexes under MBEDTLS_THREADING_C). close() clears the flag so a socket can be re-initialised. - init() checks psa_crypto_init() and fails the connect with an error message instead of running TLS on uninitialised PSA state. Affected releases: v11.4.6, v12.0.0, v12.0.1 and master (introduced in machinezone#527, widened to mbedTLS 4.x in machinezone#579), only when built against mbedTLS >= 3.6. v11.4.5 and earlier are not affected. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…y non-empty - IXNetSystem.cpp: include <psa/crypto.h> only for mbedTLS >= 3.6, so builds against mbedTLS 2.x (no psa/ headers) still compile. - The PSA lifetime test compiles to a single passing placeholder case below mbedTLS 3.6 instead of an empty binary, which Catch2 reports as "no tests ran" (exit 2) and would fail ctest for a supported config. - Document in usage.md that uninitNetSystem() now also releases process-global TLS (PSA) state and must follow the last socket close. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ClintonSarkar
force-pushed
the
fix/psa-crypto-process-lifetime
branch
from
October 2, 2026 02:16
0f296d8 to
65528a1
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SocketMbedTLS::close()calledmbedtls_psa_crypto_free()for every socket. That call is process-global: it tears down all PSA key slots and the shared entropy/RNG state, so closing one TLS socket breaks every other live TLS socket in the process. A process with ~6 concurrent TLS WebSocket clients reconnecting on their own threads hitPSA_ERROR_BAD_STATEfailures and heap-corruption aborts. This PR moves the teardown intoix::uninitNetSystem(), as suggested in #612.What changed
close(): no longer callsmbedtls_psa_crypto_free().uninitNetSystem(): callsmbedtls_psa_crypto_free()on all platforms when built with mbedTLS >= 3.6, mirroring the existingWSAStartup/WSACleanuppair. Documented inIXNetSystem.h.initMBedTLS()guard: a per-socket_mbedtlsInitializedflag stops the ctor +init()double call from re-initialising live mbedTLS contexts (withMBEDTLS_THREADING_Cthat re-inits live mutexes).close()clears it so a socket can be re-initialised.psa_crypto_init()stays outside the guard (it is idempotent).psa_crypto_init()ininit(): a failed PSA init now fails the connect withPSA crypto init failed: <status>instead of running TLS on uninitialised PSA state.test/IXSocketMbedTLSPSATest.cpp([socket_mbedtls], built only withUSE_TLS+USE_MBED_TLSand mbedTLS >= 3.6). It probes PSA state viapsa_generate_randomand an HMAC key import (hashing works without PSA init, so it would not detect the bug), across another socket's close, and checks thatuninitNetSystem()followed bypsa_crypto_init()recovers.IXNetSystem.cppincludes<psa/crypto.h>only for mbedTLS >= 3.6, so builds against mbedTLS 2.x (nopsa/headers) are unaffected.cteststays green there.Affected versions
Builds against mbedTLS < 3.6 never compiled the call and are not affected.
Behaviour change
Applications that never call
ix::uninitNetSystem()now keep PSA state until process exit, which the OS reclaims anyway. Applications that do call it must do so after closing all sockets — the same contract asWSACleanupon Windows.Note on threading
Concurrent TLS from multiple threads also requires mbedTLS built with
MBEDTLS_THREADING_C, which the stock mbedTLS config leaves off. This is a documentation note only; this PR does not change it.Testing
Linux (g++ 13, Ninja), mbedTLS 3.6.5 built from source, configured with
-DUSE_TLS=ON -DUSE_MBED_TLS=ON -DUSE_ZLIB=OFF -DUSE_TEST=ON.Red — test commit only, on unfixed master (
IXSocketMbedTLSPSATest "[socket_mbedtls]"):(-137 is
PSA_ERROR_BAD_STATE; line 64 is the check right after another socket'sclose().)Green — with the fix:
Full
ctest: 17/18 passed.IXHttpServerTestfails onAccept-Encoding == "gzip"both with and without this change (my build hadUSE_ZLIB=OFF), so it is unrelated.The pre-3.6 branches (include guard and test placeholder) were syntax-checked by forcing the version condition off with
g++ -fsyntax-only, not built against a real older mbedTLS.🤖 Generated with Claude Code