David Chou @ Golang Taiwan
CC-BY-SA-3.0-TW
Fuzzing is the process of sending intentionally invalid data to a product in the hopes of triggering an error.
- H.D. Moore
func CountAverage(num []byte) int {
sum := byte(0)
for _, v := range num {
sum += v
}
return int(sum) / len(num)
}func TestCountAverage(t *testing.T) {
tests := []struct {
name string
num []byte
want int
}{
{
num: []byte{1, 2, 3, 4, 5},
want: 3,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := CountAverage(tt.num)
assert.EqualValues(t, tt.want, got)
})
}
}$ go test -cover
PASS
coverage: 100.0% of statements$ go tool cover
Usage of 'go tool cover':
Given a coverage profile produced by 'go test':
go test -coverprofile=c.out
Open a web browser displaying annotated source code:
go tool cover -html=c.out
Write out an HTML file instead of launching a web browser:
go tool cover -html=c.out -o coverage.html
Display coverage percentages to stdout for each function:
go tool cover -func=c.out
Finally, to generate modified source code with coverage annotations
(what go test -cover does):
go tool cover -mode=set -var=CoverageVariableName program.go
package go_fuzzing_playground
func CountAverage(num []byte) int {GoCover.Count[0] = 1;
sum := byte(0)
for _, v := range num {GoCover.Count[2] = 1;
sum += v
}
GoCover.Count[1] = 1;return int(sum) / len(num)
}
var GoCover = struct {
Count [3]uint32
Pos [3 * 3]uint32
NumStmt [3]uint16
} {
Pos: [3 * 3]uint32{
3, 5, 0x180023, // [0]
8, 8, 0x1c0002, // [1]
5, 7, 0x30018, // [2]
},
NumStmt: [3]uint16{
2, // 0
1, // 1
1, // 2
},
}$ go tool cover -mode=set ./count_average.go
{StartLine, EndLine, ColumnInfo} [link]
Number of statements
package go_fuzzing_playground
func CountAverage(num []byte) int {GoCover.Count[0] = 1;
sum := byte(0)
for _, v := range num {GoCover.Count[2] = 1;
sum += v
}
GoCover.Count[1] = 1;return int(sum) / len(num)
}
var GoCover = struct {
Count [3]uint32
Pos [3 * 3]uint32
NumStmt [3]uint16
} {
Pos: [3 * 3]uint32{
3, 5, 0x180023, // [0]
8, 8, 0x1c0002, // [1]
5, 7, 0x30018, // [2]
},
NumStmt: [3]uint16{
2, // 0
1, // 1
1, // 2
},
}block0
block1
block2
$ go test ./ -coverprofile=coverage.out && cat coverage.out
ok 0.003s coverage: 100.0% of statements
mode: set
count_average.go:3.35,5.24 2 1
count_average.go:8.2, 8.28 1 1
count_average.go:5.24,7.3 1 1$ go test ./ -coverprofile=coverage.out && cat coverage.out
block0
block1
block2
block0
block1
block2
Basic block: 100%
Branch coverage: 67%
package go_fuzzing_playground
func CountAverage(num []byte) int {GoCover.Count[0] = 1;
sum := byte(0)
for _, v := range num {GoCover.Count[2] = 1;
sum += v
}
GoCover.Count[1] = 1;return int(sum) / len(num)
}block0
block1
block2
// edge inserts coverage instrumentation for libfuzzer.
func (o *orderState) edge() {
// Create a new uint8 counter to be allocated in section
// __libfuzzer_extra_counters.
counter := staticinit.StaticName(types.Types[types.TUINT8])
counter.SetLibfuzzerExtraCounter(true)
// counter += 1
incr := ir.NewAssignOpStmt(base.Pos, ir.OADD, counter, ir.NewInt(1))
o.append(incr)
}func (o *orderState) stmt(n ir.Node) {
switch n.Op() {
...
case ir.OFOR:
edge()
case ir.OIF:
edge()
case ir.ORANGE:
edge()
case ir.OSELECT:
edge()
case ir.OSWITCH:
edge()
case OANDAND, OOROR:
edge()
...
}
}// _counters and _ecounters mark the start and end, respectively, of where
// the 8-bit coverage counters reside in memory. They're known to cmd/link,
// which specially assigns their addresses for this purpose.
var _counters, _ecounters [0]byte
func coverage() []byte {
addr := unsafe.Pointer(&_counters)
size := uintptr(unsafe.Pointer(&_ecounters)) - uintptr(addr)
var res []byte
*(*unsafeheader.Slice)(unsafe.Pointer(&res)) = unsafeheader.Slice{
Data: addr,
Len: int(size),
Cap: int(size),
}
return res
}$ objdump go-fuzzing.test -h
go-fuzzing.test: file format elf64-x86-64
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0027890e 0000000000401000 0000000000401000 00001000 2**5
CONTENTS, ALLOC, LOAD, READONLY, CODE
...
19 .data 0000a550 0000000000883da0 0000000000883da0 00483da0 2**5
CONTENTS, ALLOC, LOAD, DATA
20 .bss 00031708 000000000088e300 000000000088e300 0048e300 2**5
ALLOC
21 .noptrbss 00006fc0 00000000008bfa20 00000000008bfa20 004bfa20 2**5
ALLOC
22 __libfuzzer_extra_counters 000052a4 00000000008c69e0 00000000008c69e0 004c69e0 2**0
ALLOC
$ go build -gcflags=all=-d=fuzzing -buildmode=c-archive -o pngfuzz.a .
$ clang -o png.fuzzer pngfuzz.a -fsanitize=fuzzerAmazon CTO Dr. Werner Vogels