从崩溃难题看 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一起,创造属于开发者的多彩世界。

图片

- 智慧链接 思想协作 -

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SQL注入漏洞之高阶手法 宽字节注入以及编码解释 以及堆叠注入原理说明

目录 宽字节注入 编码区分 原理 函数 转译符号解释 注意 绕过方式详解 堆叠【Stack】注入攻击 注入语句 宽字节注入 在说宽字节注入之前 我们需要知道编码相关的知识点,这个有助于搞定什么是宽字节注入 分清楚是ascii码是什么宽字节注入代码里面加入了adds…

DeepSeek r1本地安装全指南

环境基本要求 硬件配置 需要本地跑模型,兼顾质量、性能、速度以及满足日常开发需要,我们需要准备以下硬件: CPU:I9内存:128GB硬盘:3-4TB 最新SSD,C盘确保有400GB,其它都可划成D盘…

最新版仿天涯论坛系统源码带后台

亲测正常使用版,代码精简,压缩包也小,程序运行速度更快,效率更高,服务器抗攻击能力更强 功能方面: 仿天涯论坛模板的免费论坛系统在功能方面也很强大!程序本身包含一个PC版网站和一个手机版网站 支持打包…

PostgreSQL 数据备份与恢复:掌握 pg_dump 和 pg_restore 的最佳实践

title: PostgreSQL 数据备份与恢复:掌握 pg_dump 和 pg_restore 的最佳实践 date: 2025/1/28 updated: 2025/1/28 author: cmdragon excerpt: 在数据库管理中,备份与恢复是确保数据安全和业务连续性的关键措施。PostgreSQL 提供了一系列工具,以便于数据库管理员对数据进行…

C++ 写一个简单的加减法计算器

************* C topic:结构 ************* Structure is a very intersting issue. I really dont like concepts as it is boring. I would like to cases instead. If I want to learn something, donot hesitate to make shits. Like building a house. Wh…

我的2024年博客总结(在工作、博客和生活中找到自己的生活节奏)

文章目录 ⭐前言⭐工作和博客的关联⭐找到自己的生活节奏⭐结束 ⭐前言 大家好,我是yma16,本文主要写2024年博客总结,关于在工作、博客和生活中找到自己的生活节奏。 node系列往期文章 node_windows环境变量配置 node_npm发布包 linux_配置…

【视频+图文详解】HTML基础1-html和css介绍、上网原理

图文详解 html介绍 概念:html是超文本标记语言的缩写,其英文全称为HyperText Markup Language,是用来搭建网站结构的语言,比如网页上的文字,按钮,图片,视频等。html的版本分为1.0、2.0、3.0、…

VT:优化LLM推理过程的记忆与探索

📖标题:LLMs Can Plan Only If We Tell Them 🌐来源:arXiv, 2501.13545 🌟摘要 🔸大型语言模型(LLM)在自然语言处理和推理方面表现出了显著的能力,但它们在自主规划方面…

C++并发编程指南07

文章目录 [TOC]5.1 内存模型5.1.1 对象和内存位置图5.1 分解一个 struct,展示不同对象的内存位置 5.1.2 对象、内存位置和并发5.1.3 修改顺序示例代码 5.2 原子操作和原子类型5.2.1 标准原子类型标准库中的原子类型特殊的原子类型备选名称内存顺序参数 5.2.2 std::a…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…

AJAX综合案例——图书管理

黑马程序员视频地址: AJAX-Day02-10.案例_图书管理AJAX-Day02-10.案例_图书管理_总结_V1.0是黑马程序员前端AJAX入门到实战全套教程,包含学前端框架必会的(ajaxnode.jswebpackgit),一套全覆盖的第25集视频&#xff0c…

Linux_线程同步生产者消费者模型

同步的相关概念 同步:在保证数据安全的前提下,让线程能够按照某种特定的顺序访问临界资源,从而有效避免饥饿问题,叫做同步竞态条件:因为时序问题,而导致程序异常,我们称之为竞态条件。 同步的…

Qt u盘自动升级软件

Qt u盘自动升级软件 Chapter1 Qt u盘自动升级软件u盘自动升级软件思路:step1. 获取U盘 判断U盘名字是否正确, 升级文件是否存在。step2. 升级step3. 升级界面 Chapter2 Qt 嵌入式设备应用程序,通过U盘升级的一种思路Chapter3 在开发板上运行的…

拦截器快速入门及详解

拦截器Interceptor 快速入门 什么是拦截器? 是一种动态拦截方法调用的机制,类似于过滤器。 拦截器是Spring框架中提供的,用来动态拦截控制器方法的执行。 拦截器的作用:拦截请求,在指定方法调用前后,根…

信息安全专业优秀毕业设计选题汇总:热点选题

目录 前言 毕设选题 开题指导建议 更多精选选题 选题帮助 最后 前言 大家好,这里是海浪学长毕设专题! 大四是整个大学期间最忙碌的时光,一边要忙着准备考研、考公、考教资或者实习为毕业后面临的升学就业做准备,一边要为毕业设计耗费大量精力。学长给大家整理…

Linux中使用unzip

安装命令 yum install unzip unzip常用选项和参数 选项 说明 -q 隐藏解压过程中的消息输出 -d /path/to/directory 指定解压文件的目标目录 -P password 如果.zip文件被密码保护,使用此选项可以指定打开文件所需的密码 解压命令 unzip 要解压的压缩包unz…

ThreadLocal源码解析

文章目录 一、概述二、get()方法三、set()方法四、可能导致的内存泄漏问题五、remove六、思考:为什么要将ThreadLocalMap的value设置为强引用? 一、概述 ThreadLocal是线程私有的,独立初始化的变量副本。存放在和线程进行绑定的ThreadLocalMa…

批量解密,再也没有任何限制了

有的时候我们在网上下载了PDF文档。发现没有办法进行任何的操作,就连打印权限都没有。今天给大家介绍的这个软件可以一键帮你进行PDF解密,非常方便,完全免费。 PDF智能助手 批量解密PDF文件 这个软件不是很大,只有10MB&#xff…

《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》

文章目录 Langchain的定义Langchain的组成三个核心组件实现整个核心组成部分 为什么要使用LangchainLangchain的底层原理Langchain实战操作LangSmithLangChain调用LLM安装openAI库-国内镜像源代码运行结果小结 使用Langchain的提示模板部署Langchain程序安装langserve代码请求格…

车载软件 --- 大一新生入门汽车零部件嵌入式开发

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 简单,单纯,喜欢独处,独来独往,不易合同频过着接地气的生活…