Documentation
¶
Overview ¶
Package fuzzbound bounds the resources one parse of attacker-controlled bytes may consume, so that a fuzz target fails on a resource blow-up instead of merely surviving it.
It exists because "did it panic?" is not a strong enough oracle for the parsers that read encrypted documents. The worst bug this module has shipped (C360) was a CFB header field used unvalidated as an allocation size: a 512-byte file drove a 16 GiB allocation. That does not panic. On a large machine it succeeds; on a small one the kernel OOM-kills the process, which a human reads as infrastructure flake rather than as a finding — and Go's fuzzing engine cannot distinguish it from a machine falling over either. Turning it into an assertion failure with a message is the whole point.
Two resources are bounded.
Allocation volume is read from the runtime's cumulative allocation counter (/gc/heap/allocs:bytes), not from resident memory. That is deliberate: a make([]uint32, 0, 1<<32) is served by fresh mmap'd pages that are never touched, so RSS can stay flat while 16 GiB is charged to the heap. The counter records large allocations (>32 KiB, the only ones that matter here) exactly and at the moment they happen. Small allocations are accumulated at span granularity and so are approximate by at most a few tens of KiB, which is immaterial against the budgets below.
Wall-clock time is bounded so that a quadratic or unbounded loop fails as a finding rather than as a package timeout nobody attributes to a parser.
A budget is (fixed floor) + (rate x input size). The floor absorbs the per-call constants — decoder buffers, cipher key schedules, maps — that do not scale with the input; the rate expresses the structural claim that a container cannot cost materially more than a small multiple of its own size.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Measure ¶
Measure runs fn and reports how many bytes it allocated and how long it took.
The allocation figure is process-wide for the duration of the call: another goroutine allocating concurrently inflates it. Fuzz workers run one execution at a time, so in practice fn is the only allocator; Budget.Check re-measures before failing to keep a stray concurrent allocation from being reported as a finding.
Types ¶
type Budget ¶
type Budget struct {
// What names the operation in failure messages, e.g. "readCFB".
What string
// Bytes is the allocation allowed regardless of input size, and
// BytesPerInputByte the allocation allowed per byte of input.
Bytes uint64
BytesPerInputByte uint64
// Time is the wall clock allowed regardless of input size, and TimePerMiB
// the wall clock allowed per MiB of input.
Time time.Duration
TimePerMiB time.Duration
}
Budget is what one parse of an n-byte input is allowed to spend.
func (Budget) Check ¶
Check runs fn over an input of n bytes and fails tb when fn allocates or runs longer than the budget allows.
fn must be repeatable: a near-miss is measured a second time and reported only if the second measurement also breaks the budget. That costs nothing on the overwhelmingly common in-budget path and keeps a stray concurrent allocation from being reported as a parser bug, while a blow-up (grossFactor times the budget or more) is reported from the first measurement.