OpenSSL BIO阻塞读过程中跨线程调用close是否线程安全
OpenSSL BIO跨线程阻塞读与并发关闭的线程安全结论
首先明确OpenSSL线程安全规则的核心判定标准:
所有OpenSSL实例对象(包括SSL连接、绑定连接的BIO对象)都不支持两个及以上线程并发调用操作接口,官方FAQ提到的「单SSL连接不可多线程并发使用」就是这个规则的典型场景,没有例外。
针对提到的两个未明确的场景,实际结论和注意事项如下:
- 单线程内对BIO发起阻塞式
BIO_read调用本身是线程安全的:只要该BIO实例在阻塞读挂起的整个周期内,没有其他线程对它调用任何OpenSSL/BIO相关接口,就不会触发竞态问题。 - 阻塞读挂起状态下,其他线程直接对同一个BIO调用
BIO_close/BIO_free类关闭操作属于明确的非线程安全行为,会触发未定义后果。
阻塞读执行过程中,BIO内部状态、绑定的SSL上下文读状态正被读线程占用,跨线程直接关闭会同时修改底层IO句柄状态、释放上下文内存,轻则导致读线程返回乱码错误、触发非法内存访问,重则直接导致进程coredump。OpenSSL 1.0.x、1.1.x、3.x全版本线都没有对这种跨线程关闭操作做安全承诺,不同版本只是崩溃概率、报错表现有差异,不存在“某版本可以这么用”的情况。
要实现“无响应时终止阻塞BIO读、退出应用”的需求,正确的操作流程是:
- 不要直接跨线程操作BIO对象,先通过
BIO_get_fd拿到BIO绑定的原生socket句柄 - 在触发终止的线程中,对拿到的原生socket调用
shutdown(fd, SHUT_RDWR)(Windows平台调用closesocket),主动触发IO句柄异常,让阻塞在BIO_read的线程立刻收到错误返回 - 等原读线程退出
BIO_read调用、完成上下文状态校验后,再在原读线程,或者通过全局同步确保没有任何线程操作该BIO的时机,调用BIO_close/BIO_free完成资源释放
- 注意:不要试图通过给BIO外加自定义全局锁规避这个问题——阻塞读持锁后,关闭线程会因为拿不到锁一直阻塞,根本达不到终止阻塞调用的目的,本质上必须先通过底层IO事件唤醒阻塞的读线程,再走正常的资源释放流程。
内容的提问来源于stack exchange,提问作者Кирилл Волков
相关产品推荐
相关产品推荐

