发布时间:2026/8/31 17:39:46
用模糊测试挖出FFmpeg除以零漏洞:从harness到复现完整实践 最近在做一个多媒体解析相关的稳定性测试任务时我们盯上了 FFmpeg。作为使用率极高的开源音视频处理库FFmpeg 被集成在转码服务、播放器、推流工具甚至大量移动端 SDK 里。代码量大、支持格式多、历史包袱重是典型的“模糊测试友好型”目标——语法解析路径极多输入格式复杂稍有不慎就会在边界场景触发崩溃。而这次我们通过一个基于 vibecode 思路开发的模糊测试工具真的在一段视频解码路径中挖出了“除以零”的缺陷。本文会把整套过程拆开来讲从工具怎么搭、种子样本怎么选、harness 怎么写到崩溃如何复现、根因如何定位、修复补丁如何验证一次性讲清楚。读这篇文章之前你需要对 C 语言和 FFmpeg 命令行有一定了解但不要求有模糊测试经验。我会把模糊测试里最容易劝退新手的几个概念比如覆盖率、崩溃样本去重、ASAN/UBSAN 编译、harness 数据流用实例说明白。跟着走一遍后你不仅能上手跑通这个流程还能把同样的方法复用到其他解析类组件上比如图片解码器、压缩库、协议解析器甚至是自己项目里的配置文件解析模块。1. 背景为什么 FFmpeg 这种老牌项目还会出现“除以零”1.1 FFmpeg 的解析链路远比想象中复杂很多人对 FFmpeg 的第一印象是命令行工具ffmpeg -i 1.m4s -c copy 1.mp4 ffmpeg -y -i input.mov -vf fadetin:st0:d1 output.mp4 ffmpeg -i playlist.m3u8 -c copy output.mp4 ffmpeg -re -i stream.flv -c copy -f flv rtmp://127.0.0.1/live/stream这些命令用起来很顺但底层做的事情比表面看到的复杂得多。以m3u8 转 mp4为例FFmpeg 需要先解析 m3u8 索引文件再按顺序拉取分片每个分片可能来自不同的容器格式再统一交给 demuxer 解封装之后是 decoder 解码、filter 处理、encoder 编码最后 muxer 封装成 mp4。任何一个环节拿到异常输入都可能走进未覆盖的代码路径。容器格式和编码格式的数量极其庞大MP4、FLV、TS、MKV、AVI、MOVH.264、H.265、VP9、AV1、AAC、MP3、Opus……每个格式都有独立的解析器。而解析器为了性能大量使用位运算、宏定义、查表法极少做冗余的运行时校验。这导致一个很现实的结果FFmpeg 的健壮性严重依赖输入是否“正常”。一旦输入文件在某个关键字段上出现非预期值就可能触发空指针解引用、越界读、整数溢出以及本文要重点讨论的整数除以零。1.2 除以零听起来低级实际很致命在 C 语言里整数除法和浮点数除法不一样。浮点数除以零在 IEEE 754 标准下会得到inf或nan程序不一定会崩溃但整数除以零是未定义行为在 x86 平台上通常表现为SIGFPE进程直接收到浮点异常信号退出。对于服务端转码任务这种崩溃意味着处理该媒体文件的 worker 直接挂掉。如果 worker 没有做进程隔离崩溃可能影响整个服务实例。对于客户端播放器则可能直接导致播放器闪退。最麻烦的是这类崩溃样本经过 fuzz 工具压缩后往往只有几十 KB用户只要下载一个恶意音视频文件播放器就会被“打死”。所以“除以零”并不是一个可以一笑而过的低级问题它属于可用性漏洞在 CVE 里也有不少收录案例。1.3 为什么需要模糊测试FFmpeg 的解析逻辑分支非常多靠人工阅读代码去准备边界样例覆盖率远远不够。比如一个 MP4 的stszbox 中sample_size字段为 0又或者一个 H.264 SPS 里的pic_width_in_mbs_minus1出现异常这类组合人工很难逐一构造。模糊测试通过不断变异输入数据配合插桩反馈能在短时间内探索大量未知路径把“人类想不到的坏输入”自动找出来。用更直白的话说模糊测试是在用机器暴力对抗解析器里的“假设”。解析器假设字段应该是什么范围、什么对齐方式、什么比例关系fuzzer 就偏把字段改成 0、改成负数、改成极大值然后看哪个假设没有成立。2. 核心概念速览vibecode、模糊测试与除以零2.1 vibecode 指的是什么vibecodevibe coding是最近很流行的一种编程方式核心思路是用自然语言描述需求让 AI 辅助生成代码、命令和脚本。它不是一种具体工具而是一种工作流。我们这次实验里“基于 vibecode 的模糊测试工具”指的是先用自然语言向 AI 描述“我要给 FFmpeg 的 demuxer 做模糊测试需要生成一个 libFuzzer harness 和对应构建脚本”再由 AI 快速生成初版代码我们人工审查和调整最终形成可运行的测试工具。有人可能会质疑 AI 生成的代码质量。但从工程效率角度看fuzz harness 本身是重复性很高的胶水代码AI 先写初版、人类再改确实能把原本需要一两天的准备时间压缩到一两个小时。当然AI 生成的代码必须在本地编译、运行并且经过人工 review不能直接丢到生产环境里去跑。2.2 模糊测试的基本循环所有覆盖率引导的模糊测试工具无论名字叫什么核心循环都是下面几步准备一批种子样本seed corpus。从语料库中取出一个样本。对样本字节做变异翻转 bit、替换字节、插入数据、拼接其他样本等。把变异后的数据交给被测程序执行。如果程序崩溃或者触发 sanitizer 告警保存这个样本。如果本次执行覆盖到了新的代码路径覆盖率提升把样本加入语料库。回到第 2 步无限循环。这个循环的关键在于第 6 步。没有覆盖率反馈的 fuzz 是盲目随机测试效率极低有了覆盖率反馈fuzzer 才会逐渐“进化”出更加刁钻的输入。主流工具如 libFuzzer、AFL 都支持覆盖率引导。下文示例里我们以 clang/LLVM 自带的 libFuzzer 为例因为它在和 ASAN/UBSAN 配合时开箱即用适合快速验证流程。AFL 的源码插桩模式也很好但配置步骤更多本文不展开。2.3 sanitizer 与“除以零”检测要抓到“除以零”只靠程序崩溃现象是不够的。有些除以零会因为上层判断提前 return 而无法稳定崩溃甚至在不开启优化时表现不一致。所以我们需要在编译期插入动态检测工具也就是 sanitizer。这里主要用到两个AddressSanitizer检测堆越界、栈越界、use-after-free、double-free 等内存问题简写为 ASAN。UndefinedBehaviorSanitizer检测未定义行为包括整数除以零、无符号整数溢出、移位越界、空指针访问、对齐错误等简写为 UBSAN。两个可以同时开启。编译命令里会看到-fsanitizeaddress,undefined这就是同时启用 ASAN 和 UBSAN。开启之后一旦执行到除以零UBSAN 会在输出日志里明确打印出是哪一行代码、哪个表达式触发了问题这比人工看 core dump 要直观得多。3. 环境准备与工具链3.1 构建带插桩的 FFmpeg首先我们需要一份 FFmpeg 源码。不建议直接用系统包管理器安装的版本比如 CentOS 7 上常用的yum install ffmpeg因为发行版默认编译参数通常没有 enable sanitizer也没开启我们需要的覆盖率插桩。为了做 fuzz我们要从源码自行构建。环境建议使用 Ubuntu 22.04 或更新的 Linux 发行版需要安装以下基础工具sudo apt update sudo apt install -y build-essential clang llvm \ pkg-config nasm yasm \ git python3然后拉取 FFmpeg 源码并切换到一个稳定的 release 分支git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg cd ffmpeg git checkout n6.1这里以常见 release 版本为例具体分支按你的需要选择。接下来配置编译参数。为了覆盖更多解析路径我们尽量把常见的 demuxer、decoder 都编进去但不要编进所有第三方库否则会引入外部依赖影响问题定位。./configure \ --disable-shared \ --enable-static \ --ccclang \ --cxxclang \ --enable-debug3 \ --disable-stripping \ --extra-cflags-fsanitizeaddress,undefined -g -O1 \ --extra-ldflags-fsanitizeaddress,undefined \ --enable-libfuzzer--enable-libfuzzer是 FFmpeg 官方为模糊测试预留的配置开关。它主要影响 FFmpeg 自带的 fuzz 目标编译方式。也可以不使用这个开关而是单独写 harness 编译本文后面采用后者。配置完成后执行make -j$(nproc)构建时间取决于机器性能通常在几分钟到十几分钟。构建完成后可以在ffmpeg/目录下看到ffmpeg、ffprobe可执行文件。先跑一下最基础的验证./ffmpeg -version如果输出版本信息说明基础构建没问题。这里提示一点ffmpeg命令里的-y参数表示“输出文件已存在时直接覆盖”对应的人机交互场景是避免重复询问。后面在自动化脚本里跑测试时经常需要加上-y否则进程会因为等待输入而挂起。3.2 准备模糊测试工具因为我们选的是 libFuzzer 路线不需要额外安装大型 fuzz 平台。检查 clang 是否支持-fsanitizefuzzerclang -fsanitizefuzzer -v 21 | head -20如果能正常执行说明当前 clang 自带 libFuzzer。如果提示找不到fuzzer链接库可能需要安装libfuzzer开发包或者切换 clang 版本。另外建议准备一个崩溃样本管理目录比如mkdir -p ~/ffuzz-project/{seed,corpus,crashes,scripts}目录结构说明如下seed/种子样本人工收集的正常媒体文件。corpus/fuzzer 迭代中新增的有价值样本。crashes/触发崩溃的样本按时间或崩溃特征存放。scripts/构建、运行、分析脚本。3.3 种子样本库怎么准备种子样本是 fuzz 的起点。种子越贴近真实输入fuzzer 的探索效率越高。对 FFmpeg 来说我们可以从以下几个方面收集从测试素材网站下载合法的公开测试视频比如 MP4、FLV、TS、MKV 各准备几个。使用 FFmpeg 自己转码生成小文件ffmpeg -f lavfi -i testsrcduration1:size320x240:rate15 \ -f lavfi -i sinefrequency440:duration1 \ -c:v libx264 -c:a aac -shortest seed/base.mp4从真实业务场景中抽取匿名化后的失败文件。很多线上播放器崩溃本质就是某个文件在某个分辨率、码率、时长组合下触发了兼容问题。如果这些文件不涉及版权和隐私放入种子库能显著提升针对性。需要注意种子样本必须来源合法、可公开测试。不要拿涉及隐私或商业版权的视频做 fuzz 分发。模糊测试会让样本发生不可控的变异变异后的文件既可能不代表原视频内容也可能无法保护原素材的版权所以用公开测试素材最安全。4. 实战从零跑一次针对 FFmpeg 解析器的模糊测试4.1 编写 fuzz harnessharness 是连接 libFuzzer 与 FFmpeg 的桥梁。libFuzzer 会不断调用我们定义的LLVMFuzzerTestOneInput函数把变异后的数据作为字节数组传进来。我们需要在这个函数里把数据构造为 FFmpeg 能解析的输入并执行解封装、流信息探测、解码尝试等操作。先创建一个文件ffmpeg_harness.c内容如下#include stdint.h #include stddef.h #include string.h #include libavformat/avformat.h #include libavcodec/avcodec.h #include libavutil/mem.h typedef struct { const uint8_t *data; size_t size; size_t pos; } BufContext; static int buf_read(void *opaque, uint8_t *buf, int buf_size) { BufContext *bc (BufContext *)opaque; if (bc-pos bc-size) { return AVERROR_EOF; } int remain (int)(bc-size - bc-pos); int n buf_size remain ? buf_size : remain; memcpy(buf, bc-data bc-pos, n); bc-pos n; return n; } static int64_t buf_seek(void *opaque, int64_t offset, int whence) { BufContext *bc (BufContext *)opaque; if (whence AVSEEK_SIZE) { return (int64_t)bc-size; } int64_t new_pos; switch (whence) { case SEEK_SET: new_pos offset; break; case SEEK_CUR: new_pos (int64_t)bc-pos offset; break; case SEEK_END: new_pos (int64_t)bc-size offset; break; default: return -1; } if (new_pos 0 || new_pos (int64_t)bc-size) { return -1; } bc-pos (size_t)new_pos; return new_pos; } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size 16) { return 0; } BufContext bc { data, size, 0 }; unsigned char *avio_buffer av_malloc(32768); if (!avio_buffer) { return 0; } AVIOContext *avio avio_alloc_context( avio_buffer, 32768, 0, bc, buf_read, NULL, buf_seek); if (!avio) { av_free(avio_buffer); return 0; } AVFormatContext *fmt_ctx avformat_alloc_context(); if (!fmt_ctx) { avio_context_free(avio); return 0; } fmt_ctx-pb avio; fmt_ctx-flags | AVFMT_FLAG_CUSTOM_IO; int ret avformat_open_input(fmt_ctx, fuzz_input, NULL, NULL); if (ret 0) { avformat_close_input(fmt_ctx); return 0; } ret avformat_find_stream_info(fmt_ctx, NULL); if (ret 0) { avformat_close_input(fmt_ctx); return 0; } // 尝试打开第一路视频流或音频流 for (unsigned int i 0; i fmt_ctx-nb_streams; i) { AVStream *st fmt_ctx-streams[i]; const AVCodec *decoder avcodec_find_decoder(st-codecpar-codec_id); if (!decoder) { continue; } AVCodecContext *codec_ctx avcodec_alloc_context3(decoder); if (!codec_ctx) { continue; } if (avcodec_parameters_to_context(codec_ctx, st-codecpar) 0) { avcodec_free_context(codec_ctx); continue; } if (avcodec_open2(codec_ctx, decoder, NULL) 0) { avcodec_free_context(codec_ctx); continue; } avcodec_free_context(codec_ctx); } avformat_close_input(fmt_ctx); return 0; }这个 harness 的核心逻辑是把 fuzzer 输入的字节流包装成自定义AVIOContext然后让 FFmpeg 把它当作一个媒体文件去打开。这样 fuzzer 的变异数据可以直接驱动avformat_open_input、avformat_find_stream_info以及解码器初始化路径覆盖范围比较广。代码里需要注意几个点size 16的提前返回是为了过滤太短的样本同时避免大量无意义的空解析但这本身不是必须的可以按需调整。buf_seek的AVSEEK_SIZE分支需要保留某些封装格式在探测时会询问输入总长度。解码器没有真正解码帧只做了avcodec_open2。如果想覆盖解码器的像素处理函数需要进一步读取AVPacket并调用avcodec_send_packet。但这会增加运行耗时导致 fuzzer 每秒执行次数下降。第一次跑可以先做浅层探测后面再针对性加深。4.2 编译 harness 与运行我们把 harness 与 FFmpeg 静态库一起编译。在 FFmpeg 源码目录外执行clang -g -O1 -fsanitizeaddress,undefined -fsanitizefuzzer \ -I../ffmpeg -I../ffmpeg/libavformat -I../ffmpeg/libavcodec -I../ffmpeg/libavutil \ ffmpeg_harness.c \ ../ffmpeg/libavformat/libavformat.a \ ../ffmpeg/libavcodec/libavcodec.a \ ../ffmpeg/libavutil/libavutil.a \ -lm -lz -pthread \ -o ffmpeg_fuzzer这里要特别提醒FFmpeg 编译时如果启用了大量外部库比如libx264、libmp3lame、libvpx链接时还需要额外加对应的-l参数。为了简化前面配置 FFmpeg 时我们可以先不 enable 这些外部库。命令行转码功能够用但 fuzz 阶段主要关注自带解析器和解码器不依赖第三方编码器。编译完成后先用种子库跑一次./ffmpeg_fuzzer -timeout10 -rss_limit_mb2048 \ -max_len1048576 \ ./seed ./corpus参数说明-timeout10单个用例执行超过 10 秒就判定超时。-rss_limit_mb2048内存上限 2GB超出即跳过。-max_len1048576输入数据最大长度限制在 1MB避免超大文件拖慢速度。./seed ./corpus第一个目录是种子第二个目录是语料库输出。libFuzzer 会先加载 seed 里的所有文件然后开始变异迭代。跑起来后终端会不断刷新执行次数、覆盖率、崩溃数。输出类似#1000000 NEW cov: 5423 ft: 8921 corp: 128/512Kb lim: 1048576 exec/s: 1234 rss: 512Mb看到NEW说明发现了新的覆盖路径corpus 会增长。这属于正常现象。如果想要更快发现特定解析器的问题可以修改 harness只指定一种输入格式比如强制把所有输入当作裸 H.264 流解析AVInputFormat *fmt av_find_input_format(h264); ret avformat_open_input(fmt_ctx, fuzz_input, fmt, NULL);4.3 观察崩溃一个“除以零”样本的诞生在我们的某次运行中fuzzer 在运行了大约几百万次迭代后终端出现了类似下面的输出ERROR: libFuzzer: deadly signal The signal is caused by a WRITE memory access. ... artifact_prefix./crashes/; Test unit written to ./crashes/ffmpeg-xxx-12345678更具体地因为启用了 UBSAN还会在崩溃前看到类似日志runtime error: division by zero #0 0x... in get_ue_golomb .../libavcodec/h264dec.c:... #1 0x... in ...这通常意味着 fuzzer 发现了一个可以稳定触发整数除以零的输入。libFuzzer 会把最小化后的样本保存到crashes目录。文件名通常带有时间戳和 hash我们需要保存好这个文件接下来就是分析和修复阶段。需要提醒的是发现崩溃不代表马上就能定位根因。崩溃日志里的调用栈只是执行到异常点的路径真正的“罪魁祸首”可能是在更早的代码里一个字段被解析成了 0或者一个宏展开时漏掉了检查。下面我们看如何系统地分析。5. 崩溃样本分析把“除以零”定位到具体代码5.1 用 sanitizer 日志还原崩溃现场先用最小复现命令确认崩溃./ffmpeg_fuzzer -runs1 ./crashes/ffmpeg-xxx-12345678这个命令会让 fuzzer 只跑一次指定样本。如果仍然崩溃终端会打印完整 sanitizer 栈。我们需要记录三样东西异常类型比如runtime error: division by zero。崩溃函数比如某个ff_*或者get_ue_golomb。源码文件和行号。接着用 gdb 或 lldb 做更细的调试gdb --args ./ffmpeg_fuzzer -runs1 ./crashes/ffmpeg-xxx-12345678 (gdb) r (gdb) bt在崩溃处执行bt可以打印完整调用栈。栈会告诉我们这个除以零出现在哪一层解析逻辑里是 demuxer 解析容器结构时还是 decoder 解析码流参数时。5.2 从“除以零”反推上游假设假设崩溃栈指向的是 H.264 的指数哥伦布编码解析函数比如get_ue_golomb。这个函数通常用于读取Exp-Golomb编码的无符号整数它的逻辑是先统计前导零的数量再读取若干数据位最后做(1 leading_bits) - 1 ...这样的计算。如果输入数据经过变异后让某个位数计算结果变成 0而调用方拿这个 0 当除数就会触发除以零。但根因往往不止一处。去看源码时会发现很多解析函数在开头会做类似“如果值等于 0 就 return”的检查但检查可能放在第一个除法之前却没有覆盖到第二个除法。或者某个width / n里n来自色度采样格式字段而色度采样字段在进入该函数之前没有被校验。这就是模糊测试的价值它直接帮你找到“检查缺失”的那一行而不是让你去穷举所有输入组合。5.3 修复思路与验证修复“除以零”通常不是简单地在LLVMFuzzerTestOneInput里捕获信号而是要回到 FFmpeg 源码中在真正的除法之前加上合法性判断。以常见的av_image_get_buffer_size相关路径为例修复方式可能是在函数入口校验width和height不能为 0同时校验分母相关的参数if (width 0 || height 0 || linesize_align 0) { return AVERROR(EINVAL); }对于某些需要保持 ABI 稳定的函数不能用assert因为 release 版本会禁用 assert。正确做法是返回负的错误码比如AVERROR(EINVAL)让上层调用链结束。补丁完成后重新编译 FFmpeg 和 fuzzer再用同一个崩溃样本执行./ffmpeg_fuzzer -runs1 ./crashes/ffmpeg-xxx-12345678如果不再崩溃说明该样本被修复了。但这还不够更严格的做法是把崩溃样本加入回归测试语料库并让 fuzzer 继续跑一段时间确保修复没有引入新的问题。6. 常见问题与排查思路6.1 编译期常见问题问题现象常见原因解决思路clang 找不到 libfuzzer 链接库clang 版本过旧或缺少 fuzzer 运行时更新 clang或单独安装libfuzzer-XX-devFFmpeg configure 报缺少 yasm/nasm汇编器未安装sudo apt install -y nasm yasm链接时一堆 undefined referenceFFmpeg 启用了外部库但链接参数没补全重新 configure 关闭外部库或按错误提示补-l参数运行时提示 ASAN runtime does not come firstclang 编译的 .so 被其他运行时抢先加载编译时加-static-libasan或设置环境变量6.2 fuzz 运行期常见问题问题现象常见原因解决思路长时间没有 NEW 覆盖种子样本过于单一或 harness 只走了少量路径补充不同格式的种子或调整 harness 强制指定 demuxer/decoder执行速度很慢sanitizer 开销 解码器avcodec_open2太频繁减少每轮avcodec_open2调用先只跑 demuxer或用-runs控制单轮时间崩溃样本过多但都是同一个栈没有样本去重用-artifact_prefix分类或写脚本解析 sanitizer 栈顶函数名内存一直增长FFmpeg 某些路径存在资源泄漏或 corpus 无限制增长设置-rss_limit_mb定期清理 corpus或使用-reduce_inputs样本超过 1MB 后 fuzzer 明显变慢大样本变异成本高设置-max_len优先用小样本触发深层解析6.3 定位崩溃时最容易踩的坑定位崩溃时很多人会直接看最新栈帧然后去改那个函数。但对于“除以零”这类问题最新栈帧往往只负责执行除法真正的输入来源可能在几层之外。举个例子某个picture_width / crop_unit_x其中crop_unit_x来自SPS的chroma_format_idc而chroma_format_idc又来自比特流里的一段 Exp-Golomb 编码。如果直接给除法加保护确实能阻止崩溃但更好的修复方式是校验chroma_format_idc是否在合法取值范围内并且在生成crop_unit_x的地方就拦截非法值。所以我的建议是先分析完整调用链再决定在哪一层做防御。通常放在“数据进入子系统边界”的地方最合适这样既保护后续所有调用者又不需要在几十个除法点重复打补丁。7. 最佳实践把模糊测试持续集成到日常开发7.1 把 fuzz 当作回归测试的一部分一次性的 fuzz 实验意义有限真正有价值的是把它接入持续集成。每次 FFmpeg 源码或你自己的解析模块有更新时自动跑固定时长的 fuzz。例如可以用 GitHub Actions 或 GitLab CI 创建一个定时任务schedule: - cron: 0 2 * * *跑完以后如果出现崩溃样本自动把样本上传到制品库并触发 issue。这样做能尽早发现上游更新带来的兼容问题。对于 FFmpeg 这种第三方库尤其建议在自己的业务 fork 上做这个动作因为 FFmpeg 更新频繁行为差异可能直接影响到播放器和转码服务。7.2 样本库要小而精种子样本不是越多越好。很多团队一开始把大量视频文件丢进 seed结果 fuzzer 大部分时间都在解析重复的容器结构覆盖率提升并不明显。更合理的做法是每种容器格式准备 2 到 3 个最小样本每个样本控制在几 KB 到几百 KB并覆盖不同的编码组合。比如对于 MP4准备一个 H.264 AAC 的一个 HEVC AAC 的对于 FLV准备一个 H.264 直播流的一个带字幕的。语料库会随着 fuzz 过程自动增长建议定期用-merge1合并语料库删除冗余样本。合并操作会把所有样本放到一个新目录只保留覆盖新路径的有效输入。7.3 崩溃样本要保留现场信息和最小复现发现崩溃后不要只保存文件本身。建议同时保存fuzzer 运行时的参数。sanitizer 输出的完整日志。FFmpeg 的 commit hash 或版本号。构建时的 configure 参数。这样后续无论谁来复现都能保证环境一致。尤其对于“除以零”这类与编译器优化级别相关的问题不同-O级别下行为可能不一样。我们在实验时统一使用-O1配合 sanitizer这样既保留了一定优化又能确定性问题地检测未定义行为。7.4 生产环境怎么看待 fuzz 发现的崩溃需要明确一点fuzz 崩溃样本不要直接拿到生产环境去复现。生产环境没有 ASAN/UBSAN崩溃行为可能与本地不一致而且某些变异文件可能正好走了某个资源消耗极高的解码路径导致线上 CPU 飙高。所有样本都先在测试环境验证再决定是否上报给上游 FFmpeg 社区。如果你在真实业务里使用 FFmpeg还要注意几个安全习惯不要把用户上传的媒体文件直接交给不受监控的 ffmpeg 进程处理应该做进程隔离、资源限制、超时控制。使用-threads和-timeout控制转码并发和时长避免单个文件拖垮整个服务。定期从上游拉取安全修补版本或者自行维护包含关键补丁的 fork。对于基于zlmediakit拉取 RTSP 流并调用 FFmpeg 转推的场景同样要关注 FFmpeg 解析缺陷的影响。zlmediakit通常只是负责网络流接入如果拉流后直接把裸流交给 FFmpeg 解码那么一个恶意的 RTSP 源理论上可以通过异常 SPS/PPS 触发 FFmpeg 崩溃。所以即使你的上层服务没有直接解析媒体格式底层的 FFmpeg 进程健壮性仍然值得投入。7.5 借助 vibe coding 思维快速迭代我们在这次实践中的体会是vibecode 工作流特别适合 fuzz 工具的“脚手架阶段”。比如让 AI 生成一个脚本自动从 sanitizer 日志里提取崩溃点的函数名和行号再按函数聚类很快就能生成一份“崩溃热点报告”。实际上这种辅助脚本的价值不亚于 fuzz 本身因为它把“发现崩溃”和“分析崩溃”之间的时间缩短了。不过要注意AI 生成的代码必须经过严格 review尤其是涉及文件删除、命令执行、正则提取的部分很容易因为边界条件写错而产生误判。比较好的做法是AI 生成初版你用规则明确“只能输出分析结果不允许直接修改文件”再人工执行。8. 总结回到最初的问题为什么 FFmpeg 这种久经考验的项目还会出现“除以零”这种看似基础的问题因为媒体格式的种类和版本太多了解析器面对的是几乎无限的输入组合任何一环的“假设”都可能被精心构造的文件破坏。模糊测试不是银弹但它能以一种自动化的方式把“输入校验缺失”这类问题暴露出来。通过本文的完整流程你大概可以掌握以下内容使用 clang 构建带 ASAN/UBSAN 的 FFmpeg。编写 fuzz harness把内存字节流注入 FFmpeg 解封装路径。运行 libFuzzer 并理解覆盖率、语料库、崩溃样本这些核心概念。从 sanitizer 日志和调用栈定位“除以零”的根因。用最小补丁修复问题并验证回归。把 fuzz 流程固化到日常开发和持续集成中。如果你接下来想深入可以沿着两条线继续学习一条是研究 FFmpeg 官方tools/目录下的 fuzz 目标另一条是学习 AFL 的插桩模式和并行 fuzz 策略。工具本身并不神秘真正有价值的是问题定位的耐心和方法论。如果在自己的项目里也遇到“莫名其妙崩溃”的解析类问题不妨搭一套最小 fuzz 环境让机器帮你把坏输入找出来。

相关新闻

2026/8/31 17:39:46

从“超模”评价到系统化复盘:一场比赛背后的数据思维

“登峰辅助在涅槃组太超模了”——如果你最近看过这场比赛的赛后讨论,大概会对这个标题有印象。WBG 1-2 NIP,第一局里 fengyue 的辅助表现被不少人反复提起,议论的角度也很集中:从登峰组过来的辅助,到了涅槃组好像突然…

2026/8/31 17:39:46

连锁故障的本质、危害与防御:从负载容量模型到系统韧性设计

简介:本资源聚焦电力系统连锁故障(cascading failure)的建模、仿真与风险分析,面向电力系统专业本科生、研究生及电网运行研究人员,解决级联失效机理理解难、传播路径模拟缺工具、关键节点识别无数据支撑等实际问题。压…

2026/8/31 17:39:46

欧卡2座舱改造:1:1包围中控台与跷跷板实体按键DIY指南

之前群里有人晒了一套欧卡2座舱改造图,标题写着“1.60版本,1:1包围中控台跷跷板实体按键带灯光”。很多玩家在评论区感叹“还原度离谱”,但真正动手做过的朋友都知道:这种离谱不是靠买一个现成外壳就能堆出来的,而是从…

2026/8/31 17:54:48

STM32+LD2330语音识别智能垃圾桶设计:从原理图到程序全解析

简介:本资源是一套面向嵌入式初学者与电子设计爱好者的智能硬件实践方案,聚焦语音交互式垃圾分类场景,解决传统垃圾桶操作不便、分类效率低的问题。资料包共包含原理图、PCB布局文件及完整STM32源程序(C语言)&#xff…

2026/8/31 17:54:48

四大厂AI办公对比:从插件化改造到AI Native,差距在哪

四大厂都在做AI办公,但把"大模型对话框"接进文档里,和让整个办公流程因为AI而重新设计,是两回事。这次想聊一个比较扎眼的观察:飞书、钉钉、腾讯文档、WPS AI这些产品,表面上都在密集发布AI功能,…

2026/8/31 17:54:48

计算机辅助验证与形式化证明:程序员如何用工具攻克数学难题

如果最近刷到“困扰数学圈22年的难题,居然被协和实习医生解决了”这条消息,先别急着转发。这类叙事天然自带传播属性:非科班身份、体制外视角、一个漫长封闭的难题、最终被“外行”瞬间突破。但作为技术人,比转发更有价值的&#…

2026/8/31 17:54:48

Spring Boot+Vue仓库管理系统源码解析与实战部署指南

简介:这是一套面向计算机专业本科生及初级Java全栈开发者的仓库管理实战项目,基于Spring Boot后端与Vue前端构建,完整覆盖商品入库、出库、库存查询、员工管理等核心业务场景,适用于毕业设计、课程设计与期末大作业。压缩包共109个…

2026/8/31 17:49:47

用LLM缓解迁移疲劳:Spring Boot升级javax到jakarta实战

大型后端项目里,最消耗团队精力的往往不是写新功能,而是迁移。Spring Boot 从 2.7 升到 3.x,要把一批javax开头的 import 改成jakarta开头;旧服务 SDK 换代,要在几十个服务里同步改调用方式;内部组件重构包…

2026/8/31 1:05:20

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/8/31 2:14:20

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/8/31 1:41:28

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/8/31 0:07:32

STM32C5设备支持包(IAR DFP)安装指南与常见坑

上一阵子在IAR里折腾一块基于STM32C5系列的新板子,工程从STM32CubeMX导出来之后怎么都编译不过。报错信息很干脆:找不到设备描述文件。跟着错误路径去查,发现指向的是一个让我愣了一下的名字:STMicroelectronics.stm32c5xx.2.1.0.…

2026/8/31 0:07:32

STM32N657 SWO引脚矛盾:CubeMX显示PB3,数据手册为PB5

拿到STM32N657这颗料的第一天,我就撞上了一个让人原地懵圈的引脚矛盾:CubeMX里清清楚楚显示SWO在PB3,翻开数据手册的引脚说明表,却赫然写着PB5。对于一个靠SWO输出调试日志吃饭的人而言,这种"工具和手册打架"…

2026/8/31 12:44:45

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/31 9:19:59

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/31 6:53:02

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…