diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2017-11-17 17:57:02 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-11-17 17:57:51 +0100 |
| commit | fba338cd51aa2894bdd97b6943c27236df6b98d9 (patch) | |
| tree | 6dcdb70f4e75568255befcf488923c7503fc969e /pkg/csource | |
| parent | 3fb087023a62213a128683d3ef52ff52d32b07fe (diff) | |
pkg/csource: add function to parse serialized options
Also move options and options tests into a separate file,
add serialization function.
Diffstat (limited to 'pkg/csource')
| -rw-r--r-- | pkg/csource/csource.go | 44 | ||||
| -rw-r--r-- | pkg/csource/csource_test.go | 63 | ||||
| -rw-r--r-- | pkg/csource/options.go | 82 | ||||
| -rw-r--r-- | pkg/csource/options_test.go | 131 |
4 files changed, 215 insertions, 105 deletions
diff --git a/pkg/csource/csource.go b/pkg/csource/csource.go index 1bdc6bfbd..eda16bca9 100644 --- a/pkg/csource/csource.go +++ b/pkg/csource/csource.go @@ -20,50 +20,6 @@ import ( "github.com/google/syzkaller/sys/targets" ) -type Options struct { - Threaded bool - Collide bool - Repeat bool - Procs int - Sandbox string - - Fault bool // inject fault into FaultCall/FaultNth - FaultCall int - FaultNth int - - // These options allow for a more fine-tuned control over the generated C code. - EnableTun bool - UseTmpDir bool - HandleSegv bool - WaitRepeat bool - Debug bool - - // Generate code for use with repro package to prints log messages, - // which allows to distinguish between a hang and an absent crash. - Repro bool -} - -// Check checks if the opts combination is valid or not. -// For example, Collide without Threaded is not valid. -// Invalid combinations must not be passed to Write. -func (opts Options) Check() error { - if !opts.Threaded && opts.Collide { - // Collide requires threaded. - return errors.New("Collide without Threaded") - } - if !opts.Repeat && opts.Procs > 1 { - // This does not affect generated code. - return errors.New("Procs>1 without Repeat") - } - if opts.Sandbox == "namespace" && !opts.UseTmpDir { - // This is borken and never worked. - // This tries to create syz-tmp dir in cwd, - // which will fail if procs>1 and on second run of the program. - return errors.New("Sandbox=namespace without UseTmpDir") - } - return nil -} - func Write(p *prog.Prog, opts Options) ([]byte, error) { if err := opts.Check(); err != nil { return nil, fmt.Errorf("csource: invalid opts: %v", err) diff --git a/pkg/csource/csource_test.go b/pkg/csource/csource_test.go index 913ea9bf6..6e79edc43 100644 --- a/pkg/csource/csource_test.go +++ b/pkg/csource/csource_test.go @@ -7,7 +7,6 @@ import ( "fmt" "math/rand" "os" - "reflect" "runtime" "testing" "time" @@ -30,65 +29,7 @@ func initTest(t *testing.T) (*prog.Target, rand.Source, int) { return target, rs, iters } -func enumerateField(opt Options, field int) []Options { - var opts []Options - s := reflect.ValueOf(&opt).Elem() - fldName := s.Type().Field(field).Name - fld := s.Field(field) - if fldName == "Sandbox" { - for _, sandbox := range []string{"", "none", "setuid", "namespace"} { - fld.SetString(sandbox) - opts = append(opts, opt) - } - } else if fldName == "Procs" { - for _, procs := range []int64{1, 4} { - fld.SetInt(procs) - opts = append(opts, opt) - } - } else if fldName == "FaultCall" { - opts = append(opts, opt) - } else if fldName == "FaultNth" { - opts = append(opts, opt) - } else if fld.Kind() == reflect.Bool { - for _, v := range []bool{false, true} { - fld.SetBool(v) - opts = append(opts, opt) - } - } else { - panic(fmt.Sprintf("field '%v' is not boolean", fldName)) - } - var checked []Options - for _, opt := range opts { - if err := opt.Check(); err == nil { - checked = append(checked, opt) - } - } - return checked -} - -func allOptionsSingle() []Options { - var opts []Options - fields := reflect.TypeOf(Options{}).NumField() - for i := 0; i < fields; i++ { - opts = append(opts, enumerateField(Options{}, i)...) - } - return opts -} - -func allOptionsPermutations() []Options { - opts := []Options{Options{}} - fields := reflect.TypeOf(Options{}).NumField() - for i := 0; i < fields; i++ { - var newOpts []Options - for _, opt := range opts { - newOpts = append(newOpts, enumerateField(opt, i)...) - } - opts = newOpts - } - return opts -} - -func TestOne(t *testing.T) { +func TestGenerateOne(t *testing.T) { t.Parallel() opts := Options{ Threaded: true, @@ -130,7 +71,7 @@ func TestOne(t *testing.T) { } } -func TestOptions(t *testing.T) { +func TestGenerateOptions(t *testing.T) { target, rs, _ := initTest(t) syzProg := target.GenerateAllSyzProg(rs) t.Logf("syz program:\n%s\n", syzProg.Serialize()) diff --git a/pkg/csource/options.go b/pkg/csource/options.go new file mode 100644 index 000000000..e253b3cdf --- /dev/null +++ b/pkg/csource/options.go @@ -0,0 +1,82 @@ +// Copyright 2017 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package csource + +import ( + "bytes" + "errors" + "fmt" +) + +// Options control various aspects of source generation. +// Dashboard also provides serialized Options along with syzkaller reproducers. +type Options struct { + Threaded bool + Collide bool + Repeat bool + Procs int + Sandbox string + + Fault bool // inject fault into FaultCall/FaultNth + FaultCall int + FaultNth int + + // These options allow for a more fine-tuned control over the generated C code. + EnableTun bool + UseTmpDir bool + HandleSegv bool + WaitRepeat bool + Debug bool + + // Generate code for use with repro package to prints log messages, + // which allows to distinguish between a hang and an absent crash. + Repro bool +} + +// Check checks if the opts combination is valid or not. +// For example, Collide without Threaded is not valid. +// Invalid combinations must not be passed to Write. +func (opts Options) Check() error { + if !opts.Threaded && opts.Collide { + // Collide requires threaded. + return errors.New("Collide without Threaded") + } + if !opts.Repeat && opts.Procs > 1 { + // This does not affect generated code. + return errors.New("Procs>1 without Repeat") + } + if opts.Sandbox == "namespace" && !opts.UseTmpDir { + // This is borken and never worked. + // This tries to create syz-tmp dir in cwd, + // which will fail if procs>1 and on second run of the program. + return errors.New("Sandbox=namespace without UseTmpDir") + } + return nil +} + +func (opts Options) Serialize() []byte { + return []byte(fmt.Sprintf("%+v", opts)) +} + +func DeserializeOptions(data []byte) (Options, error) { + data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1) + var opts Options + n, err := fmt.Sscanf(string(data), + "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+ + " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ + " HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", + &opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox, + &opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.EnableTun, &opts.UseTmpDir, + &opts.HandleSegv, &opts.WaitRepeat, &opts.Debug, &opts.Repro) + if err != nil { + return opts, fmt.Errorf("failed to parse repro options: %v", err) + } + if want := 14; n != want { + return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) + } + if opts.Sandbox == "empty" { + opts.Sandbox = "" + } + return opts, nil +} diff --git a/pkg/csource/options_test.go b/pkg/csource/options_test.go new file mode 100644 index 000000000..f3f42a6fc --- /dev/null +++ b/pkg/csource/options_test.go @@ -0,0 +1,131 @@ +// Copyright 2017 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package csource + +import ( + "fmt" + "reflect" + "testing" +) + +//{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false} + +func TestParseOptions(t *testing.T) { + for _, opts := range allOptionsSingle() { + data := opts.Serialize() + got, err := DeserializeOptions(data) + if err != nil { + t.Fatalf("failed to deserialize %q: %v", data, err) + } + if !reflect.DeepEqual(got, opts) { + t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts) + } + } +} + +func TestParseOptionsCanned(t *testing.T) { + // Dashboard stores csource options with syzkaller reproducers, + // so we need to be able to parse old formats. + canned := map[string]Options{ + "{Threaded:true Collide:true Repeat:true Procs:1 Sandbox:none Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": Options{ + Threaded: true, + Collide: true, + Repeat: true, + Procs: 1, + Sandbox: "none", + Fault: false, + FaultCall: -1, + FaultNth: 0, + EnableTun: true, + UseTmpDir: true, + HandleSegv: true, + WaitRepeat: true, + Debug: false, + Repro: false, + }, + "{Threaded:true Collide:true Repeat:true Procs:1 Sandbox: Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": Options{ + Threaded: true, + Collide: true, + Repeat: true, + Procs: 1, + Sandbox: "", + Fault: false, + FaultCall: -1, + FaultNth: 0, + EnableTun: true, + UseTmpDir: true, + HandleSegv: true, + WaitRepeat: true, + Debug: false, + Repro: false, + }, + } + for data, want := range canned { + got, err := DeserializeOptions([]byte(data)) + if err != nil { + t.Fatalf("failed to deserialize %q: %v", data, err) + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want) + } + } +} + +func allOptionsSingle() []Options { + var opts []Options + fields := reflect.TypeOf(Options{}).NumField() + for i := 0; i < fields; i++ { + opts = append(opts, enumerateField(Options{}, i)...) + } + return opts +} + +func allOptionsPermutations() []Options { + opts := []Options{Options{}} + fields := reflect.TypeOf(Options{}).NumField() + for i := 0; i < fields; i++ { + var newOpts []Options + for _, opt := range opts { + newOpts = append(newOpts, enumerateField(opt, i)...) + } + opts = newOpts + } + return opts +} + +func enumerateField(opt Options, field int) []Options { + var opts []Options + s := reflect.ValueOf(&opt).Elem() + fldName := s.Type().Field(field).Name + fld := s.Field(field) + if fldName == "Sandbox" { + for _, sandbox := range []string{"", "none", "setuid", "namespace"} { + fld.SetString(sandbox) + opts = append(opts, opt) + } + } else if fldName == "Procs" { + for _, procs := range []int64{1, 4} { + fld.SetInt(procs) + opts = append(opts, opt) + } + } else if fldName == "FaultCall" { + opts = append(opts, opt) + } else if fldName == "FaultNth" { + opts = append(opts, opt) + } else if fld.Kind() == reflect.Bool { + for _, v := range []bool{false, true} { + fld.SetBool(v) + opts = append(opts, opt) + } + } else { + panic(fmt.Sprintf("field '%v' is not boolean", fldName)) + } + var checked []Options + for _, opt := range opts { + if err := opt.Check(); err == nil { + checked = append(checked, opt) + } + } + return checked +} |
