Posted to tcl by stevel at Wed Jul 29 01:38:55 GMT 2026view raw

  1. What the problem was
  2.  
  3. TclTLS freed an OpenSSL data structure at exit while TLS channels were still
  4. using it.
  5.  
  6. Concretely, in TlsLibInit() the extension registered a Tcl exit handler:
  7.  
  8. Tcl_CreateExitHandler(TlsLibShutdown, NULL);
  9.  
  10. and TlsLibShutdown() ’ BIO_cleanup() ’ BIO_meth_free(BioMethods). BioMethods
  11. is the custom BIO_METHOD  the vtable of function pointers (read, write, ctrl,
  12. destroy) that every TclTLS BIO points at to route OpenSSL's I/O through Tcl
  13. channels.
  14.  
  15. The fatal assumption was that exit handlers run last. They don't. In Tcl 9's
  16. generic/tclEvent.c, both exit paths run exit handlers first and close channels
  17. afterwards:
  18.  
  19. - Tcl_Exit() ’ InvokeExitHandlers() (line 981) ’ FinalizeThread() (996) ’
  20. TclFinalizeIOSubsystem() (1413)
  21. - Tcl_Finalize() ’ InvokeExitHandlers() (1201) ’ Tcl_FinalizeThread() (1223) ’
  22. same
  23.  
  24. So on exit:
  25.  
  26. 1. TlsLibShutdown frees the vtable.
  27. 2. TclFinalizeIOSubsystem then walks every channel still open and closes it.
  28. 3. Closing your live XMPP socket ran TlsClose2Proc ’ TlsCloseProc, which
  29. called into the BIO  and BIO_ctrl() dereferences b->method->ctrl to dispatch.
  30. That vtable was freed memory. Jump through a garbage pointer ’ segfault.
  31.  
  32. That's why it only happened on exit, and only with a connection still up:
  33. jabberlib's XMPP socket was still open, so there was a live BIO pointing at a
  34. dead vtable. Close everything first and there's nothing left to trip over.
  35. It's also heap-dependent  if the freed block hadn't been reused yet, the
  36. stale pointers still read as valid and the process exited cleanly, which is
  37. exactly why it looked intermittent.

Add a comment

Please note that this site uses the meta tags nofollow,noindex for all pages that contain comments.
Items are closed for new comments after 1 week