从崩溃难题看 C 标准库与 Rust:线程安全问题引发的深度思考

在软件开发的世界里,每一次技术的变革和尝试都伴随着未知的挑战。EdgeDB 团队在将部分网络 I/O 代码从 Python 迁移到 Rust 的过程中,就遭遇了一场棘手的问题,这个问题不仅暴露了 C 标准库的线程安全隐患,也让我们对 Rust 的 “安全特性” 有了新的认识。

EdgeDB 正在为产品开发一个新的 HTTP 获取功能,选用reqwest作为 HTTP 客户端库。起初,一切进展顺利。在本地开发环境中,功能正常运行,在 x86_64 架构的 CI(持续集成)运行器上,各项测试也顺利通过,看起来十分稳定。然而,当测试在 ARM64 架构的 CI 运行器上进行时,奇怪的事情发生了。

测试开始间歇性失败,测试运行器启动后,会无限期地挂起,然后 CI 任务超时。从日志中看不到任何错误信息,只显示某个测试一直在运行。几个小时后,任务最终以超时错误结束。最初,团队以为是死锁问题,毕竟测试进程毫无响应的表现很符合死锁的特征。但深入调查后发现,事情远没有这么简单。

为了找出问题所在,团队成员决定直接连接到 ARM64 运行器一探究竟。由于 CI 机器运行在亚马逊 AWS 上,这使得他们可以获得真实的、非容器化的 root 用户权限,方便查看系统日志。一番查找后发现,测试进程并非死锁,而是直接崩溃了。只不过测试运行器没有检测到这一情况,这也成为了后续需要解决的另一个问题。

通过journalctl命令,团队找到了进程的核心转储文件,将其加载到gdb调试工具中。但一开始,由于缺少相关文件,调试过程遇到了诸多错误。经过一番操作,将相关库文件从容器中复制出来并配置好gdb后,终于得到了有用的回溯信息。令人意外的是,崩溃并非发生在新开发的 HTTP 代码中,而是出现在getenv函数里。

getenv函数用于获取环境变量的值,从回溯信息来看,它在扫描感兴趣的环境变量时,尝试从一个无效的内存位置加载数据,从而导致崩溃。但这就引发了新的疑问:为什么会出现这种情况呢?毕竟,环境变量看起来是完全有效的。

在深入调查过程中,团队成员 Yury 提供了关键线索。他指出,可能是文件 I/O 相关操作出错,Python 试图根据errno构建异常,这一过程调用了gettext,进而触发了getenv。而getenv在多线程环境中并非安全函数,这很可能就是问题的根源。

为了验证这一猜测,团队开始检查环境块。environ是 POSIX 标准定义的一个char **类型的变量,本质上是一个指向环境字符串的指针列表,列表末尾用NULL指针标记。通过gdb查看,环境块看起来并没有明显异常。但进一步分析发现,getenv函数中用于遍历environ数组的指针x20,其值与environ实际地址相差近 60MB。对比两个内存区域的指针值,发现它们在SSL_CERT_FILESSL_CERT_DIR这两个环境变量处开始出现差异。这强烈暗示了存在竞态条件,另一个线程在调用setenv时修改了environ

setenv用于设置环境变量,在多线程环境中调用它存在风险。当环境块的内存空间不足时,setenv可能会调用realloc重新分配内存,而此时如果另一个线程正在调用getenv,就可能导致数据不一致,引发崩溃。

那么,是哪段代码在调用setenv呢?经过一番谷歌搜索,团队发现问题可能出在openssl-probe上。openssl-probe会设置SSL_CERT_FILESSL_CERT_DIR这两个环境变量,而 EdgeDB 在 Linux 上使用的rust-native-tlsopenssl后端会调用这些函数。查看openssl-probe库的代码可以发现,它在设置环境变量时,没有考虑到多线程环境下的安全性。

问题找到了,那如何解决呢?EdgeDB 团队最终决定在 Linux 上放弃使用reqwestrust-native-tls/openssl后端,转而采用rustls。虽然最初选择rust-native-tls是为了避免在将 Python 代码移植到 Rust 时同时引入两个 TLS 引擎,但考虑到当前的线程安全问题,短期内使用两个引擎也成为了无奈之举。

此外,还有另一种解决思路,即在调用try_init_ssl_cert_env_vars时,持有 Python 的全局解释器锁(GIL)。Rust 本身有内部锁来防止 Rust 代码在读写环境变量时出现竞态条件,但无法阻止其他语言的代码直接使用libc。持有 GIL 可以避免与 Python 线程产生竞争。

值得一提的是,Rust 项目已经意识到了这一问题,并计划在 2024 版中将环境设置函数标记为不安全。而 glibc 项目也在近期对getenv函数进行了改进,通过避免realloc和泄漏旧环境,增加了其线程安全性。

这次事件为开发者们敲响了警钟。在多线程编程中,即使使用了像 Rust 这样强调安全性的语言,也不能忽视底层 C 标准库带来的风险。C 标准库中的一些函数,如setenvgetenv,在多线程环境下的不安全性可能会引发难以排查的问题。

对于 Rust 开发者来说,虽然 Rust 提供了强大的安全机制,但在与其他语言交互或使用底层库时,仍需谨慎对待。特别是在涉及到多线程操作时,要充分考虑不同语言和库之间的兼容性和线程安全性。

在实际开发中,我们往往会依赖各种库来实现功能,但这些库可能隐藏着潜在的风险。就像这次 EdgeDB 遇到的问题,openssl-probe看似无害的代码,却在多线程环境下引发了严重的崩溃。因此,在选择和使用库时,开发者需要深入了解其内部实现,评估潜在风险,尤其是在关键业务场景中,更要确保代码的稳定性和安全性。

同时,这也反映出跨语言开发的复杂性。不同语言有不同的特性和规范,在混合使用时,需要特别注意边界情况和交互细节。在 EdgeDB 的案例中,Rust 代码与 Python 代码以及底层 C 标准库之间的交互出现了问题,导致了崩溃。这提醒我们,在跨语言开发项目中,要建立完善的测试机制,覆盖各种可能的情况,及时发现并解决潜在问题。

从更广泛的角度看,这次事件也为整个软件开发社区提供了宝贵的经验教训。无论是语言开发者还是库开发者,都应该更加重视多线程环境下的安全性问题。语言标准的制定者可以考虑进一步完善标准库的设计,提供更安全的接口;库开发者在编写代码时,要充分考虑多线程场景,确保库的线程安全性,减少类似问题的发生。

在软件开发的道路上,每一次遇到的问题都是一次成长的机会。EdgeDB 团队通过这次经历,不仅解决了当前的技术难题,也为未来的开发积累了宝贵的经验。希望其他开发者能够从这个案例中汲取教训,在开发过程中更加注重细节,避免陷入类似的困境。你在开发中遇到过哪些因库的不安全性导致的问题呢?欢迎在评论区分享你的经验和看法。

科技脉搏,每日跳动。

与敖行客 Allthinker一起,创造属于开发者的多彩世界。

图片

- 智慧链接 思想协作 -

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/6732.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

班迪录屏:一款好用的屏幕录制软件

Bandicam(班迪录屏)是一款4K超清屏幕录像软件,最新版Bandicam v8.1.0.2516 绿色正式版已经集成授权信息,用户无需联网验证授权,启动软件即可直接使用已授权版本,享受良好的录制体验。这款软件完全摆脱了试用…

2025年国产化推进.NET跨平台应用框架推荐

2025年国产化推进.NET跨平台应用框架推荐 1. .NET MAUI NET MAUI是一个开源、免费(MIT License)的跨平台框架(支持Android、iOS、macOS 和 Windows多平台运行),是 Xamarin.Forms 的进化版,从移动场景扩展到…

PBFT算法

在我的博客中对于RAFT算法也有详细的介绍,raft算法包含三种角色,分别是:跟随者( follower ),候选人(candidate )和领导者( leader )。集群中的一个节点在某一…

用Python和Tkinter标准模块建立密码管理器

用Python和Tkinter标准模块建立密码管理器 创建一个简单的密码管理器应用程序,帮助用户存储和管理他们的密码。使用Python的tkinter模块来创建一个图形用户界面(GUI)。 本程序支持 添加、查看、搜索、复制、修改、删除 功能。 本程序使用 …

OpenAI模块重构

文章目录 1.common-openai-starter1.目录结构2.OpenAiProperties.java 新增apiUrl3.OpenAIAutoConfiguration.java4.OpenAiClient.java 使用gson重构 2.common-openai-starter-demo1.目录结构2.application.yml 新增api-url3.OpenAiController.java4.OpenAiApplication.java5.测…

数据标注开源框架 Label Studio

数据标注开源框架 Label Studio Label Studio 是一个开源的、灵活的数据标注平台,旨在帮助开发者和数据科学家轻松创建高质量的训练数据集。它支持多种类型的数据(如文本、图像、音频、视频等)以及复杂的标注任务(如分类、命名实体…

详解:TCP/IP五层(四层)协议模型

一.五层(四层)模型 1.概念 TCP/IP协议模型分为五层:物理层、数据链路层、网络层、传输层和应用层。这五层每一层都依赖于其下一层给它提供的网络去实现需求。 1)物理层:这是最基本的一层,也是最接近硬件…

使用Python进行大模型的测试与部署

随着人工智能技术的飞速发展,大规模模型在各行各业的应用日益广泛。然而,如何有效测试这些模型以确保其稳定性和准确性,成为测试人员的们面临的一大挑战。本文将详细介绍在Python环境下,如何测试大模型,并探讨其部署策…

高并发处理 --- 超卖问题+一人一单解决方案

在高并发场景下,超卖和一人一单是两个典型的并发问题。为了解决这两个问题,我们可以使用乐观锁(CAS)和悲观锁,这两者分别有不同的实现方式和适用场景。下面我们详细介绍如何通过 乐观锁(CAS) 和…

【2024年华为OD机试】(C卷,100分)- 约瑟夫问题 (JavaScriptJava PythonC/C++)

一、问题描述 题目描述 输入一个由随机数组成的数列(数列中每个数均是大于 0 的整数,长度已知),和初始计数值 m。 从数列首位置开始计数,计数到 m 后,将数列该位置数值替换计数值 m,并将数列…

浅谈APP之历史股票通过echarts绘图

浅谈APP之历史股票通过echarts绘图 需求描述 今天我们需要做一个简单的历史股票收盘价格通过echarts进行绘图,效果如下: 业务实现 代码框架 代码框架如下: . 依赖包下载 我们通过网站下载自己需要的涉及的图标,勾选之后进…

【0x0012】HCI_Delete_Stored_Link_Key命令详解

目录 一、命令参数 二、命令格式及参数 2.1. HCI_Delete_Stored_Link_Key 命令格式 2.2. BD_ADDR 2.3. Delete_All 三、生成事件及参数 3.1. HCI_Command_Complete事件 3.2. Status 3.3. Num_Keys_Deleted 四、命令执行流程 4.1. 命令发送阶段 4.2. 控制器处理阶段…

提示词的艺术 ---- AI Prompt 进阶(提示词框架)

提示词的艺术 ---- AI Prompt 进阶(提示词框架) 写在前面 上周发布了一篇《提示词的艺术----AI Prompt撰写指南》,旨在帮助读者理解提示词的作用,以及简单的提示词撰写指南。本篇作为进阶内容,将给出常用的提示词框架…

javaSE.类的继承

在定义不同类的时候,为了方便使用可以将这些共同属性抽象成一个父类,在定义其他子类时可以继承自该父类,减少代码的重复定义,子类可以使用父类中非私有成员. extents 没有可用的无形参构造方法 被构造方法覆盖了 super 需要调用父类的构造方法 super必须是构造主体的第一条语…

统计文本文件中单词频率的 Swift 与 Bash 实现详解

网罗开发 (小红书、快手、视频号同名) 大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等…

qt QUrl详解

1、概述 QUrl是Qt框架中用于处理URL(统一资源定位符)的类,它提供了构建、解析、编码、解码和处理URL的功能。QUrl支持多种协议,如HTTP、HTTPS、FTP以及文件URL等,并能处理URL的各个组成部分,如协议、主机、…

c++----------------------多态

1.多态 1.1多态的概念 多态(polymorphism)的概念:通俗来说,就是多种形态。多态分为编译时多态(静态多态)和运⾏时多 态(动态多态),这⾥我们重点讲运⾏时多态,编译时多态(静态多态)和运⾏时多态(动态多态)。编译时 多态(静态多态)…

javaSE.类与对象

类与对象 人类,鸟类,鱼类... 例如人,具有不同性格,但根本上都是人。 对象是某一类事物实际存在的每个个体(实例)例如:雷军 A:谁拿走了我的手机? B:是个人(类&#xff0…

Windows cmd常用命令

文章目录 Windows cmd常用命令一、引言二、文件和目录操作1、查看和切换目录2、文件和目录的创建与删除 三、系统信息与网络配置1、系统信息2、网络配置 四、使用示例五、总结 Windows cmd常用命令 一、引言 Windows 命令提示符(cmd)是一个强大的工具&a…

保健食品注册数据库<一键查询保健食品信息>

在保健品市场竞争激烈的情况下,企业要如何保障产品合规、信息公开,并且能够迅速应对市场变化呢?查询保健食品注册信息是关键环节。 当下,查询保健食品注册信息主要有两种途径:一是利用国家保健食品注册数据库进行查询…