ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Skia 模糊测试实战指南:用 fuzz 与 libfuzzer 复现崩溃、编写 fuzzer 并驯服 OOM

Skia 模糊测试实战指南:用 fuzz 与 libfuzzer 复现崩溃、编写 fuzzer 并驯服 OOM 图形学图像处理【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images.项目地址https://gitcode.com/gh_mirrors/skia1/skia点击查看免费下载导读本文是 Skia 官方测试文档 site/docs/dev/testing/fuzz.md 的深度展开系统讲解 Skia 的模糊测试Fuzzing工作流如何用fuzz二进制复现从 ClusterFuzz / oss-fuzz 下载的崩溃用例如何基于 libfuzzer 编写新的 fuzzer 并接入构建以及三个关键的编译期宏SK_BUILD_FOR_AFL_FUZZ、SK_BUILD_FOR_LIBFUZZER、SK_BUILD_FOR_FUZZER各自的作用。读完本文你可以独立完成下载崩溃用例 → 本地复现 → 定位问题 → 编写新 fuzzer → 接入持续模糊测试的完整闭环。一、Skia 模糊测试体系概览Skia 的模糊测试分成两层一层是仓库内的fuzz驱动型 fuzzer集中在 fuzz/ 目录由 fuzz/FuzzMain.cpp 作为统一的命令行入口另一层是面向 oss-fuzz / ClusterFuzz 的 libfuzzer 风格 harness位于 fuzz/oss_fuzz/每个文件实现LLVMFuzzerTestOneInput入口。两者共享同一套Fuzz数据消费类与Fuzzable注册机制其中 libfuzzer harness 在构建时通过 BUILD.gn 中的libfuzzer_app模板自动附加fuzz/Fuzz.cpp与fuzz/FuzzCommon.cpp见 gn/fuzz.gni 中的skia_base_fuzzer_srcs。顶层fuzz入口test_app(fuzz)见 BUILD.gn把 fuzz/ 下的专项 fuzzer 与 fuzz/oss_fuzz/ 下的 oss-fuzz harness 全部编译进同一个可执行文件覆盖图像编解码、SKP/TextBlob 反序列化、SkSL 编译、路径运算、区域运算、字体描述符、COLRv1、Skottie、SVG DOM、API 级绘制调用等数十个方向。二、使用fuzz二进制复现崩溃2.1 构建先保证可复现性复现前先按 site/docs/user/build.md 完成 Skia 构建。官方文档特别强调很多 fuzz 用例只有在 ASAN 或 MSAN 构建下才能复现因此强烈建议先配置 sanitizer 构建具体步骤见 site/docs/dev/testing/xsan.md其中包含 ASAN / MSAN / TSAN 三套完整的args.gn示例MSAN 还需要单独下载或编译 MSAN 插桩版 libc。为了让复现结果对机器与平台差异不敏感构建时应在args.gn中加入以下参数skia_use_fontconfigfalse skia_use_freetypetrue skia_use_system_freetype2false skia_use_wuffstrue skia_enable_skottietrue skia_enable_fontmgr_custom_directoryfalse skia_enable_fontmgr_custom_embeddedfalse skia_enable_fontmgr_custom_emptytrue这些参数的含义与作用参数值作用skia_use_fontconfigfalse关闭系统字体发现避免宿主机器字体环境差异skia_use_freetypetrue启用 FreeType 字体栅格化后端skia_use_system_freetype2false使用仓库内置的 FreeType而非系统版本保证版本一致skia_use_wuffstrue启用 Wuffs 图像解码器用于 GIF 等格式的快速、内存安全解码skia_enable_skottietrue启用 SkottieLottie 动画渲染模块否则skottie_json类型不可用skia_enable_fontmgr_custom_directoryfalse关闭从目录加载字体的 FontMgr 后端skia_enable_fontmgr_custom_embeddedfalse关闭内嵌字体后端skia_enable_fontmgr_custom_emptytrue启用空字体管理器提供确定性的字体环境构建完成后生成的可执行文件路径类似out/ASAN/fuzz。2.2 一条命令复现从 ClusterFuzz 或 oss-fuzz 下载崩溃用例testcase后复现只需一条命令out/ASAN/fuzz -b /path/to/downloaded/testcase其中-b--bytes接收一个文件或目录的路径传入文件时使用文件内容作为 fuzz 输入字节传入目录时目录内所有文件会被逐个作为 fuzz 输入执行见 fuzz/FuzzMain.cpp 中sk_isdir分支的目录遍历逻辑。fuzz二进制会尽力根据 testcase 文件名猜测类型/名称。其自动检测逻辑在try_auto_detectfuzz/FuzzMain.cpp中实现匹配两类命名模式ClusterFuzz 风格clusterfuzz-testcase(-minimized)?-type-digitsSkia 本地 fuzzer 风格(api-)?name-hex其中带api-前缀的会被映射为api类型并提取 API 名称。类型名通过两张映射表翻译cf_api_map把api_*前缀映射到具体的 API 名称如api_raster_n32_canvas→RasterN32Canvascf_map把 ClusterFuzz 的 fuzzer 名映射到 Skia 内部类型名如image_filter_deserialize→filter_fuzz、textblob_deserialize→textblob。2.3 手动指定类型与名称自动检测失败或需要定向复现时可手动提供类型-t/--type与名称-n/--nameout/ASAN/fuzz -t filter_fuzz -b /path/to/downloaded/testcase out/ASAN/fuzz -t api -n RasterN32Canvas -b /path/to/downloaded/testcase第二条命令中-t api表示走 API 级 fuzzer 通道fuzz_api-n RasterN32Canvas指定具体的 API。在 fuzz/FuzzMain.cpp 的实现中fuzz_api遍历sk_tools::RegistryFuzzable注册表按名称匹配后调用对应的fn(Fuzz*)回调未匹配时打印全部可用名称。2.4 枚举所有类型与名称out/ASAN/fuzz --help # 列出全部类型 out/ASAN/fuzz -t api # 列出全部 API 名称--help输出的类型清单硬编码在g_type_message中fuzz/FuzzMain.cpp当前包含android_codec、animated_image_decode、api、color_deserialize、colrv1、filter_fuzz、image_decode、image_decode_incremental、image_mode、image_scale、json、path_deserialize、region_deserialize、region_set_path、skdescriptor_deserialize、skmeshspecialization、skottie_json需SK_ENABLE_SKOTTIE、skp、skruntimeblender、skruntimecolorfilter、skruntimeeffect、sksl2glsl、sksl2metal、sksl2pipeline、sksl2spirv、sksl2wgsl、svg_dom需SK_ENABLE_SVG、textblob等。-t api未带-n时print_api_names会遍历注册表打印所有Fuzzable名称。2.5 崩溃不出现时增加循环次数某些崩溃需要多次执行才会触发例如涉及随机状态或特定时序的路径。此时追加--loops标志out/ASAN/fuzz -b /path/to/downloaded/testcase --loops times-to-run在 fuzz/FuzzMain.cpp 中loopCount std::max(FLAGS_loops, 1)即每个输入至少执行一次传入文件或目录时都会在for (int i 0; i loopCount; i)循环内反复运行fuzz_file。补充一个实用选项-d/--dump见 fuzz/FuzzMain.cpp。当 fuzzer 类型会生成位图如image_scale、image_mode时可把结果以 PNG 导出例如out/ASAN/fuzz -t image_scale -b case.bin -d out.png便于肉眼检查解码产物是否符合预期。三、用 libfuzzer 编写新 fuzzerlibfuzzer 是编写新 fuzzer 最便捷的方式也是 Skia 在 oss-fuzz 上运行 fuzzer 的方式。3.1 fuzzer 入口 API你的 fuzzer 入口必须实现以下 C API 签名extern C int LLVMFuzzerTestOneInput(const uint8_t*, size_t);仓库内 fuzz/oss_fuzz/ 的每个文件都遵循这一约定且通常用#if defined(SK_BUILD_FOR_LIBFUZZER)包裹入口便于同一份源码既能在fuzz驱动模式下被调用、也能作为独立 libfuzzer harness 链接。例如 fuzz/oss_fuzz/FuzzImage.cpp#if defined(SK_BUILD_FOR_LIBFUZZER) extern C int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size 10240) { return 0; } FuzzImageDecode(data, size); return 0; } #endif注意这里对输入大小做了上限约束超过 10240 字节直接返回这是防止极端大输入拖慢模糊测试的常见手法。harness 核心逻辑FuzzImageDecode则演示了典型流程SkImages::DeferredFromEncodedData从输入构造SkImage在 128×128 的 raster 表面SkSurfaces::Raster上drawImage渲染。3.2 安装 Clang 与 libfuzzer先安装 Clang 与 libfuzzer 运行时例如在 Debian/Ubuntu 系sudo apt install clang-10 libc-10-dev libfuzzer-10-dev安装后即可在编译命令中使用-fsanitizefuzzer该 flag 会同时注入 libfuzzer 运行时与插桩。3.3 配置 GN 参数在args.gn中设置编译器与 sanitizercc clang-10 cxx clang-10 sanitize fuzzer extra_cflags [ -DSK_BUILD_FOR_LIBFUZZER, # 启用 fuzzer 约束见下文Fuzzing Defines -O1 # 优化级别按需调整 ] ...sanitize fuzzer与-fsanitizefuzzer是配套的前者配置 Skia 构建系统后者由 Clang 驱动实际插桩。extra_cflags中必须包含-DSK_BUILD_FOR_LIBFUZZER因为它会联动打开SK_BUILD_FOR_FUZZER从而在 Skia 内部启用耗时/内存守卫见第四节。3.4 构建 Skia 与 fuzzerninja -C out/libfuzzer skia clang-10 -I. -O1 -fsanitizefuzzer fuzz/oss_fuzz/whatever.cpp out/libfuzzer/libskia.a先构建静态库libskia.a再手工把 fuzzer 入口源文件链接进可执行文件。这种库 harness的拆分方式与 BUILD.gn 中libfuzzer_app模板做的事一致该模板为每个 target 自动追加fuzz/Fuzz.cpp、fuzz/FuzzCommon.cpp注入SK_BUILD_FOR_LIBFUZZER与SK_BUILD_FOR_FUZZER两个宏并在skia_use_libfuzzer_defaults开启时添加-fsanitizefuzzer的 cflags/ldflags。libfuzzer_app的实例化列表就在其后BUILD.gn包括region_deserialize、image_filter_deserialize、region_set_path、textblob_deserialize、path_deserialize、image_decode、animated_image_decode、api_create_ddl等可作为你添加新 fuzzer 的现成模板参照。3.5 运行./a.outlibfuzzer 可执行文件支持交互式模糊测试默认持续变异输入并输出覆盖率统计也支持传入语料库目录或具体种子文件。想验证 harness 本身是否健康可以先喂入几个合法输入再运行数十秒观察是否有崩溃与覆盖率增长。3.6 编写驱动型 fuzzer 的备选姿势除了 libfuzzer 入口Skia 还提供了一套驱动型 API适合逻辑较复杂的 fuzzer通过DEF_FUZZ(name, fn)宏注册Fuzzable见 fuzz/Fuzz.h即可被fuzz -t api -n name调度。Fuzz类fuzz/Fuzz.h把输入字节流当作伪随机源提供next()、nextRange()限定区间、nextEnum()、nextN()、nextBool()等模板方法它还内置了signalBug()在 fuzzer 判断输入触发了 bug 时主动raise(SIGSEGV)让模糊测试框架记录崩溃。这种设计让 fuzzer 代码不再直接依赖LLVMFuzzerTestOneInput同一个逻辑可复用于多种测试框架。四、Fuzzing Defines三个关键编译宏文档列出了三个帮助 fuzzer 更高效的宏它们的核心目标是避免 OOM、避免无谓的慢代码路径从而让模糊测试的每一次迭代都更有价值// afl-fuzz 模糊测试必需防止 OOM 引入噪音。 SK_BUILD_FOR_AFL_FUZZ // libfuzzer 模糊测试必需。 SK_BUILD_FOR_LIBFUZZER // 该宏在认为某条代码路径将耗时过长或占用过多内存时加入守卫并中止执行。 // 当上面两个宏任一被定义时本宏默认同时被设置。 SK_BUILD_FOR_FUZZER继承关系与含义SK_BUILD_FOR_LIBFUZZER编译 fuzzer 时手动加-DSK_BUILD_FOR_LIBFUZZER见 3.3 节或通过libfuzzer_app模板自动注入BUILD.gn。它标记当前构建服务于 libfuzzer并联动打开SK_BUILD_FOR_FUZZER。SK_BUILD_FOR_AFL_FUZZ面向 afl-fuzz 的同类标记同样联动打开SK_BUILD_FOR_FUZZER。SK_BUILD_FOR_FUZZER真正的行为开关。在 src/ 下被广泛引用例如 src/core/SkImageFilter.cpp、src/core/SkMallocPixelRef.cpp、src/effects/Sk1DPathEffect.cpp、src/pathops/SkOpCoincidence.cpp、src/utils/SkDashPathPriv.h 等在这些位置Skia 对可能跑很久、吃很多内存的分支如复杂路径运算、大分配、深递归提前中止避免 fuzzer 把时间浪费在注定无果的输入上。一个直接可见的效果在SK_BUILD_FOR_FUZZER下许多资源分配会走尝试分配、失败即返回的路径而非直接断言例如 fuzz/oss_fuzz/FuzzImage.cpp 中SkSurfaces::Raster返回nullptr时 harness 直接返回false而不是崩溃。这正是用守卫避免 OOM 噪音的具体体现。对照关系fuzz驱动二进制test_app(fuzz)与 libfuzzer harnesslibfuzzer_app分别由 BUILD.gn 与 BUILD.gn 定义前者默认不带 fuzzer 宏用于本地定向复现后者强制注入SK_BUILD_FOR_LIBFUZZERSK_BUILD_FOR_FUZZER用于持续模糊测试。理解这一点有助于判断为什么同一个 fuzzer 在两种模式下行为略有不同。五、完整工作流总结与排错提示复现崩溃的最小流程按 site/docs/user/build.md 构建 Skia崩溃不现身时改用 ASAN/MSAN 构建参考 site/docs/dev/testing/xsan.md并在args.gn中加入第二节的 8 项可复现性参数。out/ASAN/fuzz -b /path/to/testcase直接复现失败则用-t/-n手动指定或--loops增加执行次数。用--help与-t api枚举可用类型/名称确认目标 fuzzer 确实被编译进当前构建。编写新 fuzzer 的最小流程在 fuzz/oss_fuzz/ 新建文件实现LLVMFuzzerTestOneInputlibfuzzer 模式或通过DEF_FUZZ注册Fuzzable驱动模式。安装 clang 与 libfuzzer按 3.3 节配置args.gn务必保留-DSK_BUILD_FOR_LIBFUZZER。ninja -C out/libfuzzer skia后手工链接 harness或仿照 BUILD.gn 的libfuzzer_app实例新增 target。./a.out运行观察覆盖率与崩溃。常见排错点崩溃在普通构建下不出现换 sanitizer 构建类型自动检测失败检查 testcase 文件名是否匹配clusterfuzz-testcase-*或api-*命名或直接-t指定OOM/超时刷屏确认SK_BUILD_FOR_FUZZER已生效必要时在 harness 里像 fuzz/oss_fuzz/FuzzImage.cpp 那样限制输入大小skottie_json/svg_dom类型不可用确认构建开了skia_enable_skottie/SK_ENABLE_SVG。赞分享图形学图像处理【免费下载链接】skiaSkia is a complete 2D graphic library for drawing Text, Geometries, and Images.项目地址https://gitcode.com/gh_mirrors/skia1/skia点击查看免费下载相关推荐Skia Fuzzing 实战指南用 fuzz 复现崩溃、用 libFuzzer 编写模糊测试器并接入 OSS-FuzzSkia Fuzzing 实战指南用 fuzz 复现崩溃、用 libFuzzer 编写模糊测试器并接入 OSS Fuzz 本文基于 Skia 官方测试文档 s图形学Skia Fuzz 用例调试与复现测试编写指南从 Clusterfuzz 崩溃到修复落地Skia Fuzz 用例调试与复现测试编写指南从 Clusterfuzz 崩溃到修复落地 本文基于 Skia 仓库内置的 GN/Ninja 工作流技能文档 a图形学OSS-Fuzz 中 Skia 模糊测试的内存限制调优与崩溃复现指南OSS Fuzz 中 Skia 模糊测试的内存限制调优与崩溃复现指南 导读 本文基于 OSS Fuzz 仓库中 projects/skia/README.md网络安全开发工具CI/CD上一篇WebAssembly性能大揭秘GuiLite网页版3D渲染性能基准测试下一篇异构AI计算资源管理的范式革命HAMi如何重塑Kubernetes GPU调度生态创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表