ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

syzkaller 代码覆盖率体系详解:从 coverage points 原理到 Web 报告与 syz-cover 工具

syzkaller 代码覆盖率体系详解:从 coverage points 原理到 Web 报告与 syz-cover 工具 测试操作系统网络安全【免费下载链接】syzkallersyzkaller is an unsupervised coverage-guided kernel fuzzer项目地址https://gitcode.com/gh_mirrors/syz/syzkaller点击查看免费下载syzkaller 作为覆盖率引导的模糊测试器,其覆盖率(coverage)机制既是驱动语料库进化的核心信号,也是评估模糊测试整体进度的主要手段。本文围绕 syzkaller 仓库中的覆盖率文档 docs/coverage.md 展开,结合 pkg/cover、tools/syz-cover 与 syz-manager 的源码,讲清楚覆盖数据的采集原理、Web 报告界面的目录树与源码着色规则、原始覆盖数据(rawcover)的获取方式,以及命令行工具syz-cover生成 HTML/CSV/JSON 报告参数的完整用法。读完本文,你将能够看懂cover页面每一种颜色的含义、从运行中的syz-manager导出原始覆盖数据,并离线生成可分享的覆盖率报告。覆盖数据的来源:sanitizer coverage 与 KCOVdocs/coverage.md开篇即说明:syzkaller使用sanitizer coverage(tracing mode)和KCOV进行覆盖率采集。sanitizer coverage 同时被gcc支持,KCOV 也被一些其他操作系统支持;而gVisor的覆盖率实现是完全不同的另一套体系。coverage point:基本块还是 CFG 边覆盖率基于插入到目标代码中的coverage point(覆盖点)。一个覆盖点通常对应:一段基本块(basic block)代码,或一条控制流图(CFG)边。具体是哪一种,取决于构建时使用的编译器与插桩模式:对 Linux clang 而言默认模式是CFG 边,对 gcc 而言默认模式是基本块。文档特别强调了一个容易被忽视的事实:覆盖点是在编译器中端(middle-end)完成大量变换与优化 pass 之后才插入的,因此覆盖信息可能与源代码的关系并不直观。实际使用中会出现:未覆盖的行后面跟着已覆盖的行;你以为会出现覆盖点的地方看不到覆盖点,反之亦然(例如编译器拆分了基本块,或把控制流结构变成了无控制流的条件移动指令);因此,评估覆盖率对理解整体模糊测试进度仍然非常有用,但应当“带着一粒盐”(with a grain of salt)来看待具体数字。从源码结构看,覆盖数据在 syzkaller 内部就是一个“已执行 PC 值的集合”:pkg/cover/cover.go 中定义了核心类型Cover map[uint64]struct{},并提供MergeDiff方法在合并新覆盖时只返回新增的 PC——这正是语料库去重与“新覆盖优先”策略的数据基础。Linux 内核的符号化链路:binutils 工具链docs/linux/coverage.md 进一步给出了 Linux 内核场景下覆盖数据如何从地址映射回源码。kcov 导出每个被执行基本块的地址,而 syzkaller 运行时借助binutils工具(objdump、nm、addr2line、readelf)把这些地址映射到源码文件与函数:readelf— 检测虚拟内存偏移。执行readelf -SW kernel_image列出段头。由于 executor 在把 PC 发送给syz-manager前会把 PC 截断为uint32,syz-manager需要利用段头信息恢复偏移量:只考虑类型为PROGBITS的段,要求所有PROGBITS段的Address字段高 32 位相同,这 32 位即恢复偏移。在代码中,这一逻辑对应 pkg/cover/backend 的GetPCBase(见 pkg/cover/report.go 的GetPCBase),仅对非 GVisor/Starnix 的 Linux 目标生效。nm— 解析内核镜像中每个函数的地址与大小:nm -Ptx kernel_image。第一列是符号名,第二列是类型(只关心t/T代码段符号),第三列是地址,第四列是大小(syzkaller 中总是向上取整到 16)。最终形成以符号名为键、起止地址为值的映射,用于判断某个函数是否被调用过。objdump— 解析对__sanitizer_cov_trace_pc的每次调用的 PC 值。编译插桩会在每个基本块插入__sanitizer_cov_trace_pc调用,这些调用点就是回溯覆盖代码行的锚点:objdump -d --no-show-raw-insn kernel_image,从反汇编中识别callq ... __sanitizer_cov_trace_pc来确定可执行块的起始地址。addr2line— 把 kcov 导出的、objdump解析出的 PC 值映射到源文件与行号:addr2line -afi -e kernel_image(-afi表示显示地址、函数名并展开内联函数)。最终得到一张以 PC 为键的 frame 哈希表,frame 包含PC、Func、File、Line、Inline字段;由于内联,同一个 PC 可能对应多个 frame。对应到源码,pkg/cover/backend/backend.go 中定义了这些结构:Frame(含Module、PC、Name、FuncName、Path、Inline及行/列范围Range)、Symbol(符号地址区间)、CompileUnit(编译单元及其可回调的 PC 列表)。pkg/cover/report.go 中的symbolizePCs按模块批量调用rg.Symbolize完成 PC → frame 的转换,findSymbol则通过二分查找把 PC 定位到所属符号区间。跨架构环境下还需要注意:在启动syz-manager之前,必须为 syzkaller 提供正确的交叉编译 binutils。docs/linux/coverage.md给出的示例(mips64le 目标)是创建符号链接目录并加入PATH:mkdir -p ~/bin/mips64le ln -s which mips64el-linux-gnuabi64-addr2line ~/bin/mips64le/addr2line ln -s which mips64el-linux-gnuabi64-nm ~/bin/mips64le/nm ln -s which mips64el-linux-gnuabi64-objdump ~/bin/mips64le/objdump ln -s which mips64el-linux-gnuabi64-readelf ~/bin/mips64le/readelf export PATH~/bin/mips64le:$PATH目标三元组前缀由配置中的target选项决定。Web 界面:目录树、百分比与源码着色点击syz-manager页面上的cover链接(对应 HTTP 路由/cover,见 syz-manager/http.go 中handle(/cover, mgr.httpCover)),会得到一个以内核构建目录为根的目录树视图。目录与文件级的百分比每个目录显示X% of N或---:X% of N表示 N 个覆盖点中已有 X% 被覆盖;---表示该目录完全没有覆盖。点击目录可以下钻到其中的文件与子目录;每个源文件同样显示---或覆盖百分比。点击某个文件百分比数字,可以看到该文件内每个函数的覆盖率。从源码看,这些视图由 pkg/cover/html.go 的DoHTML生成:它先把每个源文件按路径切分挂到templateDir目录树上,再由processDir自底向上累加每个目录/文件的Total与Covered覆盖点数量并计算百分比;当某目录Covered 0时其子树会被清空,模板中对应显示---。一个值得注意的细节是percent函数:百分比向上取整,但只有完全覆盖时才显示 100%,否则封顶 99%,避免“99.x% 却显示 100%”的误导。函数级视图由addFunctionCoverage生成,每个函数显示X% of N,并在末尾给出SUMMARY汇总行。源码视图的着色规则点击任意 C 文件会进入源码视图,颜色定义在 pkg/cover/html.go 的coverTemplate中(原文档引用的report.go#L504是模板拆分前的位置)。各颜色含义如下:Covered(黑色 #000000):该行关联的所有 PC 值都已被覆盖。行左侧有一个数字,表示有多少个程序触发过该行关联的 PC 值;点击该数字可以打开对应程序。Both(橙色 #c86400):该行关联了多个 PC 值,但只有部分被执行。左侧同样有可点击的程序计数。Weak-uncovered(深红 #c80000):该行所在的函数(符号)完全没有任何覆盖,即函数从未被执行。注意:如果编译器把某符号优化掉并把代码内联到别处,则该行关联的符号是“代码被编译进去”的那个符号,这会让颜色的含义有时很难判断。Uncovered(红色 #ff0000):该行未被覆盖,但它所在的函数已经被执行过(该函数中存在至少一个已执行的 PC)。Not instrumented(灰色 #505050):该行关联的 PC 未被插桩,或该行源码根本不产生代码。从源码结构看,着色是在fileContents中按“行内片段”(chunk)粒度完成的:它把已覆盖与未覆盖的行范围(backend.Range,精确到列)逐行合并为lineCoverChunk,再输出span classcovered|uncovered|both。模板 CSS 中.covered为rgb(0, 0, 0)、.uncovered为rgb(255, 0, 0)、.both为rgb(200, 100, 0),而未插桩代码直接使用右栏基础文字色rgb(80, 80, 80)(即 #505050)呈现;原文档描述的 weak-uncovered 深红则对应函数零覆盖的语义,当前模板中未再保留独立类名,阅读时以文档定义为准。另外两个源码级细节:左侧程序计数点击后打开的“最近执行的程序”,在 pkg/cover/report.go 的prepareFileMap中实际选取的是覆盖该行且内容最短的程序(len(progs[progIndex].Data)更小者),便于阅读。当启用了精确覆盖校验(PreciseCoverage)时,prepareFileMap会检查 kcov 返回的 PC 是否都能在二进制中发现的覆盖回调点(CallbackPoints)里找到,若发现无法匹配的 PC 会报错提示检查discoverModules(),并可通过?force1跳过该校验。原始覆盖数据:rawcover 与相关导出端点syz-manager暴露了一组覆盖相关 HTTP 端点(见 syz-manager/http.go 的路由注册段),与本文档最相关的是:端点处理函数(见 pkg/cover/html.go)输出/rawcoverDoRawCover每行一个十六进制 PC(如0xffffffff839862fc),去重并排序;启用 rawCover 时直接输出单个程序的 PC 列表/rawcoverfilesDoRawCoverFilesCSV:PC,Module,Offset,Filename,Inline,StartLine,EndLine/funccoverDoCSV函数级覆盖 CSV/filecoverDoCSVFiles文件级统计 CSV/cover?jsonl1DoCoverJSONL每行一个CoverageInfo对象的 JSONL/subsystemcover、/modulecoverDoHTMLTable、DoModuleCover按子系统路径前缀/模块聚合的统计表DoRawCover输出的就是syz-cover工具需要的原始数据格式。/filecover的统计表包含CoveredLines/TotalLines、CoveredPCs/TotalPCs、函数数与“已覆盖函数内的 PC 数”等指标,按子系统(配置中的路径前缀)聚合,对应 pkg/cover/html.go 中groupCoverByFilePrefixes的聚合逻辑。syz-cover:离线生成覆盖率报告仓库提供了小型工具syz-cover,基于原始覆盖数据文件生成覆盖率报告,源码位于 tools/syz-cover/syz-cover.go。构建按文档给出的方式:GOOSlinux GOARCHamd64 go build -ldflags-s -w -o ./bin/syz-cover github.com/google/syzkaller/tools/syz-cover也可以使用 Makefile 中现成的目标(第 203 行):GOOS$(HOSTOS) GOARCH$(HOSTARCH) $(HOSTGO) build $(GOHOSTFLAGS) -o ./bin/syz-cover github.com/google/syzkaller/tools/syz-cover。获取原始覆盖数据从运行中的syz-manager下载:wget http://localhost:your syz-manager port/rawcover原始覆盖文件是纯文本,每行一个十六进制 PC,例如:0xffffffff8398658d 0xffffffff839862fc 0xffffffff8398633f除/rawcover端点外,syz-cover.go的注释还指出原始覆盖文件也可以来自带-coverfile标志的syz-execprog。生成 HTML 报告./bin/syz-cover --config location of your syzkaller config rawcover--config指向 syzkaller 的 manager 配置文件,其余参数是原始覆盖文件(支持多个,syz-cover -config config_file rawcover.file*)。不提供覆盖文件时,syz-cover会使用rg.Symbols中记录的全部PC 生成“全量覆盖”视图。默认行为是把 HTML 报告写入临时文件并调用xdg-open在浏览器中打开(见 tools/syz-cover/syz-cover.go 的main函数)。导出函数级覆盖 CSV./bin/syz-cover --config location of your syzkaller config --csv filename where to export rawcoverCSV 列头由 pkg/cover/html.go 的DoCSV定义,为:Module, Filename, Function, Covered PCs, Total PCs,即每个函数的已覆盖/总覆盖点数。导出行级覆盖 JSON./bin/syz-cover --config location of your syzkaller config --json filename where to export rawcover对应DoLineJSON,输出每个源文件的行级信息,结构为lineCoverExport:Module、Filename:所属模块与文件;Covered:完全覆盖的行号列表;Uncovered:未覆盖的行号列表;Both:同一行同时存在已执行与未执行 PC 值的行号列表。除文档提到的--csv、--json外,从 tools/syz-cover/syz-cover.go 的 flag 定义看,syz-cover还支持:--jsonl(导出 JSONL 格式的逐 frame 覆盖数据,含文件、函数、行列范围与命中次数)、--html(把 HTML 报告保存到指定文件而不弹浏览器)、--modules(提供从 manager/modules端点获取的模块 JSON 信息,用于内核模块的符号化)。小结:把覆盖率当作进度信号来用综合 docs/coverage.md 与其指向的 docs/linux/coverage.md,syzkaller 覆盖率体系可以归纳为一条链路:编译期插桩(__sanitizer_cov_trace_pc等覆盖点)→ 运行期采集(kcov/sanitizer 上报 PC)→ 符号化(binutils 把 PC 映射回符号与源码行)→ 聚合展示(目录树/文件/函数三级百分比与行级着色)→ 离线导出(syz-cover的 HTML/CSV/JSON)。理解 coverage point 与源码行之间的“非一一对应”关系,是正确解读报告中每一种颜色与百分比数字的前提;而/rawcoversyz-cover的组合则让你在syz-manager之外也能复现并分享完整的覆盖率报告。赞分享测试操作系统网络安全【免费下载链接】syzkallersyzkaller is an unsupervised coverage-guided kernel fuzzer项目地址https://gitcode.com/gh_mirrors/syz/syzkaller点击查看免费下载相关推荐syzkaller 覆盖率采集与报告指南KCOV、Sanitizer Coverage 与 syz-cover 全解析syzkaller 覆盖率采集与报告指南KCOV、Sanitizer Coverage 与 syz cover 全解析 syzkaller 是内核模糊测试工具网络安全开发工具质量保障ipatool的代码覆盖率报告使用Cover工具ipatool的代码覆盖率报告使用Cover工具 代码覆盖率是衡量测试质量的关键指标它能帮助开发者发现未被测试覆盖的代码区域。ipatool作为一款iOSCLI开发工具Git LFS Go代码覆盖率使用cover工具生成报告Git LFS Go代码覆盖率使用cover工具生成报告 1. 环境准备与依赖安装 Git LFSLarge File Storage大文件存储是Git开发工具CLI版本控制上一篇Eino流式处理4大范式详解Invoke、Stream、Collect与Transform实战指南下一篇终极指南如何一键批量下载网页资源并保持完整文件夹结构创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表