当前位置: 首页 > news >正文

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

图片

- 智慧链接 思想协作 -

相关文章:

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

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

【CSS入门学习】Flex布局设置div水平、垂直分布与居中

水平平均分布 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>…...

9. 神经网络(一.神经元模型)

首先&#xff0c;先看一个简化的生物神经元结构&#xff1a; 生物神经元有多种类型&#xff0c;内部也有复杂的结构&#xff0c;但是可以把单个神经元简化为3部分组成&#xff1a; 树突&#xff1a;一个神经元往往有多个树突&#xff0c;用于接收传入的信息。轴突&#xff1a;…...

R 语言 | future 包,非阻塞的执行耗时脚本

目的&#xff1a;有一段代码&#xff0c;后面暂时用不到&#xff0c;但是又很耗时&#xff0c;占了当前R session&#xff0c;难道只能等半个小时&#xff0c;等到它结束才能画图&#xff1f; 可以使用R多线程&#xff0c;在支线进程中执行耗时任务&#xff0c;同时不阻塞当前…...

UE学习日志#12 Niagara特效大致了解(水文,主要是花时间读了读文档和文章)

1 核心组件&#xff08;官方文档阅读&#xff09; 一些介绍主要来自官方文档Niagara概述 1.1 Systems 官方文档中的描述&#xff1a; Niagara系统是一种容器&#xff0c;可以放入你要构建该效果的所有内容。在这个系统中&#xff0c;你可以搭建不同的构建块来实现总体效果。…...

【数据结构】_链表经典算法OJ:合并两个有序数组

目录 1. 题目描述及链接 2. 解题思路 3. 程序 3.1 第一版 3.2 第二版 1. 题目描述及链接 题目链接&#xff1a;21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。 新链表是通过拼接给…...

Mongodb副本集群为什么选择3个节点不选择4个节点

一、容错能力的定义 在副本集中&#xff0c;容错能力是指系统能够容忍多少个节点故障而仍然能够保持服务可用性的能力。这通常与选举机制中的多数投票原则密切相关。 二、三节点副本集的容错能力 在三节点的副本集中&#xff0c;通常有一个主节点和两个从节点。当主节点故障…...

基于 WEB 开发的手机销售管理系统设计与实现内容

标题:基于 WEB 开发的手机销售管理系统设计与实现 内容:1.摘要 摘要&#xff1a;随着智能手机的普及和电子商务的快速发展&#xff0c;手机销售行业面临着越来越多的挑战和机遇。为了提高销售效率和管理水平&#xff0c;本文设计并实现了一个基于 WEB 的手机销售管理系统。该系…...

LeetCode - Google 大模型校招10题 第1天 Attention 汇总 (3题)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/145368666 GroupQueryAttention(分组查询注意力机制) 和 KVCache(键值缓存) 是大语言模型中的常见架构&#xff0c;GroupQueryAttention 是注意力…...

Vue3 provide/inject用法总结

1. 基本概念 provide/inject 是 Vue3 中实现跨层级组件通信的方案&#xff0c;类似于 React 的 Context。它允许父组件向其所有子孙组件注入依赖&#xff0c;无论层级有多深。 1.1 基本语法 // 提供方&#xff08;父组件&#xff09; const value ref(hello) provide(key, …...

Linux——网络基础(1)

文章目录 目录 文章目录 前言 一、文件传输协议 应用层 传输层 网络层 数据链路层 数据接收与解封装 主机与网卡 数据传输过程示意 二、IP和MAC地址 定义与性质 地址格式 分配方式 作用范围 可见性与可获取性 生活例子 定义 用途 特点 联系 四、TCP和UDP协…...

【记录】日常|从零散记录到博客之星Top300的成长之路

文章目录 shandianchengzi 2024 年度盘点概述写作风格简介2024年的创作内容总结 shandianchengzi 2024 年度盘点 概述 2024年及2025年至今我创作了786即84篇文章&#xff0c;加上这篇就是85篇。 很荣幸这次居然能够入选博客之星Top300&#xff0c;这个排名在我之前的所有年份…...

【二分查找】力扣373. 查找和最小的 K 对数字

给定两个以 非递减顺序排列 的整数数组 nums1 和 nums2 , 以及一个整数 k 。 定义一对值 (u,v)&#xff0c;其中第一个元素来自 nums1&#xff0c;第二个元素来自 nums2 。 请找到和最小的 k 个数对 (u1,v1), (u2,v2) … (uk,vk) 。 示例 1: 输入: nums1 [1,7,11], nums2 …...

池化层Pooling Layer

1. 定义 池化是对特征图进行的一种压缩操作&#xff0c;通过在一个小的局部区域内进行汇总统计&#xff0c;用一个值来代表这个区域的特征信息&#xff0c;常用于卷积神经网络&#xff08;CNN&#xff09;中。 2. 作用 提取代表性信息的同时降低特征维度&#xff0c;具有平移…...

力扣算法题——11.盛最多水的容器

目录 &#x1f495;1.题目 &#x1f495;2.解析思路 本题思路总览 借助双指针探索规律 从规律到代码实现的转化 双指针的具体实现 代码整体流程 &#x1f495;3.代码实现 &#x1f495;4.完结 二十七步也能走完逆流河吗 &#x1f495;1.题目 &#x1f495;2.解析思路…...

自由学习记录(32)

文件里找到切换颜色空间 fgui中的 颜色空间是一种总体使用前的设定 颜色空间&#xff0c;和半透明混合产生的效果有差异&#xff0c;这种问题一般可以产生联系 动效就是在fgui里可以编辑好&#xff0c;然后在unity中也准备了对应的调用手段&#xff0c;可以详细的使用每一个具…...

VScode+Latex (Recipe terminated with fatal error: spawn xelatex ENOENT)

使用VSCode编辑出现Recipe terminated with fatal error: spawn xelatex ENOENT问题咋办&#xff1f; 很好解决&#xff0c;大概率的原因是因为latex没有添加到系统环境变量中&#xff0c;所有设置的编译工具没有办法找到才出现的这种情况。 解决方法&#xff1a; winR 然后输…...

「蓝桥杯题解」蜗牛(Java)

题目链接 这道题我感觉状态定义不太好想&#xff0c;需要一定的经验 import java.util.*; /*** 蜗牛* 状态定义&#xff1a;* dp[i][0]:到达(x[i],0)最小时间* dp[i][1]:到达 xi 上方的传送门最小时间*/public class Main {static Scanner in new Scanner(System.in);static f…...

PHP EOF (Heredoc) 详解

PHP EOF (Heredoc) 详解 PHP 中的 EOF(End Of File)是一种非常有用的语法特性,允许开发者创建多行字符串。它特别适合于创建格式化文本,如配置文件、HTML 模板等。本文将详细讲解 PHP EOF 的用法、优势以及注意事项。 什么是 EOF? EOF 是一种特殊的字符串定义方式,它允…...

pyautogui操控Acrobat DC pro万能PDF转Word,不丢任何PDF格式样式

为了将PDF转换脚本改为多进程异步处理&#xff0c;我们需要确保每个进程独立操作不同的Acrobat窗口。以下是实现步骤&#xff1a; 实现代码 import os import pyautogui import time import subprocess import pygetwindow as gw from multiprocessing import Pooldef conver…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...