ARTICLE DETAIL

资讯详情

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

Apache Thrift 模糊测试(Fuzzing)基础设施与实战指南

Apache Thrift 模糊测试(Fuzzing)基础设施与实战指南 Apache Thrift 模糊测试Fuzzing基础设施与实战指南【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/GitHub_Trending/thr/thrift导读本文基于 FUZZING.md 系统介绍 Apache Thrift 的模糊测试Fuzzing基础设施包括安全性与序列化往返正确性两大测试目标、覆盖 9 种语言的反序列化与往返两类 Fuzzer、各语言的具体构建运行方式以及 OSS-Fuzz 持续集成与贡献流程。读完本文你将掌握 Thrift 仓库内 fuzzer 的完整脉络能够在本机复现运行 C/Go/Rust/Ruby 等语言的模糊测试目标并理解如何为 Thrift 的手写代码与生成代码贡献新的模糊测试用例。为什么需要给 Thrift 做 Fuzzing两大核心目标Apache Thrift 同时包含大量手写代码协议、传输层和由编译器根据.thriftIDL 生成的代码。这两类代码都可能因畸形输入而崩溃、死循环或产生内存安全问题因此仓库将 fuzzing 聚焦于两个关键方面安全性Security测试生成的代码如何处理畸形/恶意的输入。反序列化路径是攻击者唯一可以直接投递数据的入口缓冲区越界、整数溢出、过度分配等隐患都集中在这里。序列化往返正确性Serialization round-trip correctness确保数据在序列化后再反序列化后保持完全一致即serialize(deserialize(x))语义下的数据完整性同时覆盖序列化与反序列化两条代码路径。这两大目标在各语言实现中一以贯之并最终统一沉淀为两类 Fuzzer 目标。两类 Fuzzer反序列化与往返一致性对于每种受支持的语言仓库至少实现两类 Fuzzer1. Deserializer Fuzzer反序列化 Fuzzer直接接收原始 fuzzer 输入尝试将其反序列化为 Thrift 结构重点测试对畸形/意外输入的处理异常应被优雅捕获而非崩溃或越界在每种可用的协议上实现Binary、Compact以及部分语言的 JSON。2. Round-Trip Fuzzer往返 Fuzzer先将 fuzzer 输入反序列化再重新序列化最后再次反序列化并校验结果一致确保数据经过完整的序列化周期后保持完整性同时覆盖序列化与反序列化两条代码路径是比纯解析更严格的正确性检查。以 C 实现为例的源码解剖C 的通用 fuzz 逻辑集中在 lib/cpp/test/fuzz/FuzzCommon.tcc它清晰地体现了上述两类目标的实现方式消息大小上限FUZZ_MAX_MESSAGE_SIZE 10 * 1024 * 102410MB通过TConfiguration注入防止 fuzzing 期间因畸形长度字段导致过度分配FuzzCommon.tcc#L42-L43。解析型目标fuzz_parse用TMemoryBuffer以OBSERVE模式直接包装 fuzzer 输入构建指定协议后对FuzzTest结构执行read()所有TException均被捕获忽略——因为反序列化抛异常是 fuzzing 的预期行为FuzzCommon.tcc#L61-L73。往返型目标fuzz_roundtripread→write→ 再次read→ 用operator比较两个结构体若不等则输出TDebugProtocol调试串并抛runtime_error(Roundtrip failed)触发 fuzzer 记录崩溃用例FuzzCommon.tcc#L77-L117。NaN 假阳性处理is_nan_false_positive将两个结构体中均为 NaN 的double_field归一化为0.0再比较避免 IEEE 754 中NaN ! NaN造成误报FuzzCommon.tcc#L45-L57。弱符号 main非 fuzzing 构建下提供空的weak main使目标既能被 libFuzzer 链接也能作为普通测试编译FuzzCommon.tcc#L121-L125。C 的 fuzzer 使用的测试结构来自gen-cpp/FuzzTest_types.h其 IDL 定义在 test/FuzzTest.thrift另有 test/v0.16/FuzzTestNoUuid.thrift 变体覆盖了基本类型、容器、可选/必选字段等典型结构。支持的语言与 Fuzzing 工具矩阵根据 FUZZING.md 与各语言lib/language/test/fuzz/README.md当前维护的 fuzzer 及所用引擎如下语言引擎/框架状态目标位置GoGo 原生testing.F 旧 go-fuzz 目标供 OSS-Fuzz完整支持lib/go/test/fuzz、lib/go/thrift/fuzz_test.goCLLVM libFuzzer完整支持lib/cpp/test/fuzzRustcargo-fuzz完整支持lib/rs/test/fuzzPythonAtheris含 accelerated 协议变体完整支持lib/py/test/fuzzRubyRuzzy含 accelerated 变体完整支持lib/rb/test/fuzzJava/JVMJazzer完整支持lib/java/src/test/java/org/apache/thrift/test/fuzzJavaScript (Node.js)本地目标完整支持lib/nodejs/test/fuzzc_glibLLVM libFuzzer部分支持暂无 round-triplib/c_glib/test/fuzznetstdSharpFuzz仅本地支持不上 OSS-Fuzzlib/netstd/Tests/Thrift.FuzzTests各语言 Fuzzer 构建与运行每个语言的 fuzzer 都位于lib/language/test/fuzz目录。C、c_glib、Go、Rust 和 Ruby 将 fuzz 代码接入常规构建系统使代码生成与构建漂移能被尽早发现部分语言还提供本地运行目标或原生 fuzz 二进制。ClibFuzzer 原生目标进入 lib/cpp/test/fuzz 执行make check即可构建产物是标准 libFuzzer 目标直接运行二进制即可开始 fuzzing./FuzzParseBinary共 6 个目标每个实现标准LLVMFuzzerTestOneInput接口并复用FuzzCommon.tcc的通用代码目标覆盖协议类型FuzzParseBinaryBinary反序列化FuzzParseCompactCompact反序列化FuzzParseJsonJSON反序列化FuzzRoundtripBinaryBinary往返FuzzRoundtripCompactCompact往返FuzzRoundtripJsonJSON往返libFuzzer 内置变异引擎负责生成测试用例可用 Rust corpus generator 生成初始语料库见下文因为各语言实现共享相同的线上格式wire format。Go原生testing.F与 go-fuzz 双轨Go 是唯一同时维护两套目标的语言详见 lib/go/test/fuzz/README.md原生目标go test -fuzz——即普通testing.F目标不需要任何外部工具且有一个关键特性不带-fuzz运行时它们会把种子语料库seed corpus当作普通测试用例重放因此make check能免费获得回归覆盖一旦某个输入被修复只要把对应文件提交到testdata/fuzz/Target/该输入就永久固定为回归用例。这类目标分布在两处lib/go/thrift/fuzz_test.go无需生成代码直接驱动协议与传输读取路径随库自身的测试运行make -C lib/go check。包括FuzzReadBinary、FuzzReadBinaryNonStrict、FuzzReadBinaryStruct、FuzzReadCompact、FuzzReadCompactStruct、FuzzReadJSON、FuzzReadJSONStruct、FuzzReadSimpleJSON、FuzzReadHeader客户端类型探测、transform 列表、info 头、FuzzReadFramed帧长度读取、FuzzServerDispatch经TMultiplexedProcessor的服务端一跳、FuzzParseTuuid。lib/go/test/fuzz/fuzz_native_test.go对生成结构的读取与往返包括FuzzStructReadBinary/Compact/JSON与FuzzStructRoundtripBinary/Compact/JSON。真实 fuzzing 运行方式先在本目录执行一次make check生成gen-go# 在 lib/go 下 go test ./thrift -run ^$ -fuzz FuzzReadHeader -fuzztime 2m # 在 lib/go/test/fuzz 下 go test -run ^$ -fuzz FuzzStructRoundtripCompact -fuzztime 2m运行失败时 Go 会把输入写入目标旁的testdata/fuzz/Target/提交该文件与修复代码此后每次go test都会重放它。go-fuzz 目标OSS-Fuzz 使用——lib/go/test/fuzz/fuzz.go 持有旧式 go-fuzz 风格目标置于gofuzzbuild tag 之后供 OSS-Fuzz 构建消费make check只在本目录编译并冒烟测试它们。包括FuzzTutorial启动迷你服务并灌入随机数据、FuzzParseBinary/Compact/Json与FuzzRoundtripBinary/Compact/Json。复现这类目标的 bug 时可在fuzz_test.go中把输入缓冲传给相应函数。Rustcargo-fuzz 与结构感知目标在 lib/rs/test/fuzz 执行make check构建之后可用标准 cargo-fuzz 命令直接运行例如cargo fuzz run $fuzzer_name。共 6 个目标parse_compact/parse_binaryCompact/Binary 协议反序列化roundtrip_compact/roundtrip_binary序列化再反序列化并与原始输入比较structured_roundtrip_compact/structured_roundtrip_binary结构感知的往返——从合法 Thrift 结构出发因此能同时覆盖序列化路径与其他语言的非结构感知往返目标形成互补还能处理一些边界情况。Rust 还提供corpus_generator可执行文件用于生成解析类 fuzzer 的输入语料库见下文“跨语言 corpus 复用”其实现位于 lib/rs/test/fuzz/bin/corpus_generator.rs。PythonAtheris含 accelerated 协议变体Python 实现使用 Atheris——一个基于 libFuzzer、进程内、覆盖率引导的 Python fuzzer。与 C 不同Python fuzzer 不能直接在本地环境独立运行需要由相应构建系统执行 Atheris 生成的程序。目标定义在 lib/py/test/fuzz共 8 个FuzzParseBinary/FuzzParseCompact含Accelerated变体Binary/Compact 反序列化FuzzRoundtripBinary/FuzzRoundtripCompact含Accelerated变体往返。Accelerated变体对应纯 Python 之外的加速实现TBinaryProtocolAccelerated等公共逻辑集中在fuzz_common.py。RubyRuzzy 与 make 目标封装Ruby 使用 RuzzyTrailOfBits 出品的、面向纯 Ruby 与 C 扩展的覆盖率引导 fuzzer文档见 lib/rb/test/fuzz/README.md。共 8 个目标Binary/accelerated Binary/Compact/JSON 的解析与往返fuzz_parse_*、fuzz_roundtrip_*。本目录中的.rb文件是tracer 入口Ruzzy 要求纯 Ruby fuzzing 从 tracer 脚本加载独立 harness因此不要直接调用对应的_harness.rb文件。公共逻辑在fuzz_common.rb。环境准备仓库根目录执行apt install -y clang-19 MAKEmake --environment-overrides V1 \ CC/usr/bin/clang-19 \ CXX/usr/bin/clang-19 \ LDSHARED/usr/bin/clang-19 -shared \ LDSHAREDXX/usr/bin/clang-19 -shared \ gem install ruzzy # 校验 fuzz 目录生成 test/fuzz/gen-rb、语法检查 fuzz 脚本、验证 Fuzz::FuzzTest 可加载 make -C lib/rb/test/fuzz checkmake -C lib/rb check已递归进入lib/rb/test/fuzz因此常规 Rubymake check流程也会执行同样的校验。运行Makefile 封装了 Ruzzy 所需的LD_PRELOAD与ASAN_OPTIONS设置。纯 Ruby 目标只需fuzz-prepare即check的工作make -C lib/rb/test/fuzz fuzz-parse-binary make -C lib/rb/test/fuzz fuzz-roundtrip-compact make -C lib/rb/test/fuzz fuzz-parse-jsonaccelerated 目标会先以 sanitizer 标志重建thrift_nativemake -C lib/rb/test/fuzz fuzz-parse-binary-accelerated make -C lib/rb/test/fuzz fuzz-roundtrip-binary-acceleratedCORPUS...指定输入语料库路径相对lib/rb解析make -C lib/rb/test/fuzz fuzz-parse-binary \ CORPUS../rs/test/fuzz/corpus/binary make -C lib/rb/test/fuzz fuzz-parse-compact \ CORPUS../rs/test/fuzz/corpus/compactFUZZ_ARGS...可附加 libFuzzer 风格参数另有有界冒烟目标fuzz-smoke-parse-binary、fuzz-smoke-parse-binary-accelerated用于短时本地验证。若默认的clang/clang名称不对可用FUZZ_CC/FUZZ_CXX覆盖重建扩展make -C lib/rb/test/fuzz fuzz-build-ext \ FUZZ_CC/usr/bin/clang-19 \ FUZZ_CXX/usr/bin/clang-19底层的原始命令等价于# 内存分配失败常见且影响低DoS暂跳过Ruby 解释器会泄漏数据忽略Ruby 建议禁用 sigaltstack export ASAN_OPTIONSallocator_may_return_null1:detect_leaks0:use_sigaltstack0 LD_PRELOAD$(ruby -e require ruzzy; print Ruzzy::ASAN_PATH) \ ruby test/fuzz/fuzz_parse_binary_protocol.rb常见问题若 libFuzzer 打印WARNING: no interesting inputs were found so far. Is the code instrumented for coverage?并迅速把语料库缩到corp: 1/1b说明覆盖率追踪未生效——通常是 tracer 脚本被绕过或 ruzzy 未按预期 sanitizer 工具链安装。请运行本目录的.rb文件而非_harness.rb若 accelerated 目标退回到纯 Ruby 实现用fuzz-build-ext重建扩展后重跑。Java/JVMJazzerJava 使用 Jazzer——面向 JVM 的覆盖率引导、进程内 fuzzer目标见 lib/java/src/test/java/org/apache/thrift/test/fuzz。与 C 不同Java fuzzer 不能本地独立运行需由相应构建系统执行 Jazzer 生成的程序。6 个目标ParseBinaryFuzzer、ParseCompactFuzzer、ParseJSONFuzzer反序列化与RoundtripBinaryFuzzer、RoundtripCompactFuzzer、RoundtripJSONFuzzer往返公共逻辑在FuzzTestUtils.java另有TestRecursionLimit.java覆盖递归深度限制场景。c_glib部分支持c_glib 使用 LLVM libFuzzer见 lib/c_glib/test/fuzz。目前仅 2 个解析目标fuzz_parse_binary与fuzz_parse_compact往返类目标标记为 TODO“Add round trip fuzzers, similar to other languages”因此属于部分支持、尚无 round-trip 能力。JavaScript / Node.jsNode.js 的 fuzz 目标位于 lib/nodejs/test/fuzz同样按协议拆分为解析与往返两类fuzz_parse_TBinaryProtocol.js、fuzz_parse_TCompactProtocol.js、fuzz_parse_TJSONProtocol.js与对应的fuzz_roundtrip_TBinaryProtocol.js、fuzz_roundtrip_TCompactProtocol.js、fuzz_roundtrip_TJSONProtocol.js公共逻辑在fuzz_common.js。netstd仅本地支持netstd 的 fuzz 目标在 lib/netstd/Tests/Thrift.FuzzTests使用 SharpFuzz仅在本地运行不上 OSS-Fuzz。ProtocolFuzzerBase.cs 提供了公共逻辑通过TConfiguration.MaxMessageSize设置与 C 一致的 10MB 消息上限通过THRIFT_AFL_IN_PROCESS环境变量控制进程内 fuzzing置为1时用Fuzzer.Run而非Fuzzer.OutOfProcess.RunProtocolRoundtripFuzzerBase.cs 承载往返逻辑。仓库还提供 buildfuzzers.sh 与 runfuzzer.sh 两个脚本。跨语言 corpus 生成与复用由于各语言实现的线上格式一致Rust 的corpus_generator生成的语料可被 C、c_glib、Python、Ruby 等语言的解析类目标复用它不生成 JSON 协议输入仅支持 Binary 与 Compact。从仓库根目录运行cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \ --output-dir output_dir \ --protocol binary|compact \ --generate num_files \ --buffer-size buffer_size \ --random-size random_size经验推荐的参数组合# Binary 语料 cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \ --output-dir ./lib/rs/test/fuzz/corpus/binary \ --protocol binary \ --generate 1000 \ --buffer-size 65536 \ --random-size 16384 # Compact 语料 cargo run --manifest-path lib/rs/test/fuzz/Cargo.toml --bin corpus_generator -- \ --output-dir ./lib/rs/test/fuzz/corpus/compact \ --protocol compact \ --generate 1000 \ --buffer-size 16384 \ --random-size 16384生成后即可喂给其他语言例如make -C lib/rb/test/fuzz fuzz-parse-binary \ CORPUS../rs/test/fuzz/corpus/binaryOSS-Fuzz 持续集成fuzzer 在 OSS-Fuzz 上持续运行这是当前唯一方便、正式支持开启 fuzzing 的构建方式构建状态通过 OSS-Fuzz 状态看板查看查找其中的 thrift 项目构建源码上游 oss-fuzz 仓库的projects/thrift目录本仓库之外覆盖率改进通过 fuzz introspector 报告审视各目标的覆盖情况用于指导 fuzzer 改进。同时需要注意OSS-Fuzz 集成将在各语言 fuzzer 全部提交到本仓库后迎来一次重大更新。对于本地 fuzzing 实际可行的语言文档随 fuzzer 一同提供例如 C 直接构建 libFuzzer 二进制Ruby 暴露封装 Ruzzy 的make目标。此外为保证问题能被尽早发现一旦 fuzzer 稳定仓库将启用 CI 中的 fuzzing 支持。如何参与贡献当前 fuzzing 工作的最新状态与计划跟踪在 Apache Jira 的 THRIFT-5855 工单中。即使该工单关闭仍欢迎以下方向的贡献为不支持的语言新增 fuzzer参照 FUZZING.md 定义的两类目标反序列化 往返与既有语言的模式在lib/language/test/fuzz下实现改进现有 fuzzer例如参考 fuzz introspector 报告提升覆盖率或为 c_glib 补上往返目标向语料库添加测试用例对于把 corpus 保留在代码树中的语言如 Go 的testdata/fuzz/Target/任何曾经导致失败的输入都应提交到对应位置成为永久回归用例。若新增或修改了 fuzzer请记得同步更新 OSS-Fuzz 构建脚本如有需要。通用贡献规范见 CONTRIBUTING.md。结语Apache Thrift 的 fuzzing 体系以“安全性”与“序列化往返正确性”双目标为骨架通过 libFuzzer、cargo-fuzz、Atheris、Ruzzy、Jazzer、SharpFuzz 等工具覆盖 9 种语言并在 OSS-Fuzz 上持续运行。理解 FUZZING.md 及各语言lib/language/test/fuzz下的 README 与源码你就拥有了复现、扩展和贡献 Thrift fuzzer 的完整地图——无论是加固手写协议代码还是验证生成代码的鲁棒性这套基础设施都是最直接的入口。【免费下载链接】thriftApache Thrift项目地址: https://gitcode.com/GitHub_Trending/thr/thrift创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表