模糊测试并发运行时临时文件冲突的修复
同一个 Go fuzz target 单独跑种子输入时正常,启用 -fuzz 后却偶发“文件不存在”、内容不一致或解析结果漂移,最常见原因之一是多个 worker 共用了同一个临时文件名。一个 worker 刚写完,另一个可能马上截断、覆盖或删除它。
不要用filepath.Join(os.TempDir(), "case.bin")作为 fuzz target 的共享路径。优先在每次调用里创建t.TempDir(),再把稳定文件名放进这个独立目录;同一次调用需要多个临时文件时,再使用os.CreateTemp。
官方文档:https://go.dev/doc/security/fuzz/
固定临时文件名为什么只在 fuzz 时出错
Go 官方文档明确说明,启用模糊测试后,fuzz target 会由多个 worker 并行调用,默认并行进程数与 GOMAXPROCS 相关。目标函数还应保持快速、确定,并且不能依赖跨调用保留的全局状态。
下面的写法表面上有写入和删除,实际上所有调用都指向同一个 payload.bin。os.WriteFile 会截断已存在的文件;defer os.Remove 也可能删掉另一个 worker 正在使用的文件。
func FuzzDecodeFile(f *testing.F) {
f.Add([]byte("seed"))
f.Fuzz(func(t *testing.T, data []byte) {
// 错误示例:所有 worker 共享同一个固定路径。
path := filepath.Join(os.TempDir(), "payload.bin")
if err := os.WriteFile(path, data, 0o600); err != nil {
t.Fatal(err)
}
// 这个删除动作可能影响另一个并发调用。
defer os.Remove(path)
if _, err := decodeFile(path); err != nil {
t.Fatal(err)
}
})
}
这类问题通常具有非确定性:同一个输入单独回放可能通过,因为冲突依赖其他 worker 的时机,而不是输入内容本身。

最小修复是每次调用使用 t.TempDir
t.TempDir() 为当前测试返回临时目录,目录会在测试及其子测试结束后自动移除;同一个测试连续调用它,也会得到不同目录。fuzz target 收到的第一个参数正是 *testing.T,所以可以直接使用。
func FuzzDecodeFile(f *testing.F) {
f.Add([]byte("seed"))
f.Fuzz(func(t *testing.T, data []byte) {
// 每次 fuzz 调用都有独立目录,固定文件名不再跨调用冲突。
path := filepath.Join(t.TempDir(), "payload.bin")
if err := os.WriteFile(path, data, 0o600); err != nil {
t.Fatal(err)
}
got, err := decodeFile(path)
if err != nil {
t.Fatal(err)
}
// 断言只依赖当前输入和当前目录中的文件。
if len(got) == 0 && len(data) != 0 {
t.Fatalf("decode returned empty output for %d bytes", len(data))
}
})
}
这里不需要再写 defer os.RemoveAll。目录清理由 testing 包负责,失败时的清理路径也更统一。文件名仍可保持 payload.bin,因为真正需要唯一的是目录命名空间,而不是业务代码看到的文件名。
同一次调用有多个文件时再用 os.CreateTemp
如果一次 fuzz 调用会拆出多个片段,仅有独立目录还不够:目录内部也不能反复写同一个文件。os.CreateTemp 会根据模式生成唯一名称,并以读写方式打开文件。官方文档说明,多程序或 goroutine 同时调用时不会选择同一个文件名。
func writeChunk(t *testing.T, dir string, data []byte) string {
t.Helper()
// 星号位置会替换为随机字符串,避免同一目录内重名。
file, err := os.CreateTemp(dir, "chunk-*.bin")
if err != nil {
t.Fatal(err)
}
path := file.Name()
// 写入失败也要关闭句柄,错误直接归到当前 fuzz 输入。
if _, err := file.Write(data); err != nil {
file.Close()
t.Fatal(err)
}
if err := file.Close(); err != nil {
t.Fatal(err)
}
return path
}
使用 t.TempDir() 作为 CreateTemp 的目录参数后,文件会落在当前 fuzz 调用的隔离范围里,目录最终仍由 testing 包清理。

被测代码写死 os.TempDir 时要改成目录注入
如果冲突发生在生产函数内部,仅修改测试文件还不够。更稳妥的接口是让调用方传入工作目录,或者把文件创建封装为依赖。测试传 t.TempDir(),生产环境再传经过配置的目录。
func DecodeBytes(workDir string, data []byte) ([]byte, error) {
// 稳定文件名只在调用方提供的工作目录中有效。
path := filepath.Join(workDir, "payload.bin")
if err := os.WriteFile(path, data, 0o600); err != nil {
return nil, err
}
return decodeFile(path)
}
func FuzzDecodeBytes(f *testing.F) {
f.Add([]byte("seed"))
f.Fuzz(func(t *testing.T, data []byte) {
// 测试显式注入独立目录,移除对全局临时路径的依赖。
if _, err := DecodeBytes(t.TempDir(), data); err != nil {
t.Fatal(err)
}
})
}
这种改法还能让普通单元测试复用同一接口,并使目录权限、剩余空间和清理策略更容易单独测试。
怎样确认修复针对的是并发冲突
可以提高 -parallel 复查目标,但不要把“跑久了没报错”当成唯一证明。还应检查代码里是否残留固定全局路径、包级可变变量和跨调用缓存。下面只是建议的复现命令,不是本文生成图片的运行证据。
# 提高 fuzz worker 数量,观察并发条件下是否仍有文件冲突。 go test -fuzz=FuzzDecodeFile -parallel=8 -fuzztime=30s # 单独回放一个失败语料,检查结果是否由输入本身决定。 go test -run='FuzzDecodeFile/失败语料名称'
若隔离临时目录后问题仍存在,应继续排查共享端口、固定数据库名、全局随机数源、包级 map、缓存目录和外部服务账号。t.TempDir() 只能隔离文件目录,不能自动隔离所有共享状态。
常见问题
能不能用输入哈希当临时文件名
不建议把它当成主要修复。相同输入可能被重复执行,截断后的短哈希也可能碰撞;更重要的是,不同 worker 仍共享同一个父目录。独立目录加稳定文件名更直观。
t.TempDir 会让模糊测试变慢吗
频繁创建目录确实有文件系统成本。如果被测逻辑并不需要真实文件,优先把接口改为接收 io.Reader 或字节切片;必须测试文件行为时,再接受这部分隔离成本。
可以在 f.Fuzz 外面只创建一次 TempDir 吗
不要让所有调用写同一个目录。*testing.F 的设置阶段适合准备只读且不变的资源;会被目标修改的文件应在每次 *testing.T 调用内创建。
os.CreateTemp 后还要删除文件吗
如果文件位于 t.TempDir() 中,目录会自动清理;但文件句柄仍要及时关闭。若直接把空字符串作为目录参数,则调用方仍负责删除该文件。
结论
并发 fuzz 的临时文件冲突,本质是多个调用共享了可写命名空间。把 t.TempDir() 放进 fuzz target,可以先隔离每次调用;目录内需要多个文件时用 os.CreateTemp;生产函数若写死全局临时目录,则改为显式传入工作目录。修复后,失败应重新由输入决定,而不是由 worker 的碰撞时机决定。
参考:https://go.dev/doc/security/fuzz/、https://pkg.go.dev/testing#T.TempDir、https://pkg.go.dev/os#CreateTemp
服装企业委托检验时怎样准备纤维成分样品
- 上一篇
- 服装企业委托检验时怎样准备纤维成分样品
- 下一篇
- 深绿雨林玻璃光斑手机壁纸提示词
-
- Golang · Go问答 | 33分钟前 | HTTP · 故障排查 · net/http · Go问答 · 流式响应 · Go FLUSH ResponseController ResponseWriter 代理缓冲 HTTP流式响应
- ResponseController Flush 后客户端仍无数据的原因
- 108浏览 收藏
-
- Golang · Go问答 | 39分钟前 |
- HTTP 重定向后认证头丢失的客户端策略
- 435浏览 收藏
-
- Golang · Go问答 | 46分钟前 | go并发 · Go 可见性 atomic.Pointer 并发配置
- 原子指针替换配置对象时的可见性边界
- 204浏览 收藏
-
- Golang · Go问答 | 56分钟前 |
- atomic.Uint64 对齐要求在旧结构体中的处理
- 268浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- 原子类型与普通字段混用导致竞态的修复
- 360浏览 收藏
-
- Golang · Go问答 | 1小时前 |
- 模糊测试中时间与随机数依赖的确定性改造
- 273浏览 收藏
-
- Golang · Go问答 | 2小时前 |
- 模糊测试输入触发 panic 后的复现路径
- 414浏览 收藏
-
- Golang · Go问答 | 2小时前 |
- 测试临时目录在子测试结束后的清理边界
- 181浏览 收藏
-
- Golang · Go问答 | 2小时前 | go · 环境变量 ·
- t.Parallel 测试共享环境变量的隔离方案
- 115浏览 收藏
-
- Golang · Go问答 | 3小时前 |
- 测试缓存未失效时输入文件依赖的处理
- 174浏览 收藏
-
- Golang · Go问答 | 4小时前 | 依赖管理 · go · go work sync go work vendor Go workspace inconsistent vendoring Go依赖同步
- go work vendor 结果不一致的依赖同步步骤
- 192浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 405次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 483次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 493次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 437次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 262次使用
-
- Go语言自带测试库testing使用教程
- 2023-01-07 467浏览
-
- Go1.18新特性工作区模糊测试及泛型的使用详解
- 2022-12-30 131浏览
-
- Go中的新增对模糊测试的支持
- 2023-01-08 435浏览
-
- Go 测试清理逻辑迁移:从 defer 到 t.Cleanup 的正确写法
- 2026-07-02 418浏览
-
- Go testing.T.Context 怎么管理测试资源:清理时机、并发子测试与取消边界
- 2026-08-25 356浏览

