pkg/csource: add option to trace syscall results

This will be needed for testing of generated programs.
This commit is contained in:
Dmitry Vyukov 2018-07-26 19:38:24 +02:00
parent 9d92841b4e
commit b25fc7b831
7 changed files with 64 additions and 16 deletions

View File

@ -21,6 +21,10 @@
#include <stdlib.h>
#include <string.h>
#if SYZ_TRACE
#include <errno.h>
#endif
#if SYZ_EXECUTOR && !GOOS_linux
#include <unistd.h>
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];

View File

@ -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)
}

View File

@ -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.

View File

@ -15,6 +15,10 @@ var commonHeader = `
#include <stdlib.h>
#include <string.h>
#if SYZ_TRACE
#include <errno.h>
#endif
#if SYZ_EXECUTOR && !GOOS_linux
#include <unistd.h>
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);

View File

@ -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
}

View File

@ -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" {

View File

@ -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 {