一条 CPU 原子指令,一次打包死循环:LA664 丢失更新事件始末

一条 CPU 原子指令,一次打包死循环:LA664 丢失更新事件始末

💡 原文英文,约3200词,阅读约需12分钟。
📝

内容提要

2026年,Debian LoongArch 移植维护者在打包 normaliz 时发现测试陷入死循环,根源为 LA664 核心的原子加指令在特定条件下丢失更新,构成新的 CPU 勘误。经 AI 辅助,两天内稳定复现:不同物理核上无数据屏障的原子操作与 LASX 向量读取交错即可触发,影响 amadd、amcas 等指令,可导致引用计数错误和释放后使用。龙芯两周内提供固件修复,预计国庆前发布,性能损失极小。

🔎

延伸解读

触发条件与影响范围

丢失更新需同时满足三个条件:线程运行在不同物理核、对同一地址执行无数据屏障的原子操作、且至少一个线程在原子操作间插入内存读取。LASX 向量读取(xvld)最易触发,但特定地址关系下普通标量读取也可能触发。受影响指令包括 amadd、amcas、ammax 等,其中 amadd 常用于引用计数,可能导致释放后使用等内存安全问题。

软件规避与修复方案

应用开发者无法直接控制编译器生成的原子指令,软件层面规避需编译器改为生成带数据屏障的原子指令(am<OP>_db.*)或使用 LL/SC 循环,但已编译二进制需重新编译,成本较高。龙芯固件修复通过设置 MCSR24 第 13 位解决,性能损失极小,预计国庆前发布。无法更新固件的用户可在 Linux 内核中直接写入该位。

为何长期未被发现

该问题触发条件复杂,且概率较低,导致长期未被察觉。AOSC OS 在二月后发布的 Core 13 中误关闭了 glibc 的 --enable-multi-arch 选项,使 memcpy 不再使用 LASX 加速,因此八月无法复现;而 Debian 的 glibc 正常启用向量加速,故仍可复现。一旦 AOSC 发布 Core 14 重新启用该选项,问题将再次出现。

❓

Q&A

LA664 核心的原子加指令丢失更新问题是什么?

LA664 核心的原子加指令(如 amadd)在特定条件下会丢失更新,即原子操作偶尔不原子,导致计数器值比实际少,可能引发引用计数错误和释放后使用等内存安全问题。

哪些条件会触发 LA664 的原子指令丢失更新?

需要同时满足三个条件:线程运行在不同物理核心上;这些线程对同一地址执行无数据屏障的原子操作;至少一个线程在原子操作之间插入内存读取(如 LASX 向量读取 xvld,或特定地址关系下的标量读取)。

LA664 的原子指令丢失更新会影响哪些指令?

影响 amadd、amcas、ammax、amswap 等无数据屏障的原子指令,而带数据屏障的指令(如 amcas_db.d)不受影响。

这个问题是如何被发现和定位的?

2026 年 2 月,在 Debian LoongArch 上打包 normaliz 时测试陷入死循环,初步排查未果;半年后借助 AI 辅助,两天内稳定复现,最终定位到 CPU 原子加指令丢失更新。

龙芯是如何修复这个问题的?

龙芯在收到报告后两周内提供测试固件,通过设置 MCSR24 寄存器的第 13 位为 1 来修复,性能损失极小,预计国庆前发布。

这个 CPU 勘误可能造成哪些安全影响?

可能导致引用计数错误,引发释放后使用或双重释放等内存安全问题,例如 Rust 的 Arc 和 mpsc 可能崩溃;但难以用于安全攻击,因为需要同一进程内的两个线程并发操作。

🏷️

标签

➡️

继续阅读