From b25fc7b83119e8dca728a199fd92e24dd4c33fa4 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 26 Jul 2018 19:38:24 +0200 Subject: pkg/csource: add option to trace syscall results This will be needed for testing of generated programs. --- executor/common.h | 11 +++++++++++ pkg/csource/common.go | 6 ++++++ pkg/csource/csource.go | 23 +++++++++++++++-------- pkg/csource/generated.go | 11 +++++++++++ pkg/csource/options.go | 17 +++++++++++------ pkg/csource/options_test.go | 5 +++++ tools/syz-prog2c/prog2c.go | 7 +++++-- 7 files changed, 64 insertions(+), 16 deletions(-) diff --git a/executor/common.h b/executor/common.h index 9e6545926..1b9afd074 100644 --- a/executor/common.h +++ b/executor/common.h @@ -21,6 +21,10 @@ #include #include +#if SYZ_TRACE +#include +#endif + #if SYZ_EXECUTOR && !GOOS_linux #include NORETURN void doexit(int status) @@ -395,6 +399,9 @@ static void loop() #if SYZ_REPRO if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { } +#endif +#if SYZ_TRACE + printf("### start\n"); #endif int call, thread; #if SYZ_COLLIDE @@ -470,7 +477,11 @@ static void loop() fail("pipe failed"); #endif int iter; +#if SYZ_REPEAT_TIMES + for (iter = 0; iter < [[REPEAT_TIMES]]; iter++) { +#else for (iter = 0;; iter++) { +#endif #if SYZ_EXECUTOR || SYZ_USE_TMP_DIR // Create a new private work dir for this test (removed at the end of the loop). char cwdbuf[32]; diff --git a/pkg/csource/common.go b/pkg/csource/common.go index ebc085c7a..a03fba056 100644 --- a/pkg/csource/common.go +++ b/pkg/csource/common.go @@ -95,6 +95,9 @@ func defineList(p, mmapProg *prog.Prog, opts Options) ([]string, error) { if opts.Repeat { defines = append(defines, "SYZ_REPEAT") } + if opts.RepeatTimes > 1 { + defines = append(defines, "SYZ_REPEAT_TIMES") + } if opts.Procs > 1 { defines = append(defines, "SYZ_PROCS") } @@ -122,6 +125,9 @@ func defineList(p, mmapProg *prog.Prog, opts Options) ([]string, error) { if opts.Repro { defines = append(defines, "SYZ_REPRO") } + if opts.Trace { + defines = append(defines, "SYZ_TRACE") + } for _, c := range p.Calls { defines = append(defines, "__NR_"+c.Meta.CallName) } diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 66958f22b..895856ea3 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -27,13 +27,13 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { calls: make(map[string]uint64), } - calls, vars, err := ctx.generateProgCalls(ctx.p) + calls, vars, err := ctx.generateProgCalls(ctx.p, opts.Trace) if err != nil { return nil, err } mmapProg := p.Target.GenerateUberMmapProg() - mmapCalls, _, err := ctx.generateProgCalls(mmapProg) + mmapCalls, _, err := ctx.generateProgCalls(mmapProg, false) if err != nil { return nil, err } @@ -60,6 +60,7 @@ func Write(p *prog.Prog, opts Options) ([]byte, error) { } replacements := map[string]string{ "PROCS": fmt.Sprint(opts.Procs), + "REPEAT_TIMES": fmt.Sprint(opts.RepeatTimes), "NUM_CALLS": fmt.Sprint(len(p.Calls)), "MMAP_DATA": strings.Join(mmapCalls, ""), "SYSCALL_DEFINES": ctx.generateSyscallDefines(), @@ -94,17 +95,20 @@ func (ctx *context) generateSyscalls(calls []string, hasVars bool) string { opts := ctx.opts buf := new(bytes.Buffer) if !opts.Threaded && !opts.Collide { - if hasVars { + if hasVars || opts.Trace { fmt.Fprintf(buf, "\tlong res = 0;\n") } if opts.Repro { fmt.Fprintf(buf, "\tif (write(1, \"executing program\\n\", sizeof(\"executing program\\n\") - 1)) {}\n") } + if opts.Trace { + fmt.Fprintf(buf, "\tprintf(\"### start\\n\");\n") + } for _, c := range calls { fmt.Fprintf(buf, "%s", c) } } else { - if hasVars { + if hasVars || opts.Trace { fmt.Fprintf(buf, "\tlong res;") } fmt.Fprintf(buf, "\tswitch (call) {\n") @@ -145,7 +149,7 @@ func (ctx *context) generateSyscallDefines() string { return buf.String() } -func (ctx *context) generateProgCalls(p *prog.Prog) ([]string, []uint64, error) { +func (ctx *context) generateProgCalls(p *prog.Prog, trace bool) ([]string, []uint64, error) { exec := make([]byte, prog.ExecBufferSize) progSize, err := p.SerializeForExec(exec) if err != nil { @@ -155,11 +159,11 @@ func (ctx *context) generateProgCalls(p *prog.Prog) ([]string, []uint64, error) if err != nil { return nil, nil, err } - calls, vars := ctx.generateCalls(decoded) + calls, vars := ctx.generateCalls(decoded, trace) return calls, vars, nil } -func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { +func (ctx *context) generateCalls(p prog.ExecProg, trace bool) ([]string, []uint64) { var calls []string csumSeq := 0 for ci, call := range p.Calls { @@ -185,7 +189,7 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { if emitCall { native := ctx.sysTarget.SyscallNumbers && !strings.HasPrefix(callName, "syz_") fmt.Fprintf(w, "\t") - if resCopyout || argCopyout { + if resCopyout || argCopyout || trace { fmt.Fprintf(w, "res = ") } if native { @@ -219,6 +223,9 @@ func (ctx *context) generateCalls(p prog.ExecProg) ([]string, []uint64) { } } fmt.Fprintf(w, ");\n") + if trace { + fmt.Fprintf(w, "\tprintf(\"### call=%v errno=%%d\\n\", res == -1 ? errno : 0);\n", ci) + } } // Copyout. diff --git a/pkg/csource/generated.go b/pkg/csource/generated.go index 7cd6f8777..7be8ca329 100644 --- a/pkg/csource/generated.go +++ b/pkg/csource/generated.go @@ -15,6 +15,10 @@ var commonHeader = ` #include #include +#if SYZ_TRACE +#include +#endif + #if SYZ_EXECUTOR && !GOOS_linux #include NORETURN void doexit(int status) @@ -3612,6 +3616,9 @@ static void loop() #if SYZ_REPRO if (write(1, "executing program\n", sizeof("executing program\n") - 1)) { } +#endif +#if SYZ_TRACE + printf("### start\n"); #endif int call, thread; #if SYZ_COLLIDE @@ -3684,7 +3691,11 @@ static void loop() fail("pipe failed"); #endif int iter; +#if SYZ_REPEAT_TIMES + for (iter = 0; iter < [[REPEAT_TIMES]]; iter++) { +#else for (iter = 0;; iter++) { +#endif #if SYZ_EXECUTOR || SYZ_USE_TMP_DIR char cwdbuf[32]; sprintf(cwdbuf, "./%d", iter); diff --git a/pkg/csource/options.go b/pkg/csource/options.go index e60554120..a312c5db7 100644 --- a/pkg/csource/options.go +++ b/pkg/csource/options.go @@ -15,11 +15,12 @@ import ( // Options control various aspects of source generation. // Dashboard also provides serialized Options along with syzkaller reproducers. type Options struct { - Threaded bool `json:"threaded,omitempty"` - Collide bool `json:"collide,omitempty"` - Repeat bool `json:"repeat,omitempty"` - Procs int `json:"procs"` - Sandbox string `json:"sandbox"` + Threaded bool `json:"threaded,omitempty"` + Collide bool `json:"collide,omitempty"` + Repeat bool `json:"repeat,omitempty"` + RepeatTimes int `json:"repeat_times,omitempty"` // if non-0, repeat that many times + Procs int `json:"procs"` + Sandbox string `json:"sandbox"` Fault bool `json:"fault,omitempty"` // inject fault into FaultCall/FaultNth FaultCall int `json:"fault_call,omitempty"` @@ -34,8 +35,9 @@ type Options struct { HandleSegv bool `json:"segv,omitempty"` // Generate code for use with repro package to prints log messages, - // which allows to distinguish between a hang and an absent crash. + // which allows to detect hangs. Repro bool `json:"repro,omitempty"` + Trace bool `json:"trace,omitempty"` } // Check checks if the opts combination is valid or not. @@ -101,6 +103,9 @@ func (opts Options) Check(OS string) error { if opts.ResetNet && !opts.Repeat { return errors.New("ResetNet without Repeat") } + if !opts.Repeat && opts.RepeatTimes != 0 && opts.RepeatTimes != 1 { + return errors.New("RepeatTimes without Repeat") + } return nil } diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go index f43f2582e..ad86f8786 100644 --- a/pkg/csource/options_test.go +++ b/pkg/csource/options_test.go @@ -149,6 +149,11 @@ func enumerateField(OS string, opt Options, field int) []Options { fld.SetInt(procs) opts = append(opts, opt) } + } else if fldName == "RepeatTimes" { + for _, times := range []int64{0, 10} { + fld.SetInt(times) + opts = append(opts, opt) + } } else if fldName == "FaultCall" { opts = append(opts, opt) } else if fldName == "FaultNth" { diff --git a/tools/syz-prog2c/prog2c.go b/tools/syz-prog2c/prog2c.go index 9d89e8241..6abbd68b4 100644 --- a/tools/syz-prog2c/prog2c.go +++ b/tools/syz-prog2c/prog2c.go @@ -21,7 +21,7 @@ var ( flagBuild = flag.Bool("build", false, "also build the generated program") flagThreaded = flag.Bool("threaded", false, "create threaded program") flagCollide = flag.Bool("collide", false, "create collide program") - flagRepeat = flag.Bool("repeat", false, "repeat program infinitely or not") + flagRepeat = flag.Int("repeat", 1, "repeat program that many times (<=0 - infinitely)") flagProcs = flag.Int("procs", 1, "number of parallel processes") flagSandbox = flag.String("sandbox", "", "sandbox to use (none, setuid, namespace)") flagProg = flag.String("prog", "", "file with program to convert (required)") @@ -33,6 +33,7 @@ var ( flagNetdev = flag.Bool("netdev", false, "setup various net devices") flagResetNet = flag.Bool("resetnet", false, "reset net namespace after each test") flagHandleSegv = flag.Bool("segv", false, "catch and ignore SIGSEGV") + flagTrace = flag.Bool("trace", false, "trace syscall results") ) func main() { @@ -59,7 +60,8 @@ func main() { opts := csource.Options{ Threaded: *flagThreaded, Collide: *flagCollide, - Repeat: *flagRepeat, + Repeat: *flagRepeat != 1, + RepeatTimes: *flagRepeat, Procs: *flagProcs, Sandbox: *flagSandbox, Fault: *flagFaultCall >= 0, @@ -72,6 +74,7 @@ func main() { ResetNet: *flagResetNet, HandleSegv: *flagHandleSegv, Repro: false, + Trace: *flagTrace, } src, err := csource.Write(p, opts) if err != nil { -- cgit mrf-deployment