并发与内存安全术语:定义、分类与关联
本文界定并发编程与系统安全领域六个核心术语(RC、DC、TOCTOU、UAF、UB、CC)的内涵、外延及相互关系,并补充邻近术语。描述基于事实与标准,不做价值判断。
1. 核心术语定义
1.1 RC(Race Condition,竞态条件)
系统或进程的正确结果依赖于不可控的事件(如线程调度、中断、I/O完成)顺序。当该顺序偏离预期时,即产生竞态条件。
- 本质:逻辑错误,非内存错误。
- 判定标准:存在两个及以上并发操作,结果随执行顺序变化。
1.2 DC(Data Race,数据竞争)
RC的子集。两个及以上线程同时访问同一内存地址,至少一个为写操作,且无同步机制(互斥锁、原子操作、内存屏障)。
- 本质:内存访问冲突。
- 后果:读操作获得撕裂值(Torn Read)或中间状态,进而导致状态破坏。
- 注:在C/C++中,DC直接引发UB。DC是RC,但RC不必然是DC(如逻辑竞态但原子操作保护)。
1.3 TOCTOU(Time-Of-Check to Time-Of-Use,检查时间与使用时间)
RC的经典模式。系统检查资源状态(权限、类型、路径)与后续使用该资源之间存在时间窗口,攻击者可在窗口内改变资源状态,使检查结果失效。
- 载体 :文件系统(
access()+open())、权限缓存、符号链接解析。 - 判定标准:检查操作与使用操作非原子性,且两次操作间有非受控状态变更可能。
- CWE编号:CWE-367。
1.4 UAF(Use-After-Free,释放后使用)
内存管理错误。堆内存被释放(free/delete)后,遗留指针(悬垂指针)未被置空,后续代码通过该指针访问原内存区域。
- 后果层级 :
- 内存未重分配:读取残值或造成崩溃。
- 内存已重分配(大小匹配):访问或篡改新对象数据,引发内存破坏。
- 攻击者控制分配:伪造对象布局(虚表、函数指针),实现控制流劫持(CFH)。
- 语言属性:在C/C++中为UB;在Rust、Go等语言中通过所有权/GC机制杜绝。
1.5 UB(Undefined Behavior,未定义行为)
C/C++标准中未规定语义的操作。编译器假设UB不存在,并依据此假设进行激进优化。
- 常见触发:DC、UAF、数组越界、空指针解引用、有符号整数溢出、严格别名违反。
- 标准约束:标准对UB的后果不做任何要求------程序可能正常、崩溃、产生错误结果或执行任意代码。
- 地位:DC与UAF是具体场景,UB是这些场景的语言标准定性。
1.6 CC(Concurrency Control,并发控制)
协调并发操作以维持数据一致性与程序正确性的技术集合。非漏洞类别,而是一组解决方案。
- 分类 :
- 悲观控制:互斥锁(Mutex)、读写锁、信号量。
- 乐观控制:原子操作、CAS(Compare-And-Swap)、事务内存。
- 隔离级别:数据库中的MVCC、快照隔离。
- 代价:引入开销,且须处理自身副作用(见补充术语)。
2. 术语关联结构
┌──────────────────┐
│ RC (竞态条件) │──逻辑级根源
└────────┬─────────┘
│
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│ DC │ │ TOCTOU │ │ 其他RC变体 │
│ (数据竞争) │ │ (检查/使用)│ │ │
└────┬─────┘ └──────────┘ └──────────────┘
│
▼ (在C/C++中)
┌──────────┐
│ UB │◄──────────────┐
│ (未定义行为)│ │
└──────────┘ │ (内存破坏)
▲ │
│ (内存错误) │
┌──────────┐ │
│ UAF │───────────────┘
│ (释放后使用)│
└──────────┘
│
▼ (利用)
┌──────────┐
│ 控制流劫持 │
│ (CFH/ROP) │
└──────────┘
┌──────────┐
│ CC │── 作用于以上各层,但可能引入新问题
│ (并发控制) │
└──────────┘
3. 补充术语(邻近概念)
3.1 并发控制副作用
- Deadlock(死锁):线程持有资源并等待对方持有的资源,形成循环依赖,状态为永久阻塞。必要条件:互斥、持有并等待、不可抢占、循环等待。
- Livelock(活锁):线程不阻塞但持续重试失败,无法推进进度。常见于无锁算法的饥饿规避策略。
- Starvation(饥饿):低优先级线程因高优先级线程抢占,长期无法获得CPU或锁资源。
- ABA Problem(ABA问题):CAS操作中,目标值由A变B再变A,CAS误判状态未变。需通过版本号/标记指针解决。
- Memory Barrier(内存屏障) :CPU/编译器乱序执行的约束指令。保证屏障两侧的读写操作对其它核可见的顺序性。
std::atomic底层依赖此机制。
3.2 内存破坏同类项
- Double Free(双重释放) :对同一地址调用两次
free,破坏堆管理器双向链表元数据,常被用于构造任意地址读写。 - Heap/Stack Overflow(堆/栈溢出):写入数据超出缓冲区边界。栈溢出覆盖返回地址(RIP);堆溢出干扰相邻堆块元数据或对象内容。
- Uninitialized Read(未初始化读):在赋值前读取栈或堆变量,获取残留的敏感数据(如栈Canary、指针、密码),属信息泄露。
3.3 数据库并发异常
数据库语境下,RC表现为隔离性失效:
- Dirty Read(脏读):读到未提交事务的中间修改(该事务可能回滚)。
- Non-Repeatable Read(不可重复读):同一事务内两次读取同一行记录,因其它事务提交的修改,结果不一致。
- Phantom Read(幻读):同一事务内两次范围查询,因其它事务的插入/删除,结果集记录数变化。
3.4 漏洞利用技术
- Race Window(竞态窗口):从检查到使用(TOCTOU)或从释放到重用(UAF)的时间间隔。利用窗口长度决定攻击成功率。
- CFH(Control Flow Hijacking,控制流劫持):篡改函数指针、虚表指针或返回地址,改变程序执行流。
- ROP(Return-Oriented Programming) :在NX/DEP开启后,复用进程中现有代码片段(Gadget,以
ret结尾)串联攻击逻辑,绕过数据执行保护。 - CFI(Control Flow Integrity):防御机制,在间接跳转前验证目标地址合法性,阻断CFH与ROP。
- ASLR(Address Space Layout Randomization):防御机制,随机化基址,增大ROP中Gadget定位难度,但可被信息泄露绕过。
4. 分类矩阵
| 类别 | 术语 | 触发层级 | 可检测性 |
|---|---|---|---|
| 并发逻辑错误 | RC、TOCTOU、Dirty/Phantom Read | 业务/算法层 | 静态分析 + 动态并发测试 |
| 内存访问错误 | DC、UAF、Double Free、Overflow | 内存模型层 | AddressSanitizer / ThreadSanitizer |
| 语言标准定性 | UB | 语言规范层 | UndefinedBehaviorSanitizer |
| 并发控制 | CC、Deadlock、Livelock、ABA | 同步机制层 | 锁检查工具 / 动态分析 |
| 利用与防御 | CFH、ROP、CFI、ASLR | 系统/架构层 | 逆向 / 动态调试 |
5. 检测与修复工具对应
| 问题 | 静态检测 | 动态检测 | 缓解/修复 |
|---|---|---|---|
| DC | Clang Thread Safety Analysis | ThreadSanitizer (TSan) | Mutex、原子操作 |
| UAF | Clang Static Analyzer | AddressSanitizer (ASan) | 智能指针、置空指针 |
| UB | -fsanitize=undefined (编译期插桩) | UBSan | 明确语义、边界检查 |
| TOCTOU | 人工审查 + 状态机分析 | Fault Injection / 模糊测试 | 原子性API(openat)、权限检查后重新验证 |
| Deadlock | Lockdep | Helgrind | 固定锁顺序、TryLock超时 |