github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/csource/options.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 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "sort" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/flatrpc" 15 "github.com/google/syzkaller/pkg/mgrconfig" 16 "github.com/google/syzkaller/sys/targets" 17 ) 18 19 // Options control various aspects of source generation. 20 // Dashboard also provides serialized Options along with syzkaller reproducers. 21 type Options struct { 22 Threaded bool `json:"threaded,omitempty"` 23 Repeat bool `json:"repeat,omitempty"` 24 RepeatTimes int `json:"repeat_times,omitempty"` // if non-0, repeat that many times 25 Procs int `json:"procs"` 26 Slowdown int `json:"slowdown"` 27 Sandbox string `json:"sandbox"` 28 SandboxArg int `json:"sandbox_arg"` 29 30 Leak bool `json:"leak,omitempty"` // do leak checking 31 32 // These options allow for a more fine-tuned control over the generated C code. 33 NetInjection bool `json:"tun,omitempty"` 34 NetDevices bool `json:"netdev,omitempty"` 35 NetReset bool `json:"resetnet,omitempty"` 36 Cgroups bool `json:"cgroups,omitempty"` 37 BinfmtMisc bool `json:"binfmt_misc,omitempty"` 38 CloseFDs bool `json:"close_fds"` 39 KCSAN bool `json:"kcsan,omitempty"` 40 DevlinkPCI bool `json:"devlinkpci,omitempty"` 41 NicVF bool `json:"nicvf,omitempty"` 42 USB bool `json:"usb,omitempty"` 43 VhciInjection bool `json:"vhci,omitempty"` 44 Wifi bool `json:"wifi,omitempty"` 45 IEEE802154 bool `json:"ieee802154,omitempty"` 46 Sysctl bool `json:"sysctl,omitempty"` 47 Swap bool `json:"swap,omitempty"` 48 49 UseTmpDir bool `json:"tmpdir,omitempty"` 50 HandleSegv bool `json:"segv,omitempty"` 51 52 Trace bool `json:"trace,omitempty"` 53 54 CallComments bool `json:"callcomments,omitempty"` 55 56 LegacyOptions 57 } 58 59 // These are legacy options, they remain only for the sake of backward compatibility. 60 type LegacyOptions struct { 61 Collide bool `json:"collide,omitempty"` 62 Fault bool `json:"fault,omitempty"` 63 FaultCall int `json:"fault_call,omitempty"` 64 FaultNth int `json:"fault_nth,omitempty"` 65 } 66 67 // Check checks if the opts combination is valid or not. 68 // For example, Collide without Threaded is not valid. 69 // Invalid combinations must not be passed to Write. 70 func (opts Options) Check(OS string) error { 71 switch opts.Sandbox { 72 case "", sandboxNone, sandboxNamespace, sandboxSetuid, sandboxAndroid: 73 default: 74 return fmt.Errorf("unknown sandbox %v", opts.Sandbox) 75 } 76 if !opts.Threaded && opts.Collide { 77 // Collide requires threaded. 78 return errors.New("option Collide without Threaded") 79 } 80 if !opts.Repeat { 81 if opts.Procs > 1 { 82 // This does not affect generated code. 83 return errors.New("option Procs>1 without Repeat") 84 } 85 if opts.NetReset { 86 return errors.New("option NetReset without Repeat") 87 } 88 if opts.RepeatTimes > 1 { 89 return errors.New("option RepeatTimes without Repeat") 90 } 91 } 92 if opts.Sandbox == "" { 93 if opts.NetInjection { 94 return errors.New("option NetInjection without sandbox") 95 } 96 if opts.NetDevices { 97 return errors.New("option NetDevices without sandbox") 98 } 99 if opts.Cgroups { 100 return errors.New("option Cgroups without sandbox") 101 } 102 if opts.BinfmtMisc { 103 return errors.New("option BinfmtMisc without sandbox") 104 } 105 if opts.VhciInjection { 106 return errors.New("option VhciInjection without sandbox") 107 } 108 if opts.Wifi { 109 return errors.New("option Wifi without sandbox") 110 } 111 } 112 if opts.Sandbox == sandboxNamespace && !opts.UseTmpDir { 113 // This is borken and never worked. 114 // This tries to create syz-tmp dir in cwd, 115 // which will fail if procs>1 and on second run of the program. 116 return errors.New("option Sandbox=namespace without UseTmpDir") 117 } 118 if opts.NetReset && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) { 119 return errors.New("option NetReset without sandbox") 120 } 121 if opts.Cgroups && !opts.UseTmpDir { 122 return errors.New("option Cgroups without UseTmpDir") 123 } 124 return opts.checkLinuxOnly(OS) 125 } 126 127 func (opts Options) checkLinuxOnly(OS string) error { 128 if OS == targets.Linux { 129 return nil 130 } 131 if opts.NetInjection && OS != targets.OpenBSD && OS != targets.FreeBSD && OS != targets.NetBSD { 132 return fmt.Errorf("option NetInjection is not supported on %v", OS) 133 } 134 if opts.Sandbox == sandboxNamespace || 135 (opts.Sandbox == sandboxSetuid && OS != targets.OpenBSD && OS != targets.FreeBSD && OS != targets.NetBSD) || 136 opts.Sandbox == sandboxAndroid { 137 return fmt.Errorf("option Sandbox=%v is not supported on %v", opts.Sandbox, OS) 138 } 139 for name, opt := range map[string]*bool{ 140 "NetDevices": &opts.NetDevices, 141 "NetReset": &opts.NetReset, 142 "Cgroups": &opts.Cgroups, 143 "BinfmtMisc": &opts.BinfmtMisc, 144 "CloseFDs": &opts.CloseFDs, 145 "KCSAN": &opts.KCSAN, 146 "DevlinkPCI": &opts.DevlinkPCI, 147 "NicVF": &opts.NicVF, 148 "USB": &opts.USB, 149 "VhciInjection": &opts.VhciInjection, 150 "Wifi": &opts.Wifi, 151 "ieee802154": &opts.IEEE802154, 152 "Fault": &opts.Fault, 153 "Leak": &opts.Leak, 154 "Sysctl": &opts.Sysctl, 155 "Swap": &opts.Swap, 156 } { 157 if *opt { 158 return fmt.Errorf("option %v is not supported on %v", name, OS) 159 } 160 } 161 return nil 162 } 163 164 func DefaultOpts(cfg *mgrconfig.Config) Options { 165 opts := Options{ 166 Threaded: true, 167 Repeat: true, 168 Procs: cfg.Procs, 169 Slowdown: cfg.Timeouts.Slowdown, 170 Sandbox: cfg.Sandbox, 171 UseTmpDir: true, 172 HandleSegv: true, 173 CallComments: true, 174 } 175 if cfg.TargetOS == targets.Linux { 176 opts.NetInjection = true 177 opts.NetDevices = true 178 opts.NetReset = true 179 opts.Cgroups = true 180 opts.BinfmtMisc = true 181 opts.CloseFDs = true 182 opts.DevlinkPCI = true 183 opts.NicVF = true 184 opts.USB = true 185 opts.VhciInjection = true 186 opts.Wifi = true 187 opts.IEEE802154 = true 188 opts.Sysctl = true 189 opts.Swap = true 190 } 191 if cfg.Sandbox == "" || cfg.Sandbox == "setuid" { 192 opts.NetReset = false 193 } 194 if err := opts.Check(cfg.TargetOS); err != nil { 195 panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err)) 196 } 197 return opts 198 } 199 200 func (opts Options) Serialize() []byte { 201 data, err := json.Marshal(opts) 202 if err != nil { 203 panic(err) 204 } 205 return data 206 } 207 208 func deserializeLegacyOptions(data string, opts *Options) (int, error) { 209 ignoreBool := true 210 keyToTarget := map[string]any{ 211 "Threaded": &opts.Threaded, 212 "Collide": &opts.Collide, 213 "Repeat": &opts.Repeat, 214 "Procs": &opts.Procs, 215 "Sandbox": &opts.Sandbox, 216 "SandboxArg": &opts.SandboxArg, 217 "Fault": &opts.Fault, 218 "FaultCall": &opts.FaultCall, 219 "FaultNth": &opts.FaultNth, 220 "EnableTun": &opts.NetInjection, 221 "UseTmpDir": &opts.UseTmpDir, 222 "EnableCgroups": &opts.Cgroups, 223 "HandleSegv": &opts.HandleSegv, 224 "WaitRepeat": &ignoreBool, 225 "Debug": &ignoreBool, 226 "Repro": &ignoreBool, 227 } 228 229 data = strings.TrimSpace(data) 230 data = strings.TrimPrefix(data, "{") 231 data = strings.TrimSuffix(data, "}") 232 totalRead := 0 233 for _, token := range strings.Fields(data) { 234 key, value, keyValueFound := strings.Cut(token, ":") 235 if !keyValueFound { 236 return totalRead, fmt.Errorf("error splitting options token %v", token) 237 } 238 if _, ok := keyToTarget[key]; !ok { 239 return totalRead, fmt.Errorf("error, unexpected option key %v", key) 240 } 241 dest := keyToTarget[key] 242 n, err := fmt.Sscanf(value, "%v", dest) 243 if err != nil { 244 return totalRead, fmt.Errorf("failed to read %v", value) 245 } 246 totalRead += n 247 delete(keyToTarget, key) 248 } 249 250 return totalRead, nil 251 } 252 253 // Support for legacy formats. 254 func deserializeLegacyFormats(data []byte, opts *Options) error { 255 data = bytes.ReplaceAll(data, []byte("Sandbox: "), []byte("Sandbox:empty ")) 256 strData := string(data) 257 258 // We can distinguish between legacy formats by the number 259 // of fields. The formats we support have 14, 15 and 16 fields. 260 fieldsFound, err := deserializeLegacyOptions(strData, opts) 261 if err != nil { 262 return fmt.Errorf("failed to parse '%v': %w", strData, err) 263 } 264 if fieldsFound < 14 || fieldsFound > 16 { 265 return fmt.Errorf("%v params found, expected 14 <= x <= 16", fieldsFound) 266 } 267 268 if opts.Sandbox == "empty" { 269 opts.Sandbox = "" 270 } 271 return err 272 } 273 274 func DeserializeOptions(data []byte) (Options, error) { 275 opts := Options{ 276 Slowdown: 1, 277 // Before CloseFDs was added, close_fds() was always called, so default to true. 278 CloseFDs: true, 279 } 280 if err := json.Unmarshal(data, &opts); err == nil { 281 return opts, nil 282 } 283 err := deserializeLegacyFormats(data, &opts) 284 return opts, err 285 } 286 287 type Feature struct { 288 Description string 289 Enabled bool 290 } 291 292 type Features map[string]Feature 293 294 func defaultFeatures(value bool) Features { 295 return map[string]Feature{ 296 "tun": {"setup and use /dev/tun for packet injection", value}, 297 "net_dev": {"setup more network devices for testing", value}, 298 "net_reset": {"reset network namespace between programs", value}, 299 "cgroups": {"setup cgroups for testing", value}, 300 "binfmt_misc": {"setup binfmt_misc for testing", value}, 301 "close_fds": {"close fds after each program", value}, 302 "devlink_pci": {"setup devlink PCI device", value}, 303 "nic_vf": {"setup NIC VF device", value}, 304 "usb": {"setup and use /dev/raw-gadget for USB emulation", value}, 305 "vhci": {"setup and use /dev/vhci for hci packet injection", value}, 306 "wifi": {"setup and use mac80211_hwsim for wifi emulation", value}, 307 "ieee802154": {"setup and use mac802154_hwsim for emulation", value}, 308 "sysctl": {"setup sysctl's for fuzzing", value}, 309 "swap": {"setup and use a swap file", value}, 310 } 311 } 312 313 func ParseFeaturesFlags(enable, disable string, defaultValue bool) (Features, error) { 314 const ( 315 none = "none" 316 all = "all" 317 ) 318 if enable == none && disable == none { 319 return defaultFeatures(defaultValue), nil 320 } 321 if enable != none && disable != none { 322 return nil, fmt.Errorf("can't use -enable and -disable flags at the same time") 323 } 324 if enable == all || disable == "" { 325 return defaultFeatures(true), nil 326 } 327 if disable == all || enable == "" { 328 return defaultFeatures(false), nil 329 } 330 var items []string 331 var features Features 332 if enable != none { 333 items = strings.Split(enable, ",") 334 features = defaultFeatures(false) 335 } else { 336 items = strings.Split(disable, ",") 337 features = defaultFeatures(true) 338 } 339 for _, item := range items { 340 if _, ok := features[item]; !ok { 341 return nil, fmt.Errorf("unknown feature specified: %s", item) 342 } 343 feature := features[item] 344 feature.Enabled = enable != none 345 features[item] = feature 346 } 347 return features, nil 348 } 349 350 func PrintAvailableFeaturesFlags() { 351 fmt.Printf("available features for -enable and -disable:\n") 352 features := defaultFeatures(false) 353 var names []string 354 for name := range features { 355 names = append(names, name) 356 } 357 sort.Strings(names) 358 for _, name := range names { 359 fmt.Printf(" %s - %s\n", name, features[name].Description) 360 } 361 } 362 363 // This is the main configuration used by executor, only for testing. 364 var ExecutorOpts = Options{ 365 Threaded: true, 366 Repeat: true, 367 Procs: 2, 368 Slowdown: 1, 369 Sandbox: "none", 370 UseTmpDir: true, 371 } 372 373 func FeaturesToFlags(features flatrpc.Feature, manual Features) flatrpc.ExecEnv { 374 for feat := range flatrpc.EnumNamesFeature { 375 opt := FlatRPCFeaturesToCSource[feat] 376 if opt != "" && manual != nil && !manual[opt].Enabled { 377 features &= ^feat 378 } 379 } 380 var flags flatrpc.ExecEnv 381 if manual == nil || manual["net_reset"].Enabled { 382 flags |= flatrpc.ExecEnvEnableNetReset 383 } 384 if manual == nil || manual["cgroups"].Enabled { 385 flags |= flatrpc.ExecEnvEnableCgroups 386 } 387 if manual == nil || manual["close_fds"].Enabled { 388 flags |= flatrpc.ExecEnvEnableCloseFds 389 } 390 if features&flatrpc.FeatureExtraCoverage != 0 { 391 flags |= flatrpc.ExecEnvExtraCover 392 } 393 if features&flatrpc.FeatureDelayKcovMmap != 0 { 394 flags |= flatrpc.ExecEnvDelayKcovMmap 395 } 396 if features&flatrpc.FeatureNetInjection != 0 { 397 flags |= flatrpc.ExecEnvEnableTun 398 } 399 if features&flatrpc.FeatureNetDevices != 0 { 400 flags |= flatrpc.ExecEnvEnableNetDev 401 } 402 if features&flatrpc.FeatureDevlinkPCI != 0 { 403 flags |= flatrpc.ExecEnvEnableDevlinkPCI 404 } 405 if features&flatrpc.FeatureNicVF != 0 { 406 flags |= flatrpc.ExecEnvEnableNicVF 407 } 408 if features&flatrpc.FeatureVhciInjection != 0 { 409 flags |= flatrpc.ExecEnvEnableVhciInjection 410 } 411 if features&flatrpc.FeatureWifiEmulation != 0 { 412 flags |= flatrpc.ExecEnvEnableWifi 413 } 414 return flags 415 } 416 417 var FlatRPCFeaturesToCSource = map[flatrpc.Feature]string{ 418 flatrpc.FeatureNetInjection: "tun", 419 flatrpc.FeatureNetDevices: "net_dev", 420 flatrpc.FeatureDevlinkPCI: "devlink_pci", 421 flatrpc.FeatureNicVF: "nic_vf", 422 flatrpc.FeatureVhciInjection: "vhci", 423 flatrpc.FeatureWifiEmulation: "wifi", 424 flatrpc.FeatureUSBEmulation: "usb", 425 flatrpc.FeatureBinFmtMisc: "binfmt_misc", 426 flatrpc.FeatureLRWPANEmulation: "ieee802154", 427 flatrpc.FeatureSwap: "swap", 428 }