☰
Rust 使用后释放(UAF)检测实战:基于 rust-review 的原始指针逃逸审计方法论
2026/10/10 1:23:38 网站建设 项目流程
  • AI 技能
  • AI 插件
  • 应用安全
  • 网络安全
  • AI 评测

【免费下载链接】skills

Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows

项目地址:https://gitcode.com/gh_mirrors/skills8/skills
点击查看免费下载

导读

本文以 Trail of Bitsskills8/skills仓库中rust-review插件的 UAF 检测器文档为主体,系统讲解 Rust 代码中“原始指针逃逸隐式 Drop 作用域”这一高发使用后释放(Use-After-Free,UAF)漏洞形态的成因、四道验证门(Verification Gates)、常见误报规避策略与可落地的搜索正则,并结合仓库的 memory-safety 集群、worker 协议与 SARIF 导出脚本,说明该检测器在真实审计流水线中的运行位置与判定规则。读完本文,你将掌握一套可直接用于 Rust 安全审计的 UAF 排查清单,理解为什么.as_ptr()之后立刻让源对象 Drop 会制造悬垂指针,以及如何用源码证据(而非直觉)判定一个 UAF 候选是否值得上报。

UAF 检测器在 rust-review 中的定位

use-after-free-finder是rust-review插件的通用检测器(finder)之一,位于 plugins/rust-review/prompts/general/use-after-free-finder.md,其 YAML frontmatter 声明如下:

name: use-after-free-finder description: Detects use-after-free via raw pointers escaping implicit Drop scope in Rust

在整条审计流水线中,它隶属于memory-safety 集群。查看 plugins/rust-review/prompts/clusters/manifest.json 可知:该集群gate为has_unsafe,即只有被审计代码库中存在unsafe代码时,整个集群(含 UAF)才会被调度;如果目标是纯 safe Rust crate,规划器会直接省略整个集群。这背后的依据是 Rust 内存安全漏洞的共性:所有内存安全问题都必然源自unsafe代码——安全代码中的借用检查器已经排除了别名与数据竞争,而unsafe { }块内的行为不在其管辖范围内。

该集群的共享心智模型在 plugins/rust-review/prompts/clusters/memory-safety.md 中被概括为一句审计咒语:对于每个 unsafe 块,问三个问题——谁拥有这块内存、它何时被 Drop、还有谁在指向它?集群 Phase A 要求先构建一次“unsafe 内存映射”(mem_map[site] = { kind, type, owner_var, lifetime_scope, drop_site }),再按固定顺序运行 8 个 finder,其中 UAF 排在第四位(前三位为 UNINITREAD、SETLEN、INVFREE)。每个 finder 的产出是带UAF-前缀编号(如UAF-001)的 finding 文件,最终经 dedup 判官去重、FP+severity 判官定性与定级后,汇入REPORT.md与REPORT.sarif。

核心漏洞形态:11/14 的 UAF 生产事故遵循同一模式

检测器文档明确引用了一项实证研究结论作为 bug shape 依据:

11/14 的生产环境 UAF 漏洞遵循如下模式——一个复杂对象在某个词法作用域(lexical scope)内被创建,随后通过.as_ptr()/.as_mut_ptr()取出原始指针,源对象在作用域结束时被隐式 Drop(尤其是match分支或if let绑定中的临时值),之后该原始指针才被解引用。

用最小化的代码形态示意(依据上述 bug shape 归纳的典型错误写法):

// 错误形态:源对象是 match 分支里的临时值,语句结束后即被 Drop let p = match lookup(key) { Some(v) => v.as_ptr(), // v 是临时值,match 结束后 Drop None => std::ptr::null(), }; // p 此刻已是悬垂指针 unsafe { (*p).field = 1; } // UAF:解引用发生在 Drop 之后
// 错误形态:if let 绑定 + 方法链取指针 let buf; if let Some(s) = maybe_string() { buf = s.as_bytes().as_ptr(); // s 是 if let 绑定,块结束时 Drop } // buf 指向的内存已归还 unsafe { process(buf); } // UAF

关键洞察在于:问题不在“取指针”本身,而在“取指针的源对象生命周期与指针使用点之间的错位”。.as_ptr()返回的裸指针不携带任何生命周期信息,借用检查器对它的追踪到此为止——后续对裸指针的解引用不再受 Rust 所有权系统的保护。这解释了为什么该 bug shape 中“临时值”(temporaries)出现频率极高:临时值在语句/表达式结束时被立即 Drop,是所有作用域中最短的,也是最容易被审计者忽略的。

四道验证门:ALL 必须通过才允许上报

检测器规定,只有同时满足以下全部四条时,一个候选点才可被归档为UAF-xxxfinding;任何一条不满足即“do not file”(不上报)。

Gate 1 — 提取(Extraction):指针来源必须合法

原始指针必须来自以下来源之一:

  • .as_ptr()/.as_mut_ptr()(slice、String、Vec 等容器的方法);
  • Box::into_raw;
  • transmute。

注意文档给出的搜索正则\b(Box|CString)::into_raw\b|Vec::into_raw_parts|\btransmute\b还覆盖了CString::into_raw与Vec::into_raw_parts,但 Gate 1 对这两者的判定规则与.as_ptr()系来源完全不同(见 Gate 2 的分支讨论)。在 rust-review 的 memory-safety 集群 Phase A 中,对应的种子扫描是:

rg seed: "(\.|::)(as_ptr|as_mut_ptr|into_raw|from_raw(_parts)?)\(" # method `.into_raw()` AND assoc-fn `Box::from_raw(`/`Rc::into_raw(`/`Vec::from_raw_parts(`/`CString::from_raw(`

Gate 2 — 终止(Termination):后备分配确实被释放

这一步是 UAF 判定中最需要细分的环节,检测器明确区分了两种提取方式的释放语义:

情形 A:.as_ptr()/.as_mut_ptr()/transmute提取释放来自源对象的隐式 Drop——即源对象离开作用域或被重新赋值。这也是最经典的 UAF 形态:作用域结束的Drop就是“free”,而指针在 free 之后仍被使用。

情形 B:Box::into_raw/Vec::into_raw_parts提取这类 API消费(consume)源对象并抑制作用域退出时的隐式 Drop——into_raw本身就是一种显式的“放弃所有权”操作。因此:

  • 释放必须来自后续某个显式的Box::from_raw/Vec::from_raw_parts/dealloc/ owner-take;
  • 一个泄漏(leak)的into_raw指针如果从未被重新接管,不算 UAF(它只是内存泄漏,不是悬垂访问);
  • 如果该指针被两次重新接管/释放,则应归档为DFREE(double-free),而不是 UAF——详见后文 deconfliction 规则。

Gate 3 — 解引用(Dereference):使用必须时序上晚于 Drop

原始指针必须被实际解引用(读、写、offset偏移、copy_*类内存拷贝),且这些操作在时序上严格发生在 Gate 2 的 Drop 之后。仅“提取后未使用”或“使用发生在 Drop 之前”都不构成 UAF——这正是为什么审计时必须手工构建调用时序,而不能只看单行代码。

Gate 4 — 无缓解(Mitigation absence):Drop 没有被中和

如果以下任一机制中和了源对象的 Drop,则不构成 UAF:

  • mem::forget(跳过析构);
  • ManuallyDrop包装(延迟/取消析构);
  • Box::leak(显式把生命周期延长到'static)。

也就是说,只要存在这些“显式放弃 Drop”的痕迹,且语义上合理,检测器就判定为设计意图而非漏洞。

deconfliction:UAF 与 DFREE 的边界

检测器文档在 Notes 中给出了一个容易混淆的判定优先级,与 memory-safety 集群的 deconfliction 规则一致(见 plugins/rust-review/prompts/clusters/memory-safety.md):

  • UAF>DFREE仅当是单个指针在 free 之后被解引用一次;
  • DFREE需要两次不同的 Drop/free 调用。

落到into_raw场景的具体推演是:into_raw抑制了隐式 Drop,因此基于into_raw指针的 UAF 需要一次显式的后续 free(from_raw/dealloc)作为前提;如果这个 free 发生了两次,根因就从“悬垂指针”变成“双重释放”,应归档为DFREE。配套的 double-free-finder 对此有独立判定:ptr::read制造位拷贝但不移走源,使堆上对象出现两个所有者,两个所有者都走出作用域时同一分配被执行两次析构。

同理,memory-safety 集群还给出INVFREE(对未初始化内存赋值触发对垃圾值执行 Drop)优先于UAF的规则,以及PANICUNWIND(unwind 导致容器元数据与实际元素失配)优先于DFREE/UAF的规则。审计者在面对跨类别候选时应先对号入座,避免把一个根因标注到错误的 bug class。

必须规避的常见误报(False Positives)

检测器文档列出了四类高频误报,审计时应直接排除:

  1. 指针被提取但let _binding = source;延长了源对象的作用域——只要源对象活到指针使用点之后,就没有 UAF。这是最常见的“看着像 UAF、其实安全”的代码。
  2. 源对象是'static——没有 Drop,分配永不释放,不存在悬垂窗口。
  3. Pin<&'a mut T>且生命周期追踪健全——Pin的引用携带真实生命周期,借用检查器可以验证。
  4. 指针来自Box::leak()——这是显式的生命周期延长到'static,属于有意的“永远活着”,不算漏洞(Gate 4 的镜像情形)。

误报规避策略在 rust-review 流水线中是双层的:worker 依据检测器文档现场排除;即使 worker 误报了,后续rust-review-fp-judge(见 plugins/rust-review/agents/rust-review-fp-judge.md)还会对每个 primary 施加威胁模型感知的二次核验(REMOTE/LOCAL_UNPRIVILEGED/BOTH三种攻击者能力模型),并裁决TRUE_POSITIVE/LIKELY_TP/LIKELY_FP/FALSE_POSITIVE/OUT_OF_SCOPE五档结论。

可落地的搜索正则与两条“反直觉”提示

检测器文档提供了五条 ripgrep 正则,覆盖提取点、类型转换点与解引用点:

\.(as_ptr|as_mut_ptr)\( \b(Box|CString)::into_raw\b|Vec::into_raw_parts|\btransmute\b ptr::(read|write|copy(_nonoverlapping)?) \.(offset|add|sub|read|write)\s*\( let\s+\w+\s*=\s*[^;]+\.as_ptr\(\)

两条容易被审计新手踩坑的提示(原文档 Notes 原文要点):

  • offset/add/sub/read/write是裸指针的“方法形式”(p.add(i)、p.read()),并不存在ptr::offset/ptr::add这样的自由函数——因此解引用点的种子应落在方法形式的那一行(第 4 条正则),而不是ptr::自由函数行(第 3 条正则只覆盖ptr::read/ptr::write/ptr::copy_*)。
  • into_raw/into_raw_parts/transmute行是 Gate 1 中.as_ptr()系正则抓不到的“提取来源种子”——Box::into_raw这类调用并不出现.as_ptr()字样,但同样是合法的指针提取来源,必须单独扫描。

在集群 Phase A 的完整扫描集合中(见 memory-safety 集群文档),与 UAF 相关的种子还包括方法形式的全套.(add|sub|offset|read|write|copy_to|copy_from|copy_to_nonoverlapping|copy_from_nonoverlapping)(_unaligned|_volatile)?\s*\(,以及mem::forget/ManuallyDrop::(用于识别 Gate 4 的中和机制)。

在 rust-review 的执行约定中(见 plugins/rust-review/agents/rust-review-worker.md),这些种子必须通过rg(ripgrep)执行:种子写的是 ripgrep 正则语法(\s、\d、\b),部分grep -E实现会把\s静默当成字面s返回空结果,从而制造“假清场”(falsecleared)。如果rg不可用,便携回退是\s→[[:space:]]、\d→[[:digit:]]、丢弃\b(丢弃只会放宽匹配,不会漏报)。

worker 落地:coverage gate 与 finding 文件协议

检测器文档本身是 worker 的“pass 级提示词”,最终落地要经过 worker 协议。根据 rust-review-worker 协议,UAF pass 运行完毕后,worker 必须:

  1. 将每条确认的 UAF 写入独立文件{output_dir}/findings/UAF-001.md,frontmatter 至少含id(UAF-001格式)、bug_class(use-after-free)、title、location(单个path:line,禁用 markdown 链接与绝对路径)、function、confidence、worker;
  2. 正文按七段式结构撰写:## Description/## Code/## Data flow/## Reachability trace/## Impact/## Mitigations checked/## Recommendation;
  3. 在 coverage gate 文件{output_dir}/coverage/worker-N.md中为 UAF pass 登记结果行,如| UAF | use-after-free | filed: UAF-001 |或cleared (...)——cleared必须注明实际执行过的种子,skipped:不是合法结果。

仓库测试 test_validate_artifacts.py 验证了 coverage 行必须与plan.json中(prefix, bug_class)完全逐字一致,例如("UAF", "use-after-free", "cleared (no raw pointer frees)")的形态;test_gating.py 则验证规划器确实把 use-after-free-finder.md 渲染进 worker 的sub_prompt_paths。SARIF 侧,generate_sarif.py 将use-after-free映射为规则描述 “Use-after-free via dangled raw pointer”。

修复建议:三条出路

检测器文档给出了三类修复建议,审计报告中的## Recommendation段应结合现场情况择优给出:

  1. 把源对象绑定到更长生命周期的变量——例如let cs = CString::new(s)?; ffi(cs.as_ptr());,让源对象活到所有指针使用点之后。这条对.as_ptr()系 UAF 最直接有效。
  2. 确实有意为之则显式化——使用Box::leak或mem::forget并配以说明性注释,把“隐式的危险”变成“显式的设计决策”。
  3. 用引用替代裸指针——换成&T,让借用检查器重新接管生命周期验证,从类型层面消灭该类漏洞。

适用前提与限制

需要说明的是:本检测器只覆盖Rust unsafe 上下文中的悬垂裸指针。它的运行前提是被审计代码库has_unsafe=true;纯 safe Rust crate 不会触发 memory-safety 集群。与它相邻但职责不同的检测器还包括:cstring-dangling-finder(CString::as_ptr()临时值逃逸语句作用域后被 FFI 使用,前缀CSTRDANGLE,见 cstring-dangling-finder.md)、double-free-finder(ptr::read双重析构,前缀DFREE)、destructor-skip-finder(mem::forget跳过安全相关析构,前缀DROPSKIP)。审计者应结合这些相邻检测器交叉核对,避免把 UAF 与 DFREE、DROPSKIP 混淆。整个 rust-review 流水线的调用方式与输出目录约定($(pwd)/.rust-review-results/<iso-timestamp>/)可参考 plugins/rust-review/README.md 与 rust-review 技能文档。

  • AI 技能
  • AI 插件
  • 应用安全
  • 网络安全
  • AI 评测

【免费下载链接】skills

Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows

项目地址:https://gitcode.com/gh_mirrors/skills8/skills
点击查看免费下载

相关推荐

上一篇:【亲测免费】 ModelViewer3D 项目常见问题解决方案
下一篇:Qwen2.5-0.5B-Instruct性能测试:CPU环境下如何优化推理速度?实测数据分享

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询