Go Dynamic Tools

Gophercon 2015, July 9, 2015

Dmitry Vyukov

Google

Video

A video of this talk was recorded at GopherCon in Denver.

2

About me

Did a bunch of work on Go:

But actually on dynamic testing tools team:

3

Agenda

4

Data race detector

5

What is a data race?

A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.

All bets are off !

Any data race can destroy the memory/type-safety of a Go program.

6

There are no "benign" data races

// goroutine 1 // goroutine 2 m[k1] = v1 m[k2] = v2

Bad!

// goroutine 1 // goroutine 2 stat++ stat++

Also bad!

Compilers assume race-free programs and do aggressive optimizations
based on that assumption (e.g. assume "ownership" over written-to variables).

Races are non-deterministic and hard to debug.

7

Usage

$ go test -race mypkg // to test the package $ go run -race mysrc.go // to run the source file $ go build -race mycmd // to build the command $ go install -race mypkg // to install the package

That's it!

8

Example

package main func main() { m := make(map[int]int) go func() { m[1] = 1 }() m[2] = 2 }
9

Example report

WARNING: DATA RACE Write by goroutine 5: runtime.mapassign1() runtime/hashmap.go:411 +0x0 main.main.func1() race.go:6 +0x60 Previous write by main goroutine: runtime.mapassign1() runtime/hashmap.go:411 +0x0 main.main() race.go:8 +0xb6 Goroutine 5 (running) created at: main.main() race.go:7 +0x76
10

Achievements

11

Instrumentation

Compiler instrumentation pass enabled by -race.

func foo(p *int) { *p = 1 }

Becomes:

func foo(p *int) { runtime.funcenter(caller_pc) runtime.racewrite(p) *p = 1 runtime.funcexit() }
12

Run-time module

Handles:

Algorithm is based on dynamic modelling of happens-before relation:

13

Usage tips

Dynamic tools are only as good as your tests are.

14

Go-fuzz

15

Randomized testing

A different approach to testing that finds [lots of] bugs that other testing approaches do not. Intended mostly for programs that parse complex inputs.

Generate random blob -> feed into program -> see if it crashes -> profit!

Completely random blobs won't uncover lots of bugs.

How can we generate diverse but meaningful inputs that will trigger
nil derefs, off-by-ones, etc?

16

Coverage-guided fuzzing

Genetic algorithms to the rescue!

Instrument program for code coverage Collect initial corpus of inputs for { Randomly mutate an input from the corpus Execute and collect coverage If the input gives new coverage, add it to corpus }
17

Example

The following code wants "ABCD" input:

if input[0] == 'A' { if input[1] == 'B' { if input[2] == 'C' { if input[3] == 'D' { panic("input must not be ABCD") } } } }

Corpus progression:

"" "", "A" "", "A", "AB" "", "A", "AB", "ABC" "", "A", "AB", "ABC", "ABCD"
18

Game over

CRC32 checksum verification in image/png/reader.go

func (d *decoder) verifyChecksum() error { if binary.BigEndian.Uint32(d.tmp[:4]) != d.crc.Sum32() { return FormatError("invalid checksum") } return nil }

Probability that random mutations will alter input in an interesting way and
guess CRC32 at the same time is basically ZERO.

19

Sonar

Don't need to guess, program knows it!

+ v1 := binary.BigEndian.Uint32(d.tmp[:4]) + v2 := d.crc.Sum32() + __go_fuzz.Sonar(v1, v2) if v1 != v2 { return FormatError("invalid checksum") }

Then, find v1 in the input and replace it with v2. Done!

20

Game over 2

Mutations and sonar do low-level changes ("bit-flipping"):

Original:

`<item name="foo"><prop name="price">100</prop></item>`

Mutated:

`<item name="foo"><prop name="price">100</prop><<item>`

Also want high-level changes!

21

Versifier

Versifier reverse-engineers [text] protocol and learns its structure.

abc -> alphanum token 123, 1e-2 -> number "..." -> quoted [...] -> parenthesized ...,...,... -> list ...\n...\n -> lines

Then, applies structural mutations to inputs.

22

Versifier example

Original:

`<item name="foo"><prop name="price">100</prop></item>`

Versified (all valid xml):

<item name="rb54ana"><item name="foo"><prop name="price"></prop><prop/></item></item> <item name=""><prop name="price">=</prop><prop/> </item> <item name=""><prop F="">-026023767521520230564132665e0333302100</prop><prop/></item> <item SN="foo_P"><prop name="_G_nx">510</prop><prop name="vC">-9e-07036514</prop></item> <item name="foo"><prop name="c8">prop name="p"</prop>/}<prop name="price">01e-6</prop></item> <item name="foo"><item name="foo"><prop JY="">100</prop></item>8<prop/></item>
23

Algorithm

24

Achievements

25

Achievements

fmt.Sprintf("%.[]") panic: runtime error: index out of range regexp.MustCompile("((0(0){0}))").ReplaceAllString("00000", "00$00") panic: runtime error: slice bounds out of range ioutil.ReadAll(flate.NewReader(strings.NewReader("4LJNIMK\a\x00\x00\xff..\xff.....\xff"))) runs forever var x = 1/"."[0] crashes compiler archive/tar: hang archive/zip: cap out of range encoding/gob: stack overflow encoding/asn1: index out of range image/jpeg: Decode hangs image/png: nil deref math/big: incorrect string->Float conversion crypto/x509: division by zero ...
26

Usage

func Fuzz(data []byte) int { gob.NewDecoder(bytes.NewReader(data)) return 0 }
$ go-fuzz-build github.com/dvyukov/go-fuzz/examples/gob
$ go-fuzz -bin=gob-fuzz.zip -workdir=examples/gob
27

Execution tracer

28

Execution tracer

Gives insight into dynamic execution of a program.

Captures with nanosecond precision:

29

Execution tracer

30

Recap

31

Thank you

Dmitry Vyukov

Google

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)
close