You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust静态内存并发裸指针操作:是否为UB及风险问询

关于Rust裸指针并发访问静态变量的未定义行为分析

问题背景

我编写了一段Rust代码,通过原始可变指针让两个线程访问并修改同一个静态usize变量。对该值的操作为非原子位操作,但逻辑上通过条件检查确保同一时间仅有一个线程写入内存地址。

代码实现

use std::time::Duration;

#[derive(Clone, Copy)]
struct StaticNumber(*mut usize);
unsafe impl Send for StaticNumber {}

fn main() {
    let number = StaticNumber(Box::leak(Box::new(usize::MAX)) as *mut usize);

    let t1 = std::thread::spawn(move || thread1(number));
    let t2 = std::thread::spawn(move || thread2(number));

    let _ = t1.join();
    let _ = t2.join();
}

fn thread1(number: StaticNumber) {
    loop {
        if unsafe { *number.0 } & 1 == 0 {
            unsafe { *number.0 |= 1 };
        } else {
            std::thread::sleep(Duration::from_millis(1));
        }
    }
}

fn thread2(number: StaticNumber) {
    loop {
        if unsafe { *number.0 } & 1 == 1 {
            unsafe { *number.0 &= usize::MAX << 1 };
        } else {
            std::thread::sleep(Duration::from_millis(1));
        }
    }
}

核心逻辑

  • 每个线程读取usize值的最低有效位(LSB);
  • 根据LSB,线程1将其设为1,线程2将其重置为0;
  • 逻辑上通过修改前的LSB检查,确保同一时间只有一个线程写入内存。

但这段代码使用裸指针并发访问同一内存,通常被认为会触发Rust的未定义行为(UB),潜在问题包括操作非原子性、编译器/CPU优化干扰、违反Rust别名规则。现提出三个技术问题:

  1. 根据Rust内存模型,该代码是否确实属于未定义行为?
  2. 即便存在条件检查,非原子位操作是否仍会引发内存损坏、崩溃等不良行为?
  3. 即便程序看似保证了更新的互斥性,编译器或CPU优化(如指令重排、过期读取、推测执行)是否会破坏该逻辑?

我知晓使用原子操作是正确的处理方式,但好奇该带有最终一致性模型的特定代码在技术层面是否真的属于UB,希望结合Rust别名规则、编译优化及底层硬件行为进行解释。


问题解答

1. 是否属于Rust内存模型下的未定义行为?

是,肯定属于未定义行为,核心原因有两点:

  • 违反Rust别名规则:Rust的内存安全核心规则是,同一时间内内存的访问只能是“单一可变访问”或“多个不可变访问”,二者不可共存。这段代码中两个线程同时持有指向同一内存的裸指针,且都执行了读+写操作,等同于同时存在多个可变访问,直接突破了Rust的内存安全边界。
  • 未同步的并发读写:Rust内存模型基于C++11标准,其中明确规定未同步的并发读写(写操作与读/写操作无同步机制协调)属于未定义行为。你的代码没有使用任何同步原语(互斥锁、原子操作的内存屏障等),无论逻辑上的条件检查如何,这种访问模式在内存模型层面都是非法的。

2. 非原子位操作是否会引发内存损坏或崩溃?

完全可能。非原子的读-改-写操作(如*number.0 |= 1)会被硬件拆分为三个独立步骤:读取内存值到寄存器、修改寄存器值、写回内存。在并发场景下,这些步骤可能和另一个线程的操作交错执行:
比如线程1读取LSB为0后,线程2也同时读取到LSB为0(此时线程1还未完成写回),随后两个线程都执行写操作,最终内存中的值会出现逻辑上的矛盾状态。这种状态错乱可能导致后续逻辑异常,极端情况下会触发崩溃(比如依赖该值特定位的代码执行非法内存访问)。

3. 编译器/CPU优化是否会破坏互斥逻辑?

绝对会,且破坏方式不可预测:

  • 编译器指令重排:LLVM编译器在无同步约束时会优化指令顺序,比如把线程1中的“写LSB”操作提前,或者将读操作缓存到寄存器,导致线程1永远看不到线程2的修改,陷入死循环或错误的执行分支。
  • CPU缓存不一致:多核心CPU中每个线程可能持有内存的独立缓存副本,无同步机制时,一个核心的写操作不会及时同步到其他核心的缓存,导致其他线程读取到过期值——比如线程2已重置LSB为0,但线程1的缓存中仍保留旧值,持续执行错误逻辑。
  • CPU推测执行:CPU可能提前执行条件分支中的写操作,即便后续条件检查不成立,这会直接导致内存被错误修改,破坏原本的互斥逻辑。

这些优化完全符合硬件和编译器规范,因为代码没有提供任何内存屏障或同步原语,无法告知编译器/CPU“该内存访问需要跨线程同步”。


内容的提问来源于stack exchange,提问作者Babur Makhmudov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 08:27:35