Skip to content

Leave the shared stream BIO method allocated at exit - #109

Merged
quinnj-bot[bot] merged 1 commit into
mainfrom
claude/exit-bio-method
Oct 29, 2026
Merged

quinnj-bot[bot] merged 1 commit into
mainfrom
claude/exit-bio-method

Conversation

@quinnj-bot

@quinnj-bot quinnj-bot Bot commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

Every SSLStream's two BIOs use one BIO method, BIO_STREAM_METHOD, created in __init__. It had a finalizer (BIO_meth_free). Julia runs every finalizer at exit, also for live objects, while other threads keep running, so a stream created (BIO_new) or freed (SSL_free → BIO_free, which calls the method's destroy) on another thread after that finalizer ran used a freed method, and after free set the pointer to C_NULL, a NULL one: a segfault in BIO_new_ex at exit.

The method now has no finalizer and lives as long as the process, like OpenSSL's own static BIO_s_* methods. Only __init__ calls this constructor.

Verification

  • New testset StreamMethodOutlivesFinalizers: a subprocess runs the method's finalizer (as exit would), then makes a stream. On main it segfaults in BIO_new_ex (the test fails); with the fix it passes.
  • Stress: 3 threads create a stream on an existing socket and free its SSL object (what close does) in a loop while the main thread calls exit(0), -t 4. main: 12 of 20 runs crashed (SIGSEGV in BIO_new_ex); this branch: 0 of 20.
  • Full suite on Julia 1.12 -t 4: passes.

Self-review

  • Callers: BIOMethod(::String) is only called from __init__; the BIOMethod(::Ptr) constructor (OpenSSL's static methods) never had a finalizer. A caller that makes its own BIOMethod("name") now has to free it.
  • Exit: stream finalizers that run at exit (SSL_free → BIO_free → the method's destroy) now always see a live method. The method's memory is reclaimed by the process exit; OpenSSL's own atexit cleanup does not touch it.
  • No change to stream behavior otherwise: the same method and callbacks as before.

🤖 Generated with Claude Code

Every SSLStream's BIOs use one BIO method, created in `__init__`. It had a
finalizer, and Julia runs every finalizer at exit while other threads keep
running: a stream created or freed on another thread after that finalizer
ran called BIO_new/BIO_free with a freed (then NULL) method and crashed the
process (12 of 20 exits crashed in a stress run with three threads making
streams; 0 of 20 with this change). The method now lives as long as the
process, like OpenSSL's own static methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.05%. Comparing base (14a9842) to head (d1fa6de).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #109      +/-   ##
==========================================
- Coverage   83.07%   83.05%   -0.02%     
==========================================
  Files           2        2              
  Lines        1341     1340       -1     
==========================================
- Hits         1114     1113       -1     
  Misses        227      227              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@quinnj-bot
quinnj-bot Bot merged commit 772abd1 into main Oct 29, 2026
14 checks passed
@quinnj-bot
quinnj-bot Bot deleted the claude/exit-bio-method branch October 29, 2026 23:51
@quinnj-bot quinnj-bot Bot mentioned this pull request Oct 30, 2026
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.

1 participant