用vibecode模糊测试在FFmpeg中定位除以零漏洞 📅 发布时间:2026/8/31 2:05:44 👁 浏览次数: 这次我们来看一个非常有意思的技术组合用基于 vibecode 方式生成的模糊测试工具在 FFmpeg 中定位到一个除以零漏洞。先说结论除以零Divide By Zero在 FFmpeg 这类 C/C 解析类项目里通常被归类为可用性问题触发结果是进程崩溃也就是拒绝服务不是直接的远程代码执行。但放到转码服务、播放器、在线视频处理平台上一个崩溃输入就可能拖垮 worker 进程所以它仍然值得认真对待。FFmpeg 的输入解析路径长、音视频容器和编码格式多几乎每一个 demuxer、decoder 都在处理外部输入是典型的模糊测试目标。基于 vibecode 的模糊测试工具思路很简单让 AI 辅助生成 fuzz harness、样本变异脚本和崩溃分析脚本再由人来核对、开启 Sanitizer、持续喂样本最终把导致崩溃的畸形输入定位到具体函数。本文会带你完成一条可落地的完整链路从环境准备开始构建带 ASan/UBSan 的 FFmpeg fuzz 目标运行批量测试复现除零崩溃最小化崩溃样本写回归验证最后给常见问题和排查清单。整个过程不依赖昂贵硬件普通多核 CPU 开发机就能跑适合安全测试工程师、播放器和转码服务开发者以及想了解 AI 辅助编程 模糊测试组合的工程人员。1. 从标题拆解vibecode、模糊测试、除以零漏洞1.1 vibecode 在安全测试里的角色vibecode 是 vibe coding 的常见简写描述一种以 AI 辅助为主的开发方式。开发者用自然语言描述意图让代码生成工具快速产出原型代码。它并不是一个特定的模糊测试工具名称而是一种工作模式。放到安全测试场景vibecode 能提速的部分非常具体生成初始 fuzz harness 骨架比如读取输入文件、调用目标 API 的模板代码生成样本变异脚本在基础语料上做位翻转、字节替换、分片拼接生成崩溃日志聚类和调用栈解析脚本生成 CI 配置把模糊测试接入定时任务。但必须强调AI 生成的代码只适合作为起点不能直接丢到生产环境跑。编译选项、Sanitizer 配置、崩溃样本的判定都需要人工确认。模糊测试的价值不取决于代码生成得有多快而取决于测试目标构建得是否准确、Sanitizer 是否开启、崩溃样本是否分析到位。1.2 模糊测试工具要解决什么问题模糊测试Fuzzing的核心思路是向目标程序输入大量畸形、随机或结构变异的数据观察程序是否异常退出、触发内存错误或未定义行为。FFmpeg 是这类测试的理想目标因为它天生需要解析不可信的外部输入一个损坏的 MP4、一个截断的 FLAC、一个错误声明的编码参数都可能让解析器走进异常分支。常见的模糊测试引擎包括 libFuzzer、AFL、Honggfuzz。本文讨论的流程以 libFuzzer Sanitizer 为主因为 FFmpeg 官方提供了针对 fuzz 的编译配置和 fuzz target 源码复现门槛较低。1.3 除以零漏洞的实际影响除以零在 C/C 中属于未定义行为整数除零通常触发 SIGFPE导致进程直接崩溃。放在 FFmpeg 的 decoder 或 demuxer 里攻击者只要诱导用户播放一个特制媒体文件就可能让播放器或转码服务崩溃。从严重性看它一般不是 RCE而是 DoS。但如果 FFmpeg 被集成在服务端转码集群、直播处理管线中一个远程可触发的崩溃就会变成稳定性风险影响面会被放大。这也是为什么即使它“只是崩溃”仍然值得在开发和测试阶段解决。2. 核心能力速览事项说明项目类型安全研究与模糊测试工程实践测试目标FFmpeg 多媒体处理库测试方法基于 vibecode 生成的模糊测试工具 libFuzzer ASan/UBSan漏洞类型除以零Divide By Zero主要表现为拒绝服务显存需求不依赖 GPU推荐多核 CPU 足够内存启动方式命令行编译 运行 fuzzer是否支持 API本文不涉及对外 API 服务重点在本地批量测试流程是否支持批量任务支持模糊测试天然适合批量样本执行也可以接入 CI 定时任务需要掌握的技能C/C 编译、FFmpeg 基本概念、崩溃日志分析适合场景播放器/转码服务健壮性测试、开源软件安全审计、AI 辅助编程实践这里需要先说明如果读者期待的是“下载一个一键包、双击启动、打开浏览器页面”的体验那这篇文章的内容不是这个方向。基于 vibecode 的模糊测试工具更像一套工程脚本集合它的产物是命令行工具和崩溃样本而不是 WebUI。3. 适用场景与使用边界3.1 适合谁维护播放器、转码服务、点播平台底层能力的开发者可以用 FFmpeg fuzz 提前发现解析器崩溃安全测试工程师想建立一套可复现的本地模糊测试流程对 AI 辅助编程感兴趣的工程师想验证 vibecode 到底能在安全测试里承担多少工作。3.2 能解决什么问题在发布前发现 FFmpeg 解析特定媒体格式时的崩溃把崩溃样本最小化方便定位具体代码路径为已知崩溃建立回归测试防止后续重构再次引入用少量语料和较短时间覆盖大量输入组合。3.3 不适合什么场景没有崩溃样本分析能力的纯“跑批”场景跑一万个样本不分析等于没跑需要在生产环境直接使用 AI 生成测试工具的场合AI 生成代码必须经过审查对授权边界不清晰的线上服务进行扫描或压测这属于未授权测试不应进行。3.4 安全与合规边界模糊测试工具应当只用于你拥有源码、授权进行安全测试的项目。对 FFmpeg 这类开源项目可以在本地构建测试版本发现问题后走开源社区安全反馈渠道避免直接公开可利用的 0day 细节。涉及私有播放器、内部转码服务时先在测试环境验证不能用生产流量直接测试。如果崩溃样本包含用户数据或版权内容应在最小化后脱敏处理。4. 环境准备与前置条件前置条件清单如下按 Debian/Ubuntu 类系统为例macOS 和 WSL 环境需要相应调整包名。依赖版本建议用途操作系统Linux 或 macOSWindows 推荐使用 WSL2编译和运行 fuzz target 更顺畅Clang较新版本建议与 libFuzzer 匹配开启 Sanitizer 和 libFuzzerCMake / Make系统自带即可FFmpeg 构建FFmpeg 源码从官方仓库拉取按目标版本 checkout测试目标Python 3可选崩溃样本批量分析脚本磁盘空间至少 20 GB 可用FFmpeg 编译产物 语料库 崩溃样本安装基础工具的命令模板sudo apt update sudo apt install -y git build-essential make cmake clang llvm pkg-config \ yasm nasm python3 python3-pip环境准备阶段有一个常见误区不要用系统自带的 FFmpeg 二进制来测试。我们要测试的是经过 Sanitizer 编译的 FFmpeg这样才能在崩溃发生时给出精确的内存错误或未定义行为报告。5. 构建带 Sanitizer 的 FFmpeg 模糊测试目标5.1 获取 FFmpeg 源码git clone https://github.com/FFmpeg/FFmpeg.git cd FFmpeg # 建议切到稳定的 release 分支而不是直接跑 main git checkout n7.1版本号需要按实际情况选择。如果你想对比某个已知问题可以 checkout 到问题存在的版本如果只是做稳定性测试建议选择最近的 release 分支并在修复后同步更新。5.2 配置编译参数FFmpeg 官方提供 fuzz target 支持核心编译选项是--enable-ossfuzz。同时开启 AddressSanitizer 和 UndefinedBehaviorSanitizer后者对捕获除以零特别重要。export CCclang export CFLAGS-fsanitizeaddress,undefined -g -O1 -fno-omit-frame-pointer export CXXFLAGS-fsanitizeaddress,undefined -g -O1 -fno-omit-frame-pointer ./configure --ccclang --enable-ossfuzz \ --disable-doc --disable-programs \ --disable-avdevice --disable-postproc \ --disable-network --disable-swscale \ --disable-encoders --disable-muxers \ --disable-filters --disable-avfilter make -j$(nproc)这里的--disable-*是为了缩短编译时间缩小攻击面。如果你后续需要测试特定编码器或格式可以根据需要裁剪。注意--enable-ossfuzz会要求编译器支持 fuzzer 相关选项所以CCclang不能省略。编译完成后在tools/目录下应该能看到一批 fuzz target 可执行文件例如target_dec_fuzzer、target_dem_fuzzer。5.3 确认 target 能运行./tools/target_dec_fuzzer -help如果能看到 libFuzzer 的参数说明说明编译成功。这里要特别说明target_dec_fuzzer是针对 decoder 的 fuzz 目标它会随机读取输入文件并尝试解码target_dem_fuzzer则针对 demuxer也就是容器解析层。除零漏洞既可能出现在容器解析也可能出现在解码器内部所以两个目标都值得跑。6. 基于 vibecode 的模糊测试工具设计与批量测试6.1 工具的整体结构基于 vibecode 生成的模糊测试工具典型结构可以是这样fuzz-tool/ ├── corpus/ # 初始语料库 ├── output/ # 崩溃样本和日志 ├── inputs/ # 待测试批量文件 ├── mutator.py # 样本变异脚本 ├── run_fuzz.sh # 批量执行入口 ├── collect_crash.py # 崩溃日志采集 └── triage.py # 崩溃归类与去重AI 可以帮我们快速生成这些脚本的骨架但每个脚本都需要人工核对文件路径是否正确、非零退出码的判断是否合理、是否遗漏了 timeout 逻辑。下面给出一个通用批量执行脚本示例。#!/usr/bin/env bash # run_fuzz.sh - 批量运行 fuzz target 并保留崩溃样本 set -euo pipefail TARGET./tools/target_dec_fuzzer CORPUS_DIRcorpus OUTPUT_DIRoutput mkdir -p ${OUTPUT_DIR} # 使用 libFuzzer 自带的批量能力 ${TARGET} \ -runs100000 \ -timeout5 \ -max_len8192 \ -print_final_stats1 \ ${CORPUS_DIR} ${OUTPUT_DIR}这里的参数含义是运行 10 万轮、单样本超时 5 秒、单样本最大长度 8192 字节、使用corpus作为输入语料并把新生成的崩溃样本输出到output目录。6.2 语料准备语料质量直接决定 fuzz 效率。初始语料不需要太大但要覆盖常见格式一段正常的 MP4 小文件一个截断的 FLV 片段一个小体积的 PNG 图片一个简单的 WAV 文件一段 H.264 裸流。把这些文件丢进corpus/目录libFuzzer 会基于它们做变异。如果没有任何语料fuzzer 也可以从零开始随机生成输入但早期覆盖率增长会很慢。6.3 运行批量测试执行脚本chmod x run_fuzz.sh ./run_fuzz.sh运行后重点观察两类输出覆盖率增长情况和崩溃样本生成情况。libFuzzer 会周期性输出当前覆盖率、运行速度等信息。如果跑了很久覆盖率一直不动大概率是语料太少或 fuzz target 没选到关键解析路径。6.4 采集崩溃样本fuzzer 生成的崩溃样本文件名通常带有crash-前缀输出在指定目录中。除零问题在开启 UBSan 后会以运行时错误形式被捕获而不是简单地表现为“程序退出”。为了让采集更可靠可以写一个通用脚本把退出码非 0、超时、有 sanitizer 输出的输入文件统一复制到一个待分析目录。#!/usr/bin/env python3 # collect_crash.py - 通用崩溃样本采集脚本 import os import shutil import subprocess from pathlib import Path INPUT_DIR Path(inputs) OUTPUT_DIR Path(output) TARGET ./tools/target_dec_fuzzer OUTPUT_DIR.mkdir(exist_okTrue) for sample in INPUT_DIR.iterdir(): if not sample.is_file(): continue try: result subprocess.run( [TARGET, -runs1, str(sample)], capture_outputTrue, timeout10, ) if result.returncode ! 0: shutil.copy2(sample, OUTPUT_DIR / sample.name) except subprocess.TimeoutExpired: print(ftimeout: {sample.name})注意单个样本只跑 1 轮并不能确保崩溃稳定复现这个脚本定位是“初筛”后面还需要用 fuzzer 的复现模式确认。7. 复现并定位除以零漏洞7.1 用 fuzzer 复现libFuzzer 支持直接传入单个样本文件进行复现。假设崩溃样本名为crash-xxxxx./tools/target_dec_fuzzer -runs1 crash-xxxxx如果能在单独运行该样本时稳定得到异常说明问题可复现。可复现性是后续分析和上报的基础。7.2 用 UBSan 输出定位问题除以零在 UBSan 下的典型输出会明确指出是运行时错误并给出调用栈。形如runtime error: division by zero #0 0x5555557b4d78 in parse_frame_header src/libavcodec/xxxdec.c:1234 #1 0x5555557b6c2a in decode_frame src/libavcodec/xxxdec.c:1456 ...上面这段是示意格式不是真实崩溃栈。实际路径和行号会随 FFmpeg 版本不同而变。关键是要找到最内层的帧它通常指向执行除法运算的具体代码。7.3 用 gdb 确认调用栈如果 UBSan 输出不够清晰可以用 gdb 在SIGFPE处打断点gdb --args ./tools/target_dec_fuzzer -runs1 crash-xxxxx在 gdb 里执行handle SIGFPE stop run btbt会打印完整调用栈。这是定位崩溃的关键一步。如果崩溃发生在解码循环里需要继续看是哪一层的解析逻辑没有校验除法的分母。7.4 判断漏洞等级得到崩溃栈后按这个顺序判断是除零、空指针解引用还是内存越界触达这个分支需要什么前置条件是不是只需要一个特制媒体文件崩溃发生在解码线程还是解复用线程是否可能被进一步利用还是只造成 DoS绝大多数除零都落在 DoS 级别。修正思路是在执行除法前增加合法性判断分母为 0 时走错误处理分支而不是继续计算。这不是一个高难度修复但如果没有 fuzz这个问题可能不会被发现。8. 崩溃样本最小化与回归8.1 使用 libFuzzer 最小化崩溃样本可能非常大直接保留在语料库中会拖慢后续回归速度。libFuzzer 自带最小化选项./tools/target_dec_fuzzer \ -minimize_crash1 \ -max_len8192 \ crash-xxxxx最小化后libFuzzer 会生成一个更小的崩溃样本。这个样本只保留触发崩溃的最少字节极大方便定位和上报。8.2 编写回归测试找到根因后把最小化样本加入回归语料并写一个简单的测试脚本。这样未来 FFmpeg 代码变更时可以快速确认问题是否复发。#!/usr/bin/env bash # regression_test.sh - 验证除零崩溃是否已修复 set -euo pipefail TARGET./tools/target_dec_fuzzer CRASH_SAMPLEregression/divide_by_zero_sample.bin # 修复后这条命令应该正常返回 0 ${TARGET} -runs1 ${CRASH_SAMPLE} || { echo regression sample still crashes exit 1 }修复前这条脚本应该失败修复后应该通过。这就是一个最小可用的回归闭环。8.3 负责任上报如果问题在 FFmpeg 官方代码中确认存在建议先搜索是否已有公开 CVE 或 issue避免重复上报。上报时附上触发崩溃的最小样本文件FFmpeg 版本和 commit 号编译选项和 Sanitizer 配置崩溃调用栈和运行环境信息。注意不要提前公开可被利用的完整攻击细节先让维护者有足够时间修复。9. 用 CI 做持续模糊测试模糊测试不是跑一次就结束的工作。正确的做法是把它接进 CI让每次 FFmpeg 版本更新后都能自动跑一段时间的 fuzz。下面是一个 GitLab CI 伪配置核心思路是定时跑 保存产物。stages: - fuzz fuzz_ffmpeg: stage: fuzz script: - ./configure --ccclang --enable-ossfuzz --disable-doc - make -j$(nproc) - ./run_fuzz.sh artifacts: paths: - output/ rules: - if: $CI_PIPELINE_SOURCE scheduleCI 环境首要注意的是资源限制不要在 CI runner 上无限跑建议限制-runs和运行时间。比如每次调度跑 30 分钟保留语料库和崩溃产物供人工分析。10. 资源占用与性能观察10.1 性能观察方法模糊测试是典型的 CPU 密集型任务。运行 fuzzer 时用htop观察多核使用情况htop如果启用了-fork4或类似并发参数会看到多个 fuzzer 进程各占一个 CPU 核心。并行数量建议与 CPU 核心数匹配不要盲目开满否则内存会被快速耗尽。llvm 编译期间也需要观察内存占用链接大对象时常出现内存瞬间飙升。10.2 影响测试速度的因素语料库体积语料越大变异空间越大单轮耗时也可能变长-max_len单样本越长解码耗时越高Sanitizer 类型ASan 和 UBSan 都会拖慢执行速度但这是必须付出的成本目标代码路径容器解析比纯解码更快解码器如果涉及复杂算法会更慢。10.3 如何降低资源占用第一次先跑-runs10000验证链路通不通用-max_len限制样本长度只针对当前关心的 decoder 或 demuxer 编译对应 target并发进程数不要超过nproc - 1给系统留余量。11. 常见问题与排查方法问题现象可能原因排查方式解决方案configure 失败缺少 yasm/nasm 或 pkg-config查看 configure 输出的错误日志安装 yasm、nasm、pkg-config 后重试fuzz target 编译不生成未开启--enable-ossfuzz检查 configure 选项重新配置时加上该选项target_dec_fuzzer -help无输出clang 与 libFuzzer 版本不匹配检查 clang 版本统一 clang 版本或指定 clang 路径跑很久不崩溃语料太单薄或 target 覆盖路径不深观察覆盖率增长增加有效语料切换到更相关的 target崩溃样本单独运行不复现运行时环境和 fuzz 时不一致对比编译参数和运行目录使用相同 Sanitizer 参数和相同库文件UBSan 输出没有除零栈未开启-fsanitizeundefined检查 CFLAGS重新编译并启用 UBSan内存被快速耗尽并行进程太多或语料爆炸查看 htop 和语料目录大小降低-fork数清理膨胀语料只崩溃一次但无法稳定复现多线程竞争或输入触发路径不稳定多次运行同一样本结合 gdb 和多线程调试12. 最佳实践与使用建议12.1 先把基础链路跑通第一次使用不要追求快速发现漏洞。先编译一个能运行的 fuzz target用 1 个正常样本和 1 个损坏样本验证流程正常样本不崩溃、损坏样本能触发异常或退出。这条链路跑通后后面的所有批量测试才有意义。12.2 分开跑不同 SanitizerASan 和 UBSan 可以同时开启但有些场景下最好分开跑。比如 UBSan 开启后可能先报未定义行为导致 ASan 触发的越界问题被掩盖。更稳妥的做法是准备两套编译产物分别开启# ASan 构建 export CFLAGS-fsanitizeaddress -g -O1 -fno-omit-frame-pointer # UBSan 构建 export CFLAGS-fsanitizeundefined -g -O1 -fno-omit-frame-pointer12.3 保留语料库并归档崩溃样本语料库是长期资产。建议把有效的语料库提交到 Git 仓库或对象存储定期更新。崩溃样本按日期和类型分目录保存方便后续比对。12.4 借助 vibecode 但保持人工审查AI 辅助生成工具能显著提升脚本产出效率但以下内容必须人工确认fuzzer 的编译参数和 sanitizer 配置崩溃样本的唯一性和去重逻辑修复补丁的正确性上报前是否涉及敏感数据或未授权信息。AI 最适合做“写脚本初稿、解析日志、生成报告模板”这类工作不适合做“判断这是不是漏洞”的最终决策。12.5 合规提醒只对你有权测试的目标执行模糊测试。FFmpeg 开源项目本地测试没有问题但不要用同样的工具去扫公网未授权服务。崩溃样本如果来自用户上传的媒体文件要先脱敏再决定是否用于测试。13. 总结与下一步整个链路可以概括为用 vibecode 方式快速生成测试辅助脚本构建带 Sanitizer 的 FFmpeg fuzz 目标运行 libFuzzer 批量测试捕获除零崩溃样本通过 UBSan 和 gdb 定位到具体解析函数最小化样本后写入回归测试。这篇文章里最容易踩的坑不是工具本身难用而是没开 UBSan导致除零崩溃只表现为 SIGFPE没有精确调用栈语料太少跑了很久覆盖率不动误以为“FFmpeg 很安全”收集到崩溃样本不做复现和最小化堆了一堆无法分析的文件。建议你上手后先验证一件事能否用 clang 编译出一个带 UBSan 的 FFmpeg fuzz target并成功运行一次-runs10000的批量任务。这一关过了后面的崩溃发现和定位就只是时间问题。后续可以继续把测试范围扩大到更多 demuxer、decoder甚至接入 OSS-Fuzz 的样本集把 FFmpeg 的健壮性测试真正沉淀成日常工程习惯。