1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package csource 5 6import ( 7 "fmt" 8 "reflect" 9 "testing" 10) 11 12func TestParseOptions(t *testing.T) { 13 for _, opts := range allOptionsSingle("linux") { 14 data := opts.Serialize() 15 got, err := DeserializeOptions(data) 16 if err != nil { 17 t.Fatalf("failed to deserialize %q: %v", data, err) 18 } 19 if !reflect.DeepEqual(got, opts) { 20 t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts) 21 } 22 } 23} 24 25func TestParseOptionsCanned(t *testing.T) { 26 // Dashboard stores csource options with syzkaller reproducers, 27 // so we need to be able to parse old formats. 28 // nolint: lll 29 canned := map[string]Options{ 30 `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"namespace", 31 "fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, 32 "netdev":true,"resetnet":true, 33 "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { 34 Threaded: true, 35 Collide: true, 36 Repeat: true, 37 Procs: 10, 38 Sandbox: "namespace", 39 Fault: true, 40 FaultCall: 1, 41 FaultNth: 2, 42 EnableTun: true, 43 UseTmpDir: true, 44 EnableCgroups: true, 45 EnableNetdev: true, 46 ResetNet: true, 47 HandleSegv: true, 48 Repro: true, 49 }, 50 "{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}": { 51 Threaded: true, 52 Collide: true, 53 Repeat: true, 54 Procs: 1, 55 Sandbox: "none", 56 Fault: false, 57 FaultCall: -1, 58 FaultNth: 0, 59 EnableTun: true, 60 UseTmpDir: true, 61 EnableCgroups: false, 62 HandleSegv: true, 63 Repro: false, 64 }, 65 "{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}": { 66 Threaded: true, 67 Collide: true, 68 Repeat: true, 69 Procs: 1, 70 Sandbox: "", 71 Fault: false, 72 FaultCall: -1, 73 FaultNth: 0, 74 EnableTun: true, 75 UseTmpDir: true, 76 EnableCgroups: false, 77 HandleSegv: true, 78 Repro: false, 79 }, 80 "{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { 81 Threaded: false, 82 Collide: true, 83 Repeat: true, 84 Procs: 1, 85 Sandbox: "namespace", 86 Fault: false, 87 FaultCall: -1, 88 FaultNth: 0, 89 EnableTun: true, 90 UseTmpDir: true, 91 EnableCgroups: true, 92 HandleSegv: true, 93 Repro: false, 94 }, 95 } 96 for data, want := range canned { 97 got, err := DeserializeOptions([]byte(data)) 98 if err != nil { 99 t.Fatalf("failed to deserialize %q: %v", data, err) 100 } 101 if !reflect.DeepEqual(got, want) { 102 t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want) 103 } 104 } 105} 106 107func allOptionsSingle(OS string) []Options { 108 var opts []Options 109 fields := reflect.TypeOf(Options{}).NumField() 110 for i := 0; i < fields; i++ { 111 // Because of constraints on options, we need some defaults 112 // (e.g. no collide without threaded). 113 opt := Options{ 114 Threaded: true, 115 Repeat: true, 116 Sandbox: "none", 117 UseTmpDir: true, 118 } 119 opts = append(opts, enumerateField(OS, opt, i)...) 120 } 121 return opts 122} 123 124func allOptionsPermutations(OS string) []Options { 125 opts := []Options{{}} 126 fields := reflect.TypeOf(Options{}).NumField() 127 for i := 0; i < fields; i++ { 128 var newOpts []Options 129 for _, opt := range opts { 130 newOpts = append(newOpts, enumerateField(OS, opt, i)...) 131 } 132 opts = newOpts 133 } 134 return opts 135} 136 137func enumerateField(OS string, opt Options, field int) []Options { 138 var opts []Options 139 s := reflect.ValueOf(&opt).Elem() 140 fldName := s.Type().Field(field).Name 141 fld := s.Field(field) 142 if fldName == "Sandbox" { 143 for _, sandbox := range []string{"", "none", "setuid", "namespace"} { 144 fld.SetString(sandbox) 145 opts = append(opts, opt) 146 } 147 } else if fldName == "Procs" { 148 for _, procs := range []int64{1, 4} { 149 fld.SetInt(procs) 150 opts = append(opts, opt) 151 } 152 } else if fldName == "RepeatTimes" { 153 for _, times := range []int64{0, 10} { 154 fld.SetInt(times) 155 opts = append(opts, opt) 156 } 157 } else if fldName == "FaultCall" { 158 opts = append(opts, opt) 159 } else if fldName == "FaultNth" { 160 opts = append(opts, opt) 161 } else if fld.Kind() == reflect.Bool { 162 for _, v := range []bool{false, true} { 163 fld.SetBool(v) 164 opts = append(opts, opt) 165 } 166 } else { 167 panic(fmt.Sprintf("field '%v' is not boolean", fldName)) 168 } 169 var checked []Options 170 for _, opt := range opts { 171 if err := opt.Check(OS); err == nil { 172 checked = append(checked, opt) 173 } 174 } 175 return checked 176} 177