Title: During OpenSSL callbacks, calling OpenSSL functions is unsafe · Issue #65035 · nodejs/node · GitHub
Open Graph Title: During OpenSSL callbacks, calling OpenSSL functions is unsafe · Issue #65035 · nodejs/node
X Title: During OpenSSL callbacks, calling OpenSSL functions is unsafe · Issue #65035 · nodejs/node
Description: Version v26.5.0 Platform Darwin rpolzer-macbookpro.roam.internal 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:18:58 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6000 arm64 Subsystem ssl_tls What steps will reproduce the bug? Cal...
Open Graph Description: Version v26.5.0 Platform Darwin rpolzer-macbookpro.roam.internal 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:18:58 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6000 arm64 Subsystem ssl_tl...
X Description: Version v26.5.0 Platform Darwin rpolzer-macbookpro.roam.internal 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:18:58 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6000 arm64 Subsystem ssl_tl...
Opengraph URL: https://github.com/nodejs/node/issues/65035
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"During OpenSSL callbacks, calling OpenSSL functions is unsafe","articleBody":"### Version\n\nv26.5.0\n\n### Platform\n\n```text\nDarwin rpolzer-macbookpro.roam.internal 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:18:58 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6000 arm64\n```\n\n### Subsystem\n\nssl_tls\n\n### What steps will reproduce the bug?\n\n- Calling `this._handle.destroySSL()` from within the `ALPNCallback`\n- Calling `client._handle.setServername('...')` from within the `on('keylog')` callback\n- Calling `this.write('...')` from within the `ALPNCallback`\n\n### How often does it reproduce? Is there a required condition?\n\nAll of these reproduce every time.\n\n### What is the expected behavior? Why is that the expected behavior?\n\nNo segfault, no abort, no deadlock - just JS exceptions.\n\n### What do you see instead?\n\n- Calling `this._handle.destroySSL()` from within the `ALPNCallback` causes segfault of NodeJS:\n\n```\n* thread #1, name = 'node-MainThread', queue = 'com.apple.main-thread', stop reason = breakpoint 1.1\n * frame #0: 0x0000000188addaf4 libsystem_malloc.dylib`malloc_error_break\n frame #1: 0x0000000188ab19ac libsystem_malloc.dylib`malloc_vreport + 744\n frame #2: 0x0000000188ab528c libsystem_malloc.dylib`malloc_report + 64\n frame #3: 0x0000000188aba648 libsystem_malloc.dylib`___BUG_IN_CLIENT_OF_LIBMALLOC_POINTER_BEING_FREED_WAS_NOT_ALLOCATED + 76\n frame #4: 0x0000000100806e20 libssl.3.dylib`tls_post_process_client_hello + 432\n frame #5: 0x00000001007f6398 libssl.3.dylib`state_machine + 1424\n frame #6: 0x00000001007e369c libssl.3.dylib`ssl3_read_bytes + 1788\n frame #7: 0x00000001007982d4 libssl.3.dylib`ssl3_read_internal + 136\n frame #8: 0x00000001007a34fc libssl.3.dylib`SSL_read + 28\n frame #9: 0x0000000104b017cc libnode.147.dylib`node::crypto::TLSWrap::ClearOut() + 324\n frame #10: 0x0000000104b00c4c libnode.147.dylib`node::crypto::TLSWrap::Cycle() + 52\n frame #11: 0x0000000104b03558 libnode.147.dylib`node::crypto::TLSWrap::OnStreamRead(long, uv_buf_t const&) + 124\n frame #12: 0x0000000104a7d40c libnode.147.dylib`node::LibuvStreamWrap::OnUvRead(long, uv_buf_t const*) + 780\n frame #13: 0x0000000104a7d810 libnode.147.dylib`node::LibuvStreamWrap::ReadStart()::$_1::__invoke(uv_stream_s*, long, uv_buf_t const*) + 88\n frame #14: 0x0000000100080660 libuv.1.dylib`uv__stream_io + 1000\n frame #15: 0x00000001000851b0 libuv.1.dylib`uv__io_poll + 1252\n frame #16: 0x0000000100076b64 libuv.1.dylib`uv_run + 276\n frame #17: 0x0000000104904fd0 libnode.147.dylib`node::SpinEventLoopInternal(node::Environment*) + 252\n frame #18: 0x00000001049dbdd0 libnode.147.dylib`node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) + 184\n frame #19: 0x00000001049dbb04 libnode.147.dylib`node::NodeMainInstance::Run() + 140\n frame #20: 0x000000010497cfac libnode.147.dylib`node::Start(int, char**) + 684\n frame #21: 0x00000001888f3e00 dyld`start + 6992\n```\n\n- Calling `client._handle.setServername('...')` from within the `on('keylog')` callback causes assertion failure of NodeJS:\n\n```\n # node[11780]: static void node::crypto::TLSWrap::SetServername(const FunctionCallbackInfo\u003cValue> &) at ../src/crypto/crypto_tls.cc:1381\n # Assertion failed: !wrap->started_\n\n----- Native stack trace -----\n\n 1: 0x105e96010 node::Assert(node::AssertionInfo const&) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 2: 0x105f27aec node::crypto::TLSWrap::SetServername(v8::FunctionCallbackInfo\u003cv8::Value> const&) (.cold.5) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 3: 0x104ec407c node::crypto::TLSWrap::SetServername(v8::FunctionCallbackInfo\u003cv8::Value> const&) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 4: 0x10579720c Builtins_CallApiCallbackGeneric [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 5: 0x105795424 Builtins_InterpreterEntryTrampoline [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 6: 0x1175013b0 \n 7: 0x105795424 Builtins_InterpreterEntryTrampoline [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 8: 0x105792788 Builtins_JSEntryTrampoline [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n 9: 0x105792478 Builtins_JSEntry [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n10: 0x105010884 v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n11: 0x1050102bc v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::DirectHandle\u003cv8::internal::Object>, v8::internal::DirectHandle\u003cv8::internal::Object>, v8::base::Vector\u003cv8::internal::DirectHandle\u003cv8::internal::Object> const>) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n12: 0x105f2e540 v8::Function::Call(v8::Isolate*, v8::Local\u003cv8::Context>, v8::Local\u003cv8::Value>, int, v8::Local\u003cv8::Value>*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n13: 0x104cc4310 node::InternalMakeCallback(node::Environment*, v8::Local\u003cv8::Object>, v8::Local\u003cv8::Object>, v8::Local\u003cv8::Function>, int, v8::Local\u003cv8::Value>*, node::async_context, v8::Local\u003cv8::Value>) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n14: 0x104cd2e6c node::AsyncWrap::MakeCallback(v8::Local\u003cv8::Function>, int, v8::Local\u003cv8::Value>*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n15: 0x105f24cd0 node::crypto::(anonymous namespace)::KeylogCallback(ssl_st const*, char const*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n16: 0x100941234 nss_keylog_int [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n17: 0x1009533ac tls13_change_cipher_state [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n18: 0x1009916cc tls_process_server_hello [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n19: 0x10098e468 state_machine [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n20: 0x10097b69c ssl3_read_bytes [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n21: 0x1009302d4 ssl3_read_internal [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n22: 0x10093b4fc SSL_read [/Users/rpolzer/homebrew/Cellar/openssl@3/3.6.3/lib/libssl.3.dylib]\n23: 0x104ec17cc node::crypto::TLSWrap::ClearOut() [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n24: 0x104ec0c4c node::crypto::TLSWrap::Cycle() [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n25: 0x104ec3558 node::crypto::TLSWrap::OnStreamRead(long, uv_buf_t const&) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n26: 0x104e3d40c node::LibuvStreamWrap::OnUvRead(long, uv_buf_t const*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n27: 0x104e3d810 node::LibuvStreamWrap::ReadStart()::$_1::__invoke(uv_stream_s*, long, uv_buf_t const*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n28: 0x100258660 uv__stream_io [/Users/rpolzer/homebrew/Cellar/libuv/1.52.1/lib/libuv.1.0.0.dylib]\n29: 0x10025d1b0 uv__io_poll [/Users/rpolzer/homebrew/Cellar/libuv/1.52.1/lib/libuv.1.0.0.dylib]\n30: 0x10024eb64 uv_run [/Users/rpolzer/homebrew/Cellar/libuv/1.52.1/lib/libuv.1.0.0.dylib]\n31: 0x104cc4fd0 node::SpinEventLoopInternal(node::Environment*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n32: 0x104d9bdd0 node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n33: 0x104d9bb04 node::NodeMainInstance::Run() [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n34: 0x104d3cfac node::Start(int, char**) [/Users/rpolzer/homebrew/Cellar/node/26.5.0/lib/libnode.147.dylib]\n35: 0x1888f3e00 start [/usr/lib/dyld]\n\n----- JavaScript stack trace -----\n\n1: /private/tmp/z.js:41:23\n2: emit (node:events:509:20)\n3: onkeylog (node:internal/tls/wrap:554:22)\n```\n\n- Calling `this.write('...')` from within the `ALPNCallback` causes deadlock (due to `SSL_write` within `SSL_do_handshake`), the SSL state machine seems to never finish, so the `tls.connect` callback never runs.\n\n### Additional information\n\nI can provide the exact .js files upon request.\n\nIt would help if `this._handle` were not accessible but hidden within a closure, but it would not be sufficient (as you see, the deadlock one doesn't even need this).\n\nWe plan to harden BoringSSL against issues like this by having reentrant or concurrent calls to `SSL *` functions (and maybe others) return a new `ERR_R_BAD_CONCURRENCY` which would safely prevent such issues; it would however force NodeJS to move to a different approach of providing these callbacks. In particular, NodeJS's own test suite assumes that you can call `write` from `onhandshakedone`, which, albeit convenient, breaks invariants within both OpenSSL and BoringSSL and just \"happens to work\" for now, but will be broken by the pending change.\n\nYou can look at our pending patch for BoringSSL here: https://boringssl-review.googlesource.com/c/boringssl/+/97087 - this issue was actually found by testing this patch against known code bases.\n\nPossible solutions include:\n\n- Set a flag while in an OpenSSL callback, and have further SSL methods check that flag and throw immediately.\n- Defer callbacks to the next frame where possible (primarily, where the callback's return value is not needed).\n\nSee also a related issue in CPython, found by the same audit: https://github.com/python/cpython/issues/143756","author":{"url":"https://github.com/divVerent","@type":"Person","name":"divVerent"},"datePublished":"2026-08-05T06:11:02Z","url":"https://github.com/nodejs/node/issues/65035"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:8edb84d1-cc3d-32f5-78b9-ec1327e63b82 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | E9A8:36779:59E955:79C200:6AED20FF |
| html-safe-nonce | c53243f1d2f9eff3fd1ebfb41c260110b69a475905dff0e35dc69dd7c0bb02bd |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJFOUE4OjM2Nzc5OjU5RTk1NTo3OUMyMDA6NkFFRDIwRkYiLCJ2aXNpdG9yX2lkIjoiMzcwNTI1MjM4ODA4NDE5NTU4MyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | cc5515f49d463c9231bee97608545865071635caaacd0c50eda3feddbc0a39fa |
| hovercard-subject-tag | issue:5067911050 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/65035/issue_layout |
| twitter:image | https://opengraph.githubassets.com/abcff32c0b3e454f0fa9cf44427e50eb1048451740251f94b75dc0c603bf4e43/nodejs/node/issues/65035 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/abcff32c0b3e454f0fa9cf44427e50eb1048451740251f94b75dc0c603bf4e43/nodejs/node/issues/65035 |
| og:image:alt | Version v26.5.0 Platform Darwin rpolzer-macbookpro.roam.internal 25.5.0 Darwin Kernel Version 25.5.0: Tue Jun 9 22:18:58 PDT 2026; root:xnu-12377.121.10~1/RELEASE_ARM64_T6000 arm64 Subsystem ssl_tl... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | divVerent |
| hostname | github.com |
| expected-hostname | github.com |
| None | e3e0f7f63e50c6fe879198a93a0a023a648ecc0a5e9bc3b5e033ac57172237e6 |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 707a24b346ac8a82878b9c084efaa38a997660cb |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width