iOS15下多线程并行发起网络请求时Curl ossl_connect_common函数崩溃
崩溃基础信息
调用栈
0 libsystem_pthread.dylib 0x5948 pthread_rwlock_rdlock + 110 1 XYZ 0x259ac08 CRYPTO_THREAD_read_lock + 10241200 2 XYZ 0x251a184 int_err_get_item + 9714220 3 XYZ 0x2519f0c ERR_lib_error_string + 9713588 4 XYZ 0x2519cd4 ERR_error_string_n + 9713020 5 XYZ 0x260bbfc ossl_strerror + 10704036 6 XYZ 0x260c4e4 ossl_connect_common + 10706316 7 XYZ 0x2610538 Curl_ssl_connect_nonblocking + 10722784 8 XYZ 0x25e2df8 https_connecting + 10536608 9 XYZ 0x25e2d90 Curl_http_connect + 10536504 10 XYZ 0x25f0990 multi_runsingle + 10592824 11 XYZ 0x25f049c curl_multi_perform + 10591556 12 XYZ 0x25d8d28 curl_easy_perform + 10495440
环境信息
- 操作系统版本:iOS 15
- Curl版本:7.76.1
- OpenSSL版本:OpenSSL 1.1.1k 25 Mar 2021
问题场景
多线程中并行发起get/post/multipart等多种类型的网络请求,日常运行正常,但设备进入后台一段时间后就会触发崩溃,崩溃点位于ossl_connect_common函数内。
根因分析
- OpenSSL全局错误队列锁状态损坏:崩溃栈顶明确指向
pthread_rwlock_rdlock调用失败,对应OpenSSL访问全局错误队列的读锁。iOS进入后台后,系统会暂停非活跃线程的调度,若此时线程持有OpenSSL的全局读写锁,超过后台存活阈值后系统会强制回收线程上下文,导致锁的内部状态被异常篡改,后续其他线程尝试获取读锁时直接触发崩溃。 - 依赖版本兼容性缺陷:当前使用的Curl 7.76.1和OpenSSL 1.1.1k存在已知的多线程后台场景兼容性问题:ossl_connect_common函数内获取错误信息时未做线程隔离,多线程并发请求时会同时竞争错误队列的全局锁,后台场景下锁竞争的超时概率大幅提升,最终触发崩溃。
- iOS15后台网络限制触发异常回调:iOS15对后台网络请求的限制更严格,后台运行超过30秒的未完成请求会被系统强制中断,中断回调触发时OpenSSL正持有错误队列的读锁,中断上下文异常释放锁后,其他线程访问锁时直接触发非法访问崩溃。
解决方案
- 优先升级依赖:将Curl升级至7.79.1及以上版本,OpenSSL升级至1.1.1n及以上版本,两个版本均已修复后台场景下的锁状态异常问题,同时修复了ossl_connect_common函数内的错误队列访问竞态问题。
- 管控后台网络请求:APP触发进入后台回调时,调用
curl_multi_pause暂停所有未完成的网络请求,禁止后台发起新的非必要请求,仅允许申请了后台任务权限的请求继续执行,APP回到前台后再恢复所有请求调度。 - 做线程上下文隔离:每个线程使用独立的Curl easy handle,禁止多线程共享同一个SSL_CTX或者Curl handle,每次请求完成后主动调用
ERR_clear_error清空当前线程的OpenSSL错误队列,降低全局锁的竞争概率。 - 手动补充锁校验:如果暂时无法升级依赖,可自定义OpenSSL的线程锁回调,调用读写锁接口前先校验锁对象的有效性,避免访问已经被系统回收的锁对象。
内容的提问来源于stack exchange,提问作者Saurabh B
相关产品推荐
相关产品推荐

