
测试操作系统网络安全【免费下载链接】syzkallersyzkaller is an unsupervised coverage-guided kernel fuzzer项目地址https://gitcode.com/gh_mirrors/syz/syzkaller点击查看免费下载syzkaller 在模糊测试过程中需要持续记录测试程序执行耗时序列化产物大小等海量采样值并对其中位数、分位数做在线统计。本文以 syzkaller 仓库内置vendor的 gohistogram 库 README 为主体结合 go.mod 与 pkg/stats/set.go、pkg/fuzzer/stats.go 等调用方源码讲清楚流式近似直方图Streaming Approximate Histogram的算法思想、API 设计、分位数误差来源以及它如何在 syzkaller 的指标统计与测试用例中落地。读完后你能理解为什么分位数不需要排序和全量存储即可估算桶bin的动态合并机制如何工作以及stats.Distribution背后的直方图快照与百分位渲染链路。一、为什么需要流式近似直方图gohistogram 的 README 开篇即点明该库的定位提供流式近似直方图用于高效的分位数quantile如百分位数近似计算。精确计算分位数有一个前提数据必须排序。对全量样本排序并逐条存储在两类场景下代价过高流式数据数据以不可控速率不断到来如 syzkaller 每秒成百上千次的程序执行无法等待收集完整再统计长期运行进程syzkaller 的 fuzzer/manager 可持续运行数周历史样本总量可能达到千万甚至上亿级逐条驻留内存不现实。README 说明本库的算法基础是 Ben-Haim Yom-Tov 在A Streaming Parallel Decision Tree Algorithm中提出的流式算法另一个同思想实现见于 Apache Hive 项目的 NumericHistogram UDF。其核心特征是直方图的桶没有预设大小。随着数据流式进入桶被动态添加和合并bins are dynamically added and merged。换句话说它用少量可变桶 有损合并换取O(桶数) 的内存与查询成本。README 同时强调该库提供两种实现NumericHistogram基础的流式直方图实现桶记录精确的样本值与计数WeightedHistogram把桶的权重做成指数加权移动平均EWMA让分位数近似近期加权适合持续注入大量数据、希望结果偏向最近时间窗口的场景。构造函数都接收一个**最大桶数maximum bin size**参数。README 给出的取舍原则是桶数越大近似越精确但内存占用与性能开销越高——这是整个库最重要的调参依据。二、库结构接口、桶结构与两种实现vendored 的库源码仅三个 Go 文件结构非常紧凑文件内容histogram.goHistogram接口与bin桶结构numerichistogram.goNumericHistogram基础实现weightedhistogram.goWeightedHistogramEWMA 实现2.1 Histogram 接口histogram.go 定义了统一接口// Histogram is the interface that wraps the Add and Quantile methods. type Histogram interface { // Add adds a new value, n, to the histogram. Trimming is done // automatically. Add(n float64) // Quantile returns an approximation. Quantile(n float64) (q float64) // String returns a string reprentation of the histogram, // which is useful for printing to a terminal. String() (str string) }三个方法的语义Add(n float64)向直方图流式写入一个样本值注释明确说明修剪trimming是自动完成的即桶数超限时的合并不需要调用方干预Quantile(n float64)返回近似分位数参数是[0,1]区间的小数0.5 即中位数String()输出可直接打印到终端的文本化直方图便于调试观察。底层的桶定义在 histogram.gotype bin struct { value float64 // 该桶代表的样本值合并桶中是加权平均 count float64 // 该桶覆盖的样本数或 EWMA 权重 }两个字段的设计正是不逐条存储样本的关键每个样本被折叠进(value, count)对桶总数被硬上限约束因此内存占用是 O(maxbins) 的。2.2 NumericHistogram基础实现构造函数 NewHistogram 接收最大桶数// NewHistogram returns a new NumericHistogram with a maximum of n bins. // // There is no optimal bin count, but somewhere between 20 and 80 bins // should be sufficient. func NewHistogram(n int) *NumericHistogram源码注释给出了官方建议桶数没有最优值20~80 个桶通常足够。Add的插入逻辑numerichistogram.go保持了桶按 value 升序排列若已有桶的value与新值完全相等直接count精确去重重复值不产生误差若遇到value n的桶在其前面插入新桶否则追加到尾部函数以defer h.trim()收尾保证任何插入后桶数不超过上限。trim()是近似误差的唯一来源numerichistogram.go。当桶数超过maxbins时它执行如下合并// Find closest bins in terms of value minDelta : 1e99 for i : range h.bins { ... if delta : h.bins[i].value - h.bins[i-1].value; delta minDelta { minDelta delta minDeltaIndex i } }即反复寻找 value 间距最小的相邻两桶并合并合并后的桶为count两桶计数相加value按 count 加权的平均值weighted average。这种合并最相近的邻居策略保证了误差被摊在数值上最不容易察觉的位置相邻桶本身距离就小同时保留数据分布的整体形状。代价是被合并区间内的分位数查询会退化到加权平均值这正是桶越大越准的原因——桶数上限越高历史合并损失越少。分位数查询 Quantile 是一次线性扫描func (h *NumericHistogram) Quantile(q float64) float64 { count : q * float64(h.total) for i : range h.bins { count - float64(h.bins[i].count) if count 0 { return h.bins[i].value } } return -1 }含义直观从目标累计量q * total开始沿升序桶依次扣减计数扣到不剩的那一桶的 value 就是近似分位数。复杂度 O(桶数)与样本总量无关。此外该实现还提供 CDF累计分布函数统计value x的样本比例、Mean加权均值、Variance方差与CountString()则以每桶占总量比例缩放成 200 格宽的点状条形图输出。2.3 WeightedHistogram指数加权实现WeightedHistogram 与基础版结构相同区别在于桶的 count 会被指数衰减使较旧的样本权重随时间降低分位数近似因此偏向近期数据。构造函数多一个alpha参数// NewWeightedHistogram returns a new WeightedHistogram with a maximum of n bins with a decay factor // of alpha. // // There is no optimal bin count, but somewhere between 20 and 80 bins should be // sufficient. // // Alpha should be set to 2 / (N1), where N represents the average age of the moving window. // For example, a 60-second window with an average age of 30 seconds would yield an // alpha of 0.064516129. func NewWeightedHistogram(n int, alpha float64) *WeightedHistogramalpha的取法有明确公式alpha 2 / (N1)其中 N 是期望的移动窗口平均年龄。README 给的例子60 秒窗口、平均年龄 30 秒则alpha ≈ 0.064516129。衰减通过 ewma 实现func ewma(existingVal float64, newVal float64, alpha float64) (result float64) { result newVal*(1-alpha) existingVal*alpha return } func (h *WeightedHistogram) scaleDown(except int) { for i : range h.bins { if i ! except { h.bins[i].count ewma(h.bins[i].count, 0, h.alpha) } } }从源码结构看每次Add命中某个桶后会把其余所有桶的 count 向 0 做一次 EWMA 衰减except是当前新增样本所在桶其 count 不衰减。因此越久未被更新的桶权重越小total也随之缓慢收敛实现近期加权的流式行为。它的Quantile/CDF/Mean/Variance与基础版一致trim()同样采用合并最接近邻居策略见 weightedhistogram.go。2.4 两种实现的选择要点维度NumericHistogramWeightedHistogram构造参数NewHistogram(n int)NewWeightedHistogram(n int, alpha float64)桶权重语义精确计数指数加权移动平均EWMA偏近期总样本量total精确累加total随衰减动态变化float64适用场景需要长期累计分布、离线式统计持续流入数据、关心时间窗口内行为共同取舍桶数越大越准内存/性能开销越大README 原话同左另需额外选择alpha 2/(N1)三、gohistogram 在 syzkaller 仓库中的引入方式gohistogram 是 syzkaller 的第三方依赖仓库中以 Go modules vendor 目录的标准方式引入go.mod 第 13 行声明github.com/VividCortex/gohistogram v1.0.0go.sum 记录了 v1.0.0 的模块与哈希校验值保证依赖完整性vendor/modules.txt 中登记了# github.com/VividCortex/gohistogram v1.0.0及包名vendored 源码位于 vendor/github.com/VividCortex/gohistogram/。需要注意 vendored 副本只包含histogram.go、numerichistogram.go、weightedhistogram.go、README.md 与 LICENSEMIT 协议不带上游的测试文件。README 中获取源码并运行测试的原始步骤git clone上游仓库后go test .针对的是上游开发场景在当前只读的 vendor 副本中正确的验证方式是运行 syzkaller 自身使用到该库的测试见下文第五节。README 的Using in your own code给出的接入方式——import github.com/VividCortex/gohistogram在 syzkaller 中体现为 vendor 路径下的同包导入调用方如 pkg/stats/set.go直接import github.com/VividCortex/gohistogram即可编译时由 vendor 机制解析到本地副本。四、实战应用一syzkaller 指标统计中的分布直方图gohistogram 在 syzkaller 中最核心的消费者是通用指标系统 pkg/stats/set.go。该文件提供 prometheus/streamz 风格的Val指标其中Distribution{}选项让指标从单调累加值变成分布直方图// Distribution says to collect/visualize histogram of individual sample distributions. type Distribution struct{}4.1 每个样本进桶直方图惰性创建Val结构持有直方图实例set.goAdd在分布模式下每个采样值都进入直方图而不是累加func (v *Val) Add(val int) { ... if v.hist { v.histMu.Lock() if v.histVal nil { v.histVal gohistogram.NewHistogram(histogramBuckets) } v.histVal.Add(float64(val)) v.histMu.Unlock() return } v.val.Add(uint64(val)) }桶数上限来自常量定义set.goconst ( tickPeriod time.Second histogramBuckets 255 )histogramBuckets 255说明 syzkaller 选择了接近高精确端的配置——对照库注释20~80 通常足够这里用更大的桶数上限换取更准的耗时/大小分位数代价可忽略255 个bin结构约几 KB。注意Add与histMu的组合表明该实现按单 goroutine 串行写入使用多协程场景需要像这里一样加锁。分布指标当前的当前值取的是直方图均值if v.hist { ... return int(v.histVal.Mean()) }即控制台看到的分布型指标数值是Mean()与NumericHistogram.Mean()的加权均值实现对应而非累计和。4.2 秒级 tick 与直方图快照轮换syzkaller的指标集带有一个每秒触发的tick()tickPeriod time.Second见 set.go它维护长度为historySize全局注册表取 256见newSet(256, true)的历史环形缓冲区。对分布型指标tick的语义是快照轮转if v.hist { if s.historyTicks s.historyScale { v.histMu.Lock() ln.hist[s.historyPos] v.histVal v.histVal nil v.histMu.Unlock() } }即每个记录周期结束时把当前直方图指针整体存入环槽位ln.hist[s.historyPos]同时把v.histVal置空下次Add时惰性重建一个新直方图。于是历史序列变成了一串互不重叠的时间片直方图每一片独立描述该时间段内的样本分布。当环写满后 compress() 执行降采样丢弃一半槽位、historyScale翻倍让时间轴自动从秒级过渡到分钟级长期运行时仍保持 O(256) 的历史规模。4.3 百分位渲染10/50/90 三条曲线RenderHTML() 在生成网页图表时对每个历史时间片调用Quantile提取三条百分位曲线for _, percent : range []int{10, 50, 90} { g.Lines append(g.Lines, fmt.Sprintf(%v%%, percent)) for i : 0; i s.historyPos; i { v : 0.0 if ln.hist[i] ! nil { v ln.hist[i].Quantile(float64(percent) / 100) } g.Points[i].Y append(g.Points[i].Y, v) } }这正是 README 所说分位数近似的落地形态网页统计页中每个分布型指标显示为10%/50%/90%三条线数据源是各时间片NumericHistogram.Quantile(0.1/0.5/0.9)的返回值。该页面由 manager 的 HTTP 接口触发渲染见 syz-manager/http.go 中对stats.Collect/stats.RenderHTML的调用专家模式下还能通过stats.Collect(stats.All)在心跳日志中看到指标文本syz-manager/manager.go。4.4 典型分布指标fuzzer 的执行耗时fuzzer 中有一个使用Distribution的实例pkg/fuzzer/stats.gostatExecTime: stats.Create(prog exec time, Test program execution time (ms), stats.Distribution{}),prog exec time测试程序执行时间单位毫秒是模糊测试循环里最典型的分布型数据每次执行的耗时波动极大从不到 1ms 到数秒不等用单一均值会严重误导借助 gohistogrammanager 页面上能看到执行耗时的 10/50/90 分位数随时间的演变用于判断整体是否变慢是否出现长尾卡顿。五、实战应用二单元测试里的分位数统计除了生产路径gohistogram 还被用作测试工具的分布探针。prog/encodingexec_test.go 的TestSerializeForExecRandom在随机生成大量程序、反复执行SerializeForExec/DeserializeExec往返后用两个 1000 桶直方图统计序列化产物大小分布execSizes : gohistogram.NewHistogram(1000) textSizes : gohistogram.NewHistogram(1000) ... for i : 0; i iters; i { p : target.Generate(rs, 10, ct) buf, err : p.SerializeForExec() ... totalSize len(buf) execSizes.Add(float64(len(buf))) textSizes.Add(float64(len(p.Serialize()))) } t.Logf(exec sizes: 10%%:%v 50%%:%v 90%%:%v, int(execSizes.Quantile(0.1)), int(execSizes.Quantile(0.5)), int(execSizes.Quantile(0.9)))这是一个非常贴近 READMEGetting started示例的用法NewHistogram(n)构造 → 循环Add(float64(size))→Quantile(0.1/0.5/0.9)取分位数 → 日志输出。测试中把桶数放大到 1000是因为迭代次数iters很大上万次随机生成更大桶数上限可以显著降低合并损失让 10/50/90 分位数对回归变化更敏感。运行go test ./prog/ -run TestSerializeForExecRandom -v即可在测试日志中复现该分位数统计注意vendor 目录内的 gohistogram 副本不含上游测试文件库行为验证依赖 syzkaller 的这些调用方测试。六、关键参数与调优速查综合 README 与源码注释使用 gohistogram 时的决策表如下决策点取值建议依据最大桶数 nNewHistogram/NewWeightedHistogram20~80 通常足够需要高精度可放大syzkaller 用 255/1000NewHistogram 注释pkg/stats/set.goprog/encodingexec_test.gon 越大分位数越准内存与 trim 开销越大README 原文A larger bin size yields more accurate approximations at the cost of increased memory utilization and performanceEWMA 衰减因子 alphaalpha 2/(N1)N 为窗口平均年龄如 30s → α≈0.0645NewWeightedHistogram 注释并发写入接口无内置锁需调用方加锁参考 pkg/stats/set.go 中histMu的用法空直方图Quantile找不到桶时返回 -1Mean/Variance返回 0numerichistogram.go分位数参数Quantile(q)中 q ∈ [0,1]0.5 为中位数接口定义与 syzkaller 调用0.1/0.5/0.9七、小结gohistogram 是 syzkaller 依赖树中一个体量很小v1.0.0三个源文件但位置关键的库算法层它把精确分位数必须排序的问题转化为O(桶数) 内存下的近似分位数问题近似误差完全来自桶数超限时trim()对最接近邻居桶的加权合并桶数上限就是精度-开销的旋钮实现层NumericHistogram提供精确计数版WeightedHistogram用alpha 2/(N1)的 EWMA 衰减提供近期加权版两者共享Histogram接口Add/Quantile/String与统一的bin{value, count}结构工程层syzkaller 通过Distribution{}选项把它接入通用指标系统pkg/stats/set.go以 255 桶上限、秒级 tick 快照、10/50/90 百分位渲染支撑了如 prog exec time 这类分布型监控指标pkg/fuzzer/stats.go并在序列化测试中用作规模分布探针prog/encodingexec_test.go。理解了这个库也就理解了 syzkaller 统计页面中每条百分位曲线背后的数据来源若需进一步深入可以从pkg/stats/set.go的tick()/compress()/RenderHTML()三处入手配合 vendor/github.com/VividCortex/gohistogram/ 下的 README 与三个源文件通读全文代码量在数百行以内。赞分享测试操作系统网络安全【免费下载链接】syzkallersyzkaller is an unsupervised coverage-guided kernel fuzzer项目地址https://gitcode.com/gh_mirrors/syz/syzkaller点击查看免费下载相关推荐MAS 免费激活 Windows 和 Office3 分钟完整指南MAS 免费激活 Windows 和 Office3 分钟完整指南 Windows 一开机右下角那块未激活小水印已经挂了好几周。打开 Office标题操作系统Apache Beam Java ApproximateQuantiles 近似分位数变换API 详解、算法原理与实战示例Apache Beam Java ApproximateQuantiles 近似分位数变换API 详解、算法原理与实战示例 Apache Beam 的 App批处理流处理大数据MMPose 中的 RLE残差对数似然估计让回归式姿态估计逼近热力图方法的原理与实战MMPose 中的 RLE残差对数似然估计让回归式姿态估计逼近热力图方法的原理与实战 导读 本文围绕 OpenMMLab 姿态估计工具箱 MMPose 对计算机视觉人工智能深度学习创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考