github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/csource/options_test.go (about) 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 4 package csource 5 6 import ( 7 "fmt" 8 "math" 9 "reflect" 10 "testing" 11 12 "github.com/google/syzkaller/sys/targets" 13 ) 14 15 func TestParseOptions(t *testing.T) { 16 for _, opts := range allOptionsSingle(targets.Linux) { 17 data := opts.Serialize() 18 got, err := DeserializeOptions(data) 19 if err != nil { 20 t.Fatalf("failed to deserialize %q: %v", data, err) 21 } 22 if !reflect.DeepEqual(got, opts) { 23 t.Fatalf("opts changed, got:\n%+v\nwant:\n%+v", got, opts) 24 } 25 } 26 } 27 28 func TestParseOptionsCanned(t *testing.T) { 29 // Dashboard stores csource options with syzkaller reproducers, 30 // so we need to be able to parse old formats. 31 // nolint: lll, dupl 32 canned := map[string]Options{ 33 `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"namespace", 34 "fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, 35 "netdev":true,"resetnet":true, 36 "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { 37 Threaded: true, 38 Repeat: true, 39 Procs: 10, 40 Slowdown: 1, 41 Sandbox: "namespace", 42 NetInjection: true, 43 NetDevices: true, 44 NetReset: true, 45 Cgroups: true, 46 BinfmtMisc: false, 47 CloseFDs: true, 48 UseTmpDir: true, 49 HandleSegv: true, 50 LegacyOptions: LegacyOptions{ 51 Collide: true, 52 Fault: true, 53 FaultCall: 1, 54 FaultNth: 2, 55 }, 56 }, 57 `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android", 58 "fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, 59 "netdev":true,"resetnet":true, 60 "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { 61 Threaded: true, 62 Repeat: true, 63 Procs: 10, 64 Slowdown: 1, 65 Sandbox: "android", 66 NetInjection: true, 67 NetDevices: true, 68 NetReset: true, 69 Cgroups: true, 70 BinfmtMisc: false, 71 CloseFDs: true, 72 UseTmpDir: true, 73 HandleSegv: true, 74 LegacyOptions: LegacyOptions{ 75 Collide: true, 76 Fault: true, 77 FaultCall: 1, 78 FaultNth: 2, 79 }, 80 }, 81 `{"threaded":true,"collide":true,"repeat":true,"procs":10,"sandbox":"android", 82 "sandbox_arg":9,"fault":true,"fault_call":1,"fault_nth":2,"tun":true,"tmpdir":true,"cgroups":true, 83 "netdev":true,"resetnet":true, 84 "segv":true,"waitrepeat":true,"debug":true,"repro":true}`: { 85 Threaded: true, 86 Repeat: true, 87 Procs: 10, 88 Slowdown: 1, 89 Sandbox: "android", 90 SandboxArg: 9, 91 NetInjection: true, 92 NetDevices: true, 93 NetReset: true, 94 Cgroups: true, 95 BinfmtMisc: false, 96 CloseFDs: true, 97 UseTmpDir: true, 98 HandleSegv: true, 99 LegacyOptions: LegacyOptions{ 100 Collide: true, 101 Fault: true, 102 FaultCall: 1, 103 FaultNth: 2, 104 }, 105 }, 106 "{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}": { 107 Threaded: true, 108 Repeat: true, 109 Procs: 1, 110 Slowdown: 1, 111 Sandbox: "none", 112 NetInjection: true, 113 Cgroups: false, 114 BinfmtMisc: false, 115 CloseFDs: true, 116 UseTmpDir: true, 117 HandleSegv: true, 118 LegacyOptions: LegacyOptions{ 119 Collide: true, 120 Fault: false, 121 FaultCall: -1, 122 FaultNth: 0, 123 }, 124 }, 125 "{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}": { 126 Threaded: true, 127 Repeat: true, 128 Procs: 1, 129 Slowdown: 1, 130 Sandbox: "", 131 NetInjection: true, 132 Cgroups: false, 133 BinfmtMisc: false, 134 CloseFDs: true, 135 UseTmpDir: true, 136 HandleSegv: true, 137 LegacyOptions: LegacyOptions{ 138 Collide: true, 139 Fault: false, 140 FaultCall: -1, 141 FaultNth: 0, 142 }, 143 }, 144 "{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}": { 145 Threaded: false, 146 Repeat: true, 147 Procs: 1, 148 Slowdown: 1, 149 Sandbox: "namespace", 150 NetInjection: true, 151 Cgroups: true, 152 BinfmtMisc: false, 153 CloseFDs: true, 154 UseTmpDir: true, 155 HandleSegv: true, 156 LegacyOptions: LegacyOptions{ 157 Collide: true, 158 Fault: false, 159 FaultCall: -1, 160 FaultNth: 0, 161 }, 162 }, 163 "{Threaded:false Collide:true Repeat:true Procs:1 Sandbox:namespace SandboxArg:-234 Fault:false FaultCall:-1 FaultNth:0 EnableTun:true UseTmpDir:true EnableCgroups:true HandleSegv:true WaitRepeat:true Debug:false Repro:false}": { 164 Threaded: false, 165 Repeat: true, 166 Procs: 1, 167 Slowdown: 1, 168 Sandbox: "namespace", 169 SandboxArg: -234, 170 NetInjection: true, 171 Cgroups: true, 172 BinfmtMisc: false, 173 CloseFDs: true, 174 UseTmpDir: true, 175 HandleSegv: true, 176 LegacyOptions: LegacyOptions{ 177 Collide: true, 178 Fault: false, 179 FaultCall: -1, 180 FaultNth: 0, 181 }, 182 }, 183 } 184 for data, want := range canned { 185 got, err := DeserializeOptions([]byte(data)) 186 if err != nil { 187 t.Fatalf("failed to deserialize %q: %v", data, err) 188 } 189 if !reflect.DeepEqual(got, want) { 190 t.Fatalf("deserialize %q\ngot:\n%+v\nwant:\n%+v", data, got, want) 191 } 192 } 193 } 194 195 func allOptionsSingle(OS string) []Options { 196 var opts []Options 197 fields := reflect.TypeOf(Options{}).NumField() 198 for i := 0; i < fields; i++ { 199 // Because of constraints on options, we need some defaults 200 // (e.g. no collide without threaded). 201 opt := Options{ 202 Threaded: true, 203 Repeat: true, 204 Sandbox: "none", 205 UseTmpDir: true, 206 Slowdown: 1, 207 } 208 opts = append(opts, enumerateField(OS, opt, i)...) 209 } 210 return dedup(opts) 211 } 212 213 func allOptionsPermutations(OS string) []Options { 214 opts := []Options{{}} 215 fields := reflect.TypeOf(Options{}).NumField() 216 for i := 0; i < fields; i++ { 217 var newOpts []Options 218 for _, opt := range opts { 219 newOpts = append(newOpts, enumerateField(OS, opt, i)...) 220 } 221 opts = newOpts 222 } 223 return dedup(opts) 224 } 225 226 func dedup(opts []Options) []Options { 227 pos := 0 228 dedup := make(map[Options]bool) 229 for _, opt := range opts { 230 if dedup[opt] { 231 continue 232 } 233 dedup[opt] = true 234 opts[pos] = opt 235 pos++ 236 } 237 return opts[:pos] 238 } 239 240 func enumerateField(OS string, opt Options, field int) []Options { 241 var opts []Options 242 s := reflect.ValueOf(&opt).Elem() 243 fldName := s.Type().Field(field).Name 244 fld := s.Field(field) 245 if fldName == "Sandbox" { 246 for _, sandbox := range []string{"", "none", "setuid", "namespace", "android"} { 247 fld.SetString(sandbox) 248 opts = append(opts, opt) 249 } 250 } else if fldName == "SandboxArg" { 251 for _, sandboxArg := range []int64{math.MinInt, math.MaxInt} { 252 fld.SetInt(sandboxArg) 253 opts = append(opts, opt) 254 } 255 } else if fldName == "Procs" { 256 for _, procs := range []int64{1, 4} { 257 fld.SetInt(procs) 258 opts = append(opts, opt) 259 } 260 } else if fldName == "RepeatTimes" { 261 for _, times := range []int64{0, 10} { 262 fld.SetInt(times) 263 opts = append(opts, opt) 264 } 265 } else if fldName == "Slowdown" { 266 for _, val := range []int64{1, 10} { 267 fld.SetInt(val) 268 opts = append(opts, opt) 269 } 270 } else if fldName == "LegacyOptions" { 271 opts = append(opts, opt) 272 } else if fld.Kind() == reflect.Bool { 273 for _, v := range []bool{false, true} { 274 fld.SetBool(v) 275 opts = append(opts, opt) 276 } 277 } else { 278 panic(fmt.Sprintf("field '%v' is not boolean", fldName)) 279 } 280 var checked []Options 281 for _, opt := range opts { 282 if err := opt.Check(OS); err == nil { 283 checked = append(checked, opt) 284 } 285 } 286 return checked 287 } 288 289 func TestParseFeaturesFlags(t *testing.T) { 290 tests := []struct { 291 Enable string 292 Disable string 293 Default bool 294 Features map[string]bool 295 }{ 296 {"none", "none", true, map[string]bool{ 297 "tun": true, 298 "net_dev": true, 299 "net_reset": true, 300 "cgroups": true, 301 "binfmt_misc": true, 302 "close_fds": true, 303 "devlink_pci": true, 304 "nic_vf": true, 305 "usb": true, 306 "vhci": true, 307 "wifi": true, 308 "ieee802154": true, 309 "sysctl": true, 310 "swap": true, 311 }}, 312 {"none", "none", false, map[string]bool{}}, 313 {"all", "none", true, map[string]bool{ 314 "tun": true, 315 "net_dev": true, 316 "net_reset": true, 317 "cgroups": true, 318 "binfmt_misc": true, 319 "close_fds": true, 320 "devlink_pci": true, 321 "nic_vf": true, 322 "usb": true, 323 "vhci": true, 324 "wifi": true, 325 "ieee802154": true, 326 "sysctl": true, 327 "swap": true, 328 }}, 329 {"", "none", true, map[string]bool{}}, 330 {"none", "all", true, map[string]bool{}}, 331 {"none", "", true, map[string]bool{ 332 "tun": true, 333 "net_dev": true, 334 "net_reset": true, 335 "cgroups": true, 336 "binfmt_misc": true, 337 "close_fds": true, 338 "devlink_pci": true, 339 "nic_vf": true, 340 "usb": true, 341 "vhci": true, 342 "wifi": true, 343 "ieee802154": true, 344 "sysctl": true, 345 "swap": true, 346 }}, 347 {"tun,net_dev", "none", true, map[string]bool{ 348 "tun": true, 349 "net_dev": true, 350 }}, 351 {"none", "cgroups,net_dev", true, map[string]bool{ 352 "tun": true, 353 "net_reset": true, 354 "binfmt_misc": true, 355 "close_fds": true, 356 "devlink_pci": true, 357 "nic_vf": true, 358 "usb": true, 359 "vhci": true, 360 "wifi": true, 361 "ieee802154": true, 362 "sysctl": true, 363 "swap": true, 364 }}, 365 {"close_fds", "none", true, map[string]bool{ 366 "close_fds": true, 367 }}, 368 {"swap", "none", true, map[string]bool{ 369 "swap": true, 370 }}, 371 } 372 for i, test := range tests { 373 features, err := ParseFeaturesFlags(test.Enable, test.Disable, test.Default) 374 if err != nil { 375 t.Fatalf("failed to parse features flags: %v", err) 376 } 377 for name, feature := range features { 378 if feature.Enabled != test.Features[name] { 379 t.Fatalf("test #%v: invalid value for feature flag %s: got %v, want %v", 380 i, name, feature.Enabled, test.Features[name]) 381 } 382 } 383 } 384 }