From 29e95fbb262d0f19ee1f04a59bafda4bf8f97b63 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Tue, 13 Oct 2015 11:55:19 +0200 Subject: process covereage in ipc package instead of fuzzer --- fuzzer/fuzzer.go | 46 ++------------------------ ipc/ipc.go | 80 +++++++++++++++++++++++++++++++++++++++++++--- ipc/ipc_test.go | 13 +++++--- tools/execlog/execlog.go | 2 +- tools/execprog/execprog.go | 2 +- tools/stress/stress.go | 2 +- 6 files changed, 89 insertions(+), 56 deletions(-) diff --git a/fuzzer/fuzzer.go b/fuzzer/fuzzer.go index e0ee11abd..d27196786 100644 --- a/fuzzer/fuzzer.go +++ b/fuzzer/fuzzer.go @@ -8,9 +8,7 @@ package main // i.e. aim at cracking new branches and triggering bugs in that new piece of code. import ( - "bytes" "crypto/sha1" - "encoding/binary" "flag" "fmt" "log" @@ -342,7 +340,7 @@ func execute1(env *ipc.Env, p *prog.Prog, workerId int) []cover.Cover { try := 0 retry: - output, strace, failed, hanged, err := env.Exec(p) + output, strace, rawCover, failed, hanged, err := env.Exec(p) if err != nil { if try > 10 { panic(err) @@ -356,47 +354,9 @@ retry: if len(strace) != 0 { logf(4, "strace:\n%s\n", strace) } - r := bytes.NewReader(env.Out) - var ncmd uint32 - if err := binary.Read(r, binary.LittleEndian, &ncmd); err != nil { - panic(err) - } cov := make([]cover.Cover, len(p.Calls)) - for i := uint32(0); i < ncmd; i++ { - var callIndex, callNum, coverSize, pc uint32 - if err := binary.Read(r, binary.LittleEndian, &callIndex); err != nil { - panic(err) - } - if err := binary.Read(r, binary.LittleEndian, &callNum); err != nil { - panic(err) - } - if err := binary.Read(r, binary.LittleEndian, &coverSize); err != nil { - panic(err) - } - if int(callIndex) > len(cov) { - panic(fmt.Sprintf("expect index %v, got %v", i, callIndex)) - } - c := p.Calls[callIndex] - if num := c.Meta.ID; uint32(num) != callNum { - logf(0, "ERROR: call %v: expect syscall %v, got %v, executed %v", callIndex, num, callNum, ncmd) - logf(0, "program:\n%s", p.Serialize()) - logf(0, "output:\n%s", output) - } - cover1 := make([]uint32, 0, coverSize) - for j := uint32(0); j < coverSize; j++ { - if err := binary.Read(r, binary.LittleEndian, &pc); err != nil { - panic(err) - } - cover1 = append(cover1, pc) - } - if *flagV >= 4 { - log.Printf("%v:", c.Meta.Name) - for _, pc := range cover1 { - log.Printf(" 0x%x", (1<<32-1)<<32|uint64(pc)) - } - log.Printf("\n") - } - cov[callIndex] = cover.Canonicalize(cover1) + for i, c := range rawCover { + cov[i] = cover.Canonicalize(c) } return cov } diff --git a/ipc/ipc.go b/ipc/ipc.go index 678e79636..7702bba66 100644 --- a/ipc/ipc.go +++ b/ipc/ipc.go @@ -4,6 +4,8 @@ package ipc import ( + "bytes" + "encoding/binary" "fmt" "io/ioutil" "os" @@ -76,19 +78,87 @@ func (env *Env) Close() error { } } -func (env *Env) Exec(p *prog.Prog) (output, strace []byte, failed, hanged bool, err0 error) { +// Exec starts executor binary to execute program p and returns information about the execution: +// output: process output +// strace: strace output if env is created with FlagStrace +// cov: per-call coverage, len(cov) == len(p.Calls) +// failed: true if executor has detected a kernel bug +// hanged: program hanged and was killed +// err0: failed to start process, or executor has detected a logical error +func (env *Env) Exec(p *prog.Prog) (output, strace []byte, cov [][]uint32, failed, hanged bool, err0 error) { if p != nil { + // Copy-in serialized program. progData := p.SerializeForExec() if len(progData) > len(env.In) { panic("program is too long") } copy(env.In, progData) } - // Zero out the first word (ncmd), so that we don't have garbage there - // if executor crashes before writing non-garbage there. - for i := 0; i < 4; i++ { - env.Out[i] = 0 + if env.flags&FlagCover != 0 { + // Zero out the first word (ncmd), so that we don't have garbage there + // if executor crashes before writing non-garbage there. + for i := 0; i < 4; i++ { + env.Out[i] = 0 + } + } + + output, strace, failed, hanged, err0 = env.execBin() + if err0 != nil { + return + } + + if env.flags&FlagCover == 0 || p == nil { + return + } + // Read out coverage information. + r := bytes.NewReader(env.Out) + var ncmd uint32 + if err := binary.Read(r, binary.LittleEndian, &ncmd); err != nil { + err0 = fmt.Errorf("failed to read output coverage: %v", err) + return + } + cov = make([][]uint32, len(p.Calls)) + for i := uint32(0); i < ncmd; i++ { + var callIndex, callNum, coverSize, pc uint32 + if err := binary.Read(r, binary.LittleEndian, &callIndex); err != nil { + err0 = fmt.Errorf("failed to read output coverage: %v", err) + return + } + if err := binary.Read(r, binary.LittleEndian, &callNum); err != nil { + err0 = fmt.Errorf("failed to read output coverage: %v", err) + return + } + if err := binary.Read(r, binary.LittleEndian, &coverSize); err != nil { + err0 = fmt.Errorf("failed to read output coverage: %v", err) + return + } + if int(callIndex) > len(cov) { + err0 = fmt.Errorf("failed to read output coverage: expect index %v, got %v", i, callIndex) + return + } + if cov[callIndex] != nil { + err0 = fmt.Errorf("failed to read output coverage: double coverage for call %v", callIndex) + return + } + c := p.Calls[callIndex] + if num := c.Meta.ID; uint32(num) != callNum { + err0 = fmt.Errorf("failed to read output coverage: call %v: expect syscall %v, got %v, executed %v", callIndex, num, callNum, ncmd) + return + } + cov1 := make([]uint32, coverSize) + for j := uint32(0); j < coverSize; j++ { + if err := binary.Read(r, binary.LittleEndian, &pc); err != nil { + err0 = fmt.Errorf("failed to read output coverage: expect index %v, got %v", i, callIndex) + return + } + cov1[j] = pc + } + cov[callIndex] = cov1 } + return +} + +func (env *Env) execBin() (output, strace []byte, failed, hanged bool, err0 error) { dir, err := ioutil.TempDir("./", "syzkaller-testdir") if err != nil { err0 = fmt.Errorf("failed to create temp dir: %v", err) diff --git a/ipc/ipc_test.go b/ipc/ipc_test.go index 87a57d232..b87bf4d2a 100644 --- a/ipc/ipc_test.go +++ b/ipc/ipc_test.go @@ -73,7 +73,7 @@ func TestEmptyProg(t *testing.T) { defer env.Close() p := new(prog.Prog) - output, strace, failed, hanged, err := env.Exec(p) + output, strace, cov, failed, hanged, err := env.Exec(p) if err != nil { t.Fatalf("failed to run executor: %v", err) } @@ -83,6 +83,9 @@ func TestEmptyProg(t *testing.T) { if len(strace) != 0 { t.Fatalf("strace output when not stracing") } + if cov != nil { + t.Fatalf("haven't asked for coverage, but got it") + } if failed || hanged { t.Fatalf("empty program failed") } @@ -99,7 +102,7 @@ func TestStrace(t *testing.T) { defer env.Close() p := new(prog.Prog) - _, strace, failed, hanged, err := env.Exec(p) + _, strace, _, failed, hanged, err := env.Exec(p) if err != nil { t.Fatalf("failed to run executor: %v", err) } @@ -126,7 +129,7 @@ func TestExecute(t *testing.T) { for i := 0; i < iters/len(flags); i++ { p := prog.Generate(rs, 10, nil) - _, _, _, _, err := env.Exec(p) + _, _, _, _, _, err := env.Exec(p) if err != nil { t.Fatalf("failed to run executor: %v", err) } @@ -160,7 +163,7 @@ func TestCompare(t *testing.T) { rs, iters := initTest(t) for i := 0; i < iters; i++ { p := prog.Generate(rs, 10, nil) - _, strace1, _, _, err := env1.Exec(p) + _, strace1, _, _, _, err := env1.Exec(p) if err != nil { t.Fatalf("failed to run executor: %v", err) } @@ -175,7 +178,7 @@ func TestCompare(t *testing.T) { } defer env2.Close() // yes, that's defer in a loop - _, strace2, _, _, err := env2.Exec(nil) + _, strace2, _, _, _, err := env2.Exec(nil) if err != nil { t.Fatalf("failed to run c binary: %v", err) } diff --git a/tools/execlog/execlog.go b/tools/execlog/execlog.go index a9081761e..f09ca7e56 100644 --- a/tools/execlog/execlog.go +++ b/tools/execlog/execlog.go @@ -71,7 +71,7 @@ func main() { if idx%1000 == 0 { log.Printf("executing %v\n", idx) } - _, _, _, _, err := env.Exec(progs[idx%len(progs)]) + _, _, _, _, _, err := env.Exec(progs[idx%len(progs)]) if err != nil { log.Printf("failed to execute program: %v", err) } diff --git a/tools/execprog/execprog.go b/tools/execprog/execprog.go index d82ea514e..9e0ac16a9 100644 --- a/tools/execprog/execprog.go +++ b/tools/execprog/execprog.go @@ -53,7 +53,7 @@ func main() { fmt.Fprintf(os.Stderr, "failed to create execution environment: %v\n", err) os.Exit(1) } - output, strace, failed, hanged, err := env.Exec(p) + output, strace, _, failed, hanged, err := env.Exec(p) fmt.Printf("result: failed=%v hanged=%v err=%v\n\n%s", failed, hanged, err, output) if *flagStrace { fmt.Printf("strace output:\n%s", strace) diff --git a/tools/stress/stress.go b/tools/stress/stress.go index 05bd5153b..388e2767d 100644 --- a/tools/stress/stress.go +++ b/tools/stress/stress.go @@ -59,7 +59,7 @@ func execute(env *ipc.Env, p *prog.Prog) { if *flagExecutor == "" { return } - output, _, _, _, err := env.Exec(p) + output, _, _, _, _, err := env.Exec(p) if err != nil { fmt.Printf("failed to execute executor: %v\n", err) } -- cgit mrf-deployment