func (ctx *context) testProg(p *prog.Prog, duration time.Duration, opts csource.Options, reboot bool) (crashed bool, err error) { inst := <-ctx.instances if inst == nil { return false, fmt.Errorf("all VMs failed to boot") } defer func() { ctx.returnInstance(inst, reboot, crashed) }() pstr := p.Serialize() progFile, err := fileutil.WriteTempFile(pstr) if err != nil { return false, err } defer os.Remove(progFile) vmProgFile, err := inst.Copy(progFile) if err != nil { return false, fmt.Errorf("failed to copy to VM: %v", err) } repeat := "1" if opts.Repeat { repeat = "0" } command := fmt.Sprintf("%v -executor %v -cover=0 -procs=%v -repeat=%v -sandbox %v -threaded=%v -collide=%v %v", inst.execprogBin, inst.executorBin, opts.Procs, repeat, opts.Sandbox, opts.Threaded, opts.Collide, vmProgFile) Logf(2, "reproducing crash '%v': testing program (duration=%v, %+v): %s", ctx.crashDesc, duration, opts, p) return ctx.testImpl(inst, command, duration) }
func testProg(cfg *config.Config, p *prog.Prog, multiplier int, threaded, collide bool) (res bool) { log.Printf("booting VM") inst := <-instances defer func() { returnInstance(inst, res) }() pstr := p.Serialize() progFile, err := fileutil.WriteTempFile(pstr) if err != nil { log.Fatalf("%v", err) } defer os.Remove(progFile) bin, err := inst.Copy(progFile) if err != nil { log.Fatalf("failed to copy to VM: %v", err) } repeat := 100 timeoutSec := 10 * repeat / cfg.Procs if threaded { repeat *= 10 timeoutSec *= 1 } repeat *= multiplier timeoutSec *= multiplier timeout := time.Duration(timeoutSec) * time.Second command := fmt.Sprintf("%v -executor %v -cover=0 -procs=%v -repeat=%v -threaded=%v -collide=%v %v", inst.execprogBin, inst.executorBin, cfg.Procs, repeat, threaded, collide, bin) log.Printf("testing program (threaded=%v, collide=%v, repeat=%v, timeout=%v):\n%s\n", threaded, collide, repeat, timeout, pstr) return testImpl(inst, command, timeout) }
func execute(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) { allCover := execute1(pid, env, p, stat) coverMu.RLock() defer coverMu.RUnlock() for i, cov := range allCover { if len(cov) == 0 { continue } c := p.Calls[i].Meta diff := cover.Difference(cov, maxCover[c.CallID]) diff = cover.Difference(diff, flakes) if len(diff) != 0 { coverMu.RUnlock() coverMu.Lock() maxCover[c.CallID] = cover.Union(maxCover[c.CallID], diff) coverMu.Unlock() coverMu.RLock() inp := Input{p.Clone(), i, cover.Copy(cov)} triageMu.Lock() triage = append(triage, inp) triageMu.Unlock() } } }
func execute(pid int, env *ipc.Env, p *prog.Prog) { if *flagExecutor == "" { return } atomic.AddUint64(&statExec, 1) if *flagLogProg { ticket := gate.Enter() defer gate.Leave(ticket) outMu.Lock() fmt.Printf("executing program %v\n%s\n", pid, p.Serialize()) outMu.Unlock() } output, _, _, failed, hanged, err := env.Exec(p) if err != nil { fmt.Printf("failed to execute executor: %v\n", err) } paniced := failedRe.Match(output) if failed || hanged || paniced || err != nil { fmt.Printf("PROGRAM:\n%s\n", p.Serialize()) } if failed || hanged || paniced || err != nil || *flagOutput { os.Stdout.Write(output) } }
func main() { flag.Parse() corpus := readCorpus() flags := ipc.FlagThreaded if *flagDebug { flags |= ipc.FlagDebug } env, err := ipc.MakeEnv(*flagExecutor, 4*time.Second, flags) if err != nil { failf("failed to create execution environment: %v", err) } rs := rand.NewSource(time.Now().UnixNano()) rnd := rand.New(rs) for i := 0; ; i++ { var p *prog.Prog if len(corpus) == 0 || i%10 != 0 { p = prog.Generate(rs, 50, nil) execute(env, p) p.Mutate(rs, 50, nil) execute(env, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() p.Mutate(rs, 50, nil) execute(env, p) } } }
func execute(env *ipc.Env, p *prog.Prog) { allCover := execute1(env, p) for i, cov := range allCover { if len(cov) == 0 { continue } c := p.Calls[i].Meta diff := cover.Difference(cov, maxCover[c.CallID]) diff = cover.Difference(diff, flakes) if len(diff) != 0 { triage = append(triage, Input{p.Clone(), i, cover.Copy(cov)}) } } }
func testOne(t *testing.T, p *prog.Prog, opts Options) { src := Write(p, opts) srcf, err := fileutil.WriteTempFile(src) if err != nil { t.Logf("program:\n%s\n", p.Serialize()) t.Fatalf("%v", err) } defer os.Remove(srcf) bin, err := Build(srcf) if err != nil { t.Logf("program:\n%s\n", p.Serialize()) t.Fatalf("%v", err) } defer os.Remove(bin) }
func execute(env *ipc.Env, p *prog.Prog) { if *flagExecutor == "" { return } output, _, _, _, _, err := env.Exec(p) if err != nil { fmt.Printf("failed to execute executor: %v\n", err) } failed := failedRe.Match(output) if failed { fmt.Printf("PROGRAM:\n%s\n", p.Serialize()) } if failed || *flagOutput { os.Stdout.Write(output) } }
func execute(env *ipc.Env, p *prog.Prog, workerId int) []Input { allCover := execute1(env, p, workerId) var inputs []Input for i, cov := range allCover { if len(cov) == 0 { continue } c := p.Calls[i].Meta diff := cover.Difference(cov, maxCover[c.CallID]) diff = cover.Difference(diff, flakes) if len(diff) != 0 { p1 := p.Clone() p1.TrimAfter(i) inputs = append(inputs, Input{p1, i, cover.Copy(cov)}) } } return inputs }
func main() { flag.Parse() corpus := readCorpus() log.Printf("parsed %v programs", len(corpus)) calls := buildCallList() prios := prog.CalculatePriorities(corpus) ct := prog.BuildChoiceTable(prios, calls) var flags uint64 if *flagThreaded { flags |= ipc.FlagThreaded } if *flagCollide { flags |= ipc.FlagCollide } if *flagNobody { flags |= ipc.FlagDropPrivs } if *flagDebug { flags |= ipc.FlagDebug } if *flagNoPgid { flags |= ipc.FlagNoSetpgid } gate = ipc.NewGate(2 * *flagProcs) for pid := 0; pid < *flagProcs; pid++ { pid := pid go func() { env, err := ipc.MakeEnv(*flagExecutor, *flagTimeout, flags) if err != nil { failf("failed to create execution environment: %v", err) } rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12) rnd := rand.New(rs) for i := 0; ; i++ { var p *prog.Prog if len(corpus) == 0 || i%4 != 0 { p = prog.Generate(rs, programLength, ct) execute(pid, env, p) p.Mutate(rs, programLength, ct) execute(pid, env, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() p.Mutate(rs, programLength, ct) execute(pid, env, p) p.Mutate(rs, programLength, ct) execute(pid, env, p) } } }() } for range time.NewTicker(5 * time.Second).C { log.Printf("executed %v programs", atomic.LoadUint64(&statExec)) } }
func main() { flag.Parse() corpus := readCorpus() var flags uint64 if *flagThreaded { flags |= ipc.FlagThreaded } if *flagCollide { flags |= ipc.FlagCollide } if *flagNobody { flags |= ipc.FlagDropPrivs } if *flagDebug { flags |= ipc.FlagDebug } for p := 0; p < *flagProcs; p++ { go func() { env, err := ipc.MakeEnv(*flagExecutor, 10*time.Second, flags) if err != nil { failf("failed to create execution environment: %v", err) } rs := rand.NewSource(time.Now().UnixNano()) rnd := rand.New(rs) for i := 0; ; i++ { var p *prog.Prog if len(corpus) == 0 || i%10 != 0 { p = prog.Generate(rs, 50, nil) execute(env, p) p.Mutate(rs, 50, nil) execute(env, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() p.Mutate(rs, 50, nil) execute(env, p) } } }() } select {} }
func execute1(env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover { if *flagSaveProg { f, err := os.Create(fmt.Sprintf("%v.prog", *flagName)) if err == nil { f.Write(p.Serialize()) f.Close() } } else { // The following output helps to understand what program crashed kernel. // It must not be intermixed. logMu.Lock() log.Printf("executing program:\n%s", p.Serialize()) logMu.Unlock() } try := 0 retry: *stat++ output, strace, rawCover, failed, hanged, err := env.Exec(p) if err != nil { if try > 10 { panic(err) } try++ debug.FreeOSMemory() time.Sleep(time.Second) goto retry } logf(4, "result failed=%v hanged=%v:\n%v\n", failed, hanged, string(output)) if len(strace) != 0 { logf(4, "strace:\n%s\n", strace) } cov := make([]cover.Cover, len(p.Calls)) for i, c := range rawCover { cov[i] = cover.Cover(c) } return cov }
func main() { flag.Parse() corpus := readCorpus() Logf(0, "parsed %v programs", len(corpus)) calls := buildCallList() prios := prog.CalculatePriorities(corpus) ct := prog.BuildChoiceTable(prios, calls) flags, timeout, err := ipc.DefaultFlags() if err != nil { Fatalf("%v", err) } gate = ipc.NewGate(2**flagProcs, nil) for pid := 0; pid < *flagProcs; pid++ { pid := pid go func() { env, err := ipc.MakeEnv(*flagExecutor, timeout, flags, pid) if err != nil { Fatalf("failed to create execution environment: %v", err) } rs := rand.NewSource(time.Now().UnixNano() + int64(pid)*1e12) rnd := rand.New(rs) for i := 0; ; i++ { var p *prog.Prog if len(corpus) == 0 || i%4 != 0 { p = prog.Generate(rs, programLength, ct) execute(pid, env, p) p.Mutate(rs, programLength, ct, corpus) execute(pid, env, p) } else { p = corpus[rnd.Intn(len(corpus))].Clone() p.Mutate(rs, programLength, ct, corpus) execute(pid, env, p) p.Mutate(rs, programLength, ct, corpus) execute(pid, env, p) } } }() } for range time.NewTicker(5 * time.Second).C { Logf(0, "executed %v programs", atomic.LoadUint64(&statExec)) } }
// Exec starts executor binary to execute program p and returns information about the execution: // output: process output // 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 []byte, cov [][]uint32, errnos []int, failed, hanged bool, err0 error) { if p != nil { // Copy-in serialized program. progData := p.SerializeForExec(env.pid) if len(progData) > len(env.In) { err0 = fmt.Errorf("executor %v: program is too long: %v/%v", env.pid, len(progData), len(env.In)) return } copy(env.In, progData) } 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 } } atomic.AddUint64(&env.StatExecs, 1) if env.cmd == nil { atomic.AddUint64(&env.StatRestarts, 1) env.cmd, err0 = makeCommand(env.pid, env.bin, env.timeout, env.flags, env.inFile, env.outFile) if err0 != nil { return } } var restart bool output, failed, hanged, restart, err0 = env.cmd.exec() if err0 != nil || restart { env.cmd.close() env.cmd = 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("executor %v: failed to read output coverage: %v", env.pid, err) return } cov = make([][]uint32, len(p.Calls)) errnos = make([]int, len(p.Calls)) for i := range errnos { errnos[i] = -1 // not executed } dumpCov := func() string { buf := new(bytes.Buffer) for i, c := range cov { str := "nil" if c != nil { str = fmt.Sprint(len(c)) } fmt.Fprintf(buf, "%v:%v|", i, str) } return buf.String() } for i := uint32(0); i < ncmd; i++ { var callIndex, callNum, errno, coverSize, pc uint32 if err := binary.Read(r, binary.LittleEndian, &callIndex); err != nil { err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err) return } if err := binary.Read(r, binary.LittleEndian, &callNum); err != nil { err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err) return } if err := binary.Read(r, binary.LittleEndian, &errno); err != nil { err0 = fmt.Errorf("executor %v: failed to read output errno: %v", env.pid, err) return } if err := binary.Read(r, binary.LittleEndian, &coverSize); err != nil { err0 = fmt.Errorf("executor %v: failed to read output coverage: %v", env.pid, err) return } if int(callIndex) > len(cov) { err0 = fmt.Errorf("executor %v: failed to read output coverage: record %v, call %v, total calls %v (cov: %v)", env.pid, i, callIndex, len(cov), dumpCov()) return } if cov[callIndex] != nil { err0 = fmt.Errorf("executor %v: failed to read output coverage: double coverage for call %v (cov: %v)", env.pid, callIndex, dumpCov()) return } c := p.Calls[callIndex] if num := c.Meta.ID; uint32(num) != callNum { err0 = fmt.Errorf("executor %v: failed to read output coverage: call %v: expect syscall %v, got %v, executed %v (cov: %v)", env.pid, callIndex, num, callNum, ncmd, dumpCov()) 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("executor %v: failed to read output coverage: record %v, call %v, coversize=%v err=%v", env.pid, i, callIndex, coverSize, err) return } cov1[j] = pc } cov[callIndex] = cov1 errnos[callIndex] = int(errno) } return }
func execute1(pid int, env *ipc.Env, p *prog.Prog, stat *uint64) []cover.Cover { if false { // For debugging, this function must not be executed with locks held. corpusMu.Lock() corpusMu.Unlock() coverMu.Lock() coverMu.Unlock() triageMu.Lock() triageMu.Unlock() } // Limit concurrency window and do leak checking once in a while. idx := gate.Enter() defer gate.Leave(idx, func() { if idx == 0 && *flagLeak && atomic.LoadUint32(&allTriaged) != 0 { // Scan for leaks once in a while (it is damn slow). kmemleakScan(true) } }) // The following output helps to understand what program crashed kernel. // It must not be intermixed. switch *flagOutput { case "none": // This case intentionally left blank. case "stdout": data := p.Serialize() logMu.Lock() log.Printf("executing program %v:\n%s", pid, data) logMu.Unlock() case "dmesg": fd, err := syscall.Open("/dev/kmsg", syscall.O_WRONLY, 0) if err == nil { buf := new(bytes.Buffer) fmt.Fprintf(buf, "syzkaller: executing program %v:\n%s", pid, p.Serialize()) syscall.Write(fd, buf.Bytes()) syscall.Close(fd) } case "file": f, err := os.Create(fmt.Sprintf("%v-%v.prog", *flagName, pid)) if err == nil { f.Write(p.Serialize()) f.Close() } } try := 0 retry: atomic.AddUint64(stat, 1) output, rawCover, errnos, failed, hanged, err := env.Exec(p) _ = errnos if failed { // BUG in output should be recognized by manager. logf(0, "BUG: executor-detected bug:\n%s", output) // Don't return any cover so that the input is not added to corpus. return make([]cover.Cover, len(p.Calls)) } if err != nil { if try > 10 { panic(err) } try++ debug.FreeOSMemory() time.Sleep(time.Second) goto retry } logf(4, "result failed=%v hanged=%v:\n%v\n", failed, hanged, string(output)) cov := make([]cover.Cover, len(p.Calls)) for i, c := range rawCover { cov[i] = cover.Cover(c) } return cov }
// 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) } 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 } } atomic.AddUint64(&env.StatExecs, 1) if env.cmd == nil { atomic.AddUint64(&env.StatRestarts, 1) env.cmd, err0 = makeCommand(env.bin, env.timeout, env.flags, env.inFile, env.outFile) if err0 != nil { return } } output, strace, failed, hanged, err0 = env.cmd.exec() if err0 != nil { env.cmd.close() env.cmd = 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 Write(p *prog.Prog, opts Options) ([]byte, error) { exec := p.SerializeForExec(0) w := new(bytes.Buffer) fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n") handled := make(map[string]int) for _, c := range p.Calls { handled[c.Meta.CallName] = c.Meta.NR } for name, nr := range handled { fmt.Fprintf(w, "#ifndef __NR_%v\n", name) fmt.Fprintf(w, "#define __NR_%v %v\n", name, nr) fmt.Fprintf(w, "#endif\n") } fmt.Fprintf(w, "\n") enableTun := "false" if _, ok := handled["syz_emit_ethernet"]; ok { enableTun = "true" } hdr, err := preprocessCommonHeader(opts, handled) if err != nil { return nil, err } fmt.Fprint(w, hdr) fmt.Fprint(w, "\n") calls, nvar := generateCalls(exec) fmt.Fprintf(w, "long r[%v];\n", nvar) if !opts.Repeat { generateTestFunc(w, opts, calls, "loop") fmt.Fprint(w, "int main()\n{\n") fmt.Fprintf(w, "\tsetup_main_process(0, %v);\n", enableTun) fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox) fmt.Fprint(w, "\tint status = 0;\n") fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") fmt.Fprint(w, "\treturn 0;\n}\n") } else { generateTestFunc(w, opts, calls, "test") if opts.Procs <= 1 { fmt.Fprint(w, "int main()\n{\n") fmt.Fprintf(w, "\tsetup_main_process(0, %v);\n", enableTun) fmt.Fprintf(w, "\tint pid = do_sandbox_%v();\n", opts.Sandbox) fmt.Fprint(w, "\tint status = 0;\n") fmt.Fprint(w, "\twhile (waitpid(pid, &status, __WALL) != pid) {}\n") fmt.Fprint(w, "\treturn 0;\n}\n") } else { fmt.Fprint(w, "int main()\n{\n") fmt.Fprint(w, "\tint i;") fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", opts.Procs) fmt.Fprint(w, "\t\tif (fork() == 0) {\n") fmt.Fprintf(w, "\t\t\tsetup_main_process(i, %v);\n", enableTun) fmt.Fprintf(w, "\t\t\tdo_sandbox_%v();\n", opts.Sandbox) fmt.Fprint(w, "\t\t}\n") fmt.Fprint(w, "\t}\n") fmt.Fprint(w, "\tsleep(1000000);\n") fmt.Fprint(w, "\treturn 0;\n}\n") } } // Remove duplicate new lines. out := w.Bytes() for { out1 := bytes.Replace(out, []byte{'\n', '\n', '\n'}, []byte{'\n', '\n'}, -1) if len(out) == len(out1) { break } out = out1 } return out, nil }
func Write(p *prog.Prog, opts Options) []byte { exec := p.SerializeForExec() w := new(bytes.Buffer) fmt.Fprint(w, "// autogenerated by syzkaller (http://github.com/google/syzkaller)\n\n") handled := make(map[string]int) for _, c := range p.Calls { handled[c.Meta.CallName] = c.Meta.NR } for _, c := range sys.Calls { if strings.HasPrefix(c.CallName, "syz_") { handled[c.CallName] = c.NR } } for name, nr := range handled { fmt.Fprintf(w, "#ifndef __NR_%v\n", name) fmt.Fprintf(w, "#define __NR_%v %v\n", name, nr) fmt.Fprintf(w, "#endif\n") } fmt.Fprintf(w, "\n") fmt.Fprint(w, commonHeader) fmt.Fprint(w, "\n") calls, nvar := generateCalls(exec) fmt.Fprintf(w, "long r[%v];\n", nvar) if !opts.Threaded && !opts.Collide { fmt.Fprint(w, ` int main() { install_segv_handler(); memset(r, -1, sizeof(r)); `) for _, c := range calls { fmt.Fprintf(w, "%s", c) } fmt.Fprintf(w, "\treturn 0;\n}\n") } else { fmt.Fprintf(w, "void *thr(void *arg)\n{\n") fmt.Fprintf(w, "\tswitch ((long)arg) {\n") for i, c := range calls { fmt.Fprintf(w, "\tcase %v:\n", i) fmt.Fprintf(w, "%s", strings.Replace(c, "\t", "\t\t", -1)) fmt.Fprintf(w, "\t\tbreak;\n") } fmt.Fprintf(w, "\t}\n") fmt.Fprintf(w, "\treturn 0;\n}\n\n") fmt.Fprintf(w, "int main()\n{\n") fmt.Fprintf(w, "\tlong i;\n") fmt.Fprintf(w, "\tpthread_t th[%v];\n", 2*len(calls)) fmt.Fprintf(w, "\n") fmt.Fprintf(w, "install_segv_handler();\n") fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") fmt.Fprintf(w, "\tsrand(getpid());\n") fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) fmt.Fprintf(w, "\t\tpthread_create(&th[i], 0, thr, (void*)i);\n") fmt.Fprintf(w, "\t\tusleep(10000);\n") fmt.Fprintf(w, "\t}\n") if opts.Collide { fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) fmt.Fprintf(w, "\t\tpthread_create(&th[%v+i], 0, thr, (void*)i);\n", len(calls)) fmt.Fprintf(w, "\t\tif (rand()%%2)\n") fmt.Fprintf(w, "\t\t\tusleep(rand()%%10000);\n") fmt.Fprintf(w, "\t}\n") } fmt.Fprintf(w, "\tusleep(100000);\n") fmt.Fprintf(w, "\treturn 0;\n}\n") } return w.Bytes() }
func Write(p *prog.Prog, opts Options) []byte { exec := p.SerializeForExec() w := new(bytes.Buffer) fmt.Fprintf(w, `// autogenerated by syzkaller (http://github.com/google/syzkaller) #include <unistd.h> #include <sys/syscall.h> #include <string.h> #include <stdint.h> #include <pthread.h> `) handled := make(map[string]bool) for _, c := range p.Calls { name := c.Meta.CallName if handled[name] { continue } handled[name] = true fmt.Fprintf(w, "#ifndef SYS_%v\n", name) fmt.Fprintf(w, "#define SYS_%v %v\n", name, c.Meta.NR) fmt.Fprintf(w, "#endif\n") } fmt.Fprintf(w, "\n") calls, nvar := generateCalls(exec) fmt.Fprintf(w, "long r[%v];\n\n", nvar) if !opts.Threaded && !opts.Collide { fmt.Fprintf(w, "int main()\n{\n") fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") for _, c := range calls { fmt.Fprintf(w, "%s", c) } fmt.Fprintf(w, "\treturn 0;\n}\n") } else { fmt.Fprintf(w, "void *thr(void *arg)\n{\n") fmt.Fprintf(w, "\tswitch ((long)arg) {\n") for i, c := range calls { fmt.Fprintf(w, "\tcase %v:\n", i) fmt.Fprintf(w, "%s", strings.Replace(c, "\t", "\t\t", -1)) fmt.Fprintf(w, "\t\tbreak;\n") } fmt.Fprintf(w, "\t}\n") fmt.Fprintf(w, "\treturn 0;\n}\n\n") fmt.Fprintf(w, "int main()\n{\n") fmt.Fprintf(w, "\tlong i;\n") fmt.Fprintf(w, "\tpthread_t th[%v];\n", len(calls)) fmt.Fprintf(w, "\n") fmt.Fprintf(w, "\tmemset(r, -1, sizeof(r));\n") fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) fmt.Fprintf(w, "\t\tpthread_create(&th[i], 0, thr, (void*)i);\n") fmt.Fprintf(w, "\t\tusleep(10000);\n") fmt.Fprintf(w, "\t}\n") if opts.Collide { fmt.Fprintf(w, "\tfor (i = 0; i < %v; i++) {\n", len(calls)) fmt.Fprintf(w, "\t\tpthread_create(&th[i], 0, thr, (void*)i);\n") fmt.Fprintf(w, "\t\tif (i%%2==0)\n") fmt.Fprintf(w, "\t\t\tusleep(10000);\n") fmt.Fprintf(w, "\t}\n") } fmt.Fprintf(w, "\tusleep(100000);\n") fmt.Fprintf(w, "\treturn 0;\n}\n") } return w.Bytes() }