// 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" "encoding/json" "errors" "fmt" "github.com/google/syzkaller/pkg/mgrconfig" ) // 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"` 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"` FaultNth int `json:"fault_nth,omitempty"` // These options allow for a more fine-tuned control over the generated C code. EnableTun bool `json:"tun,omitempty"` UseTmpDir bool `json:"tmpdir,omitempty"` EnableCgroups bool `json:"cgroups,omitempty"` EnableNetdev bool `json:"netdev,omitempty"` ResetNet bool `json:"resetnet,omitempty"` HandleSegv bool `json:"segv,omitempty"` // Generate code for use with repro package to prints log messages, // 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. // For example, Collide without Threaded is not valid. // Invalid combinations must not be passed to Write. func (opts Options) Check(OS string) error { switch opts.Sandbox { case "", sandboxNone, sandboxNamespace, sandboxSetuid: default: return fmt.Errorf("unknown sandbox %v", opts.Sandbox) } if !opts.Threaded && opts.Collide { // Collide requires threaded. return errors.New("Collide without Threaded") } if !opts.Repeat { if opts.Procs > 1 { // This does not affect generated code. return errors.New("Procs>1 without Repeat") } if opts.ResetNet { return errors.New("ResetNet without Repeat") } if opts.RepeatTimes > 1 { return errors.New("RepeatTimes without Repeat") } } if opts.Sandbox == "" { if opts.EnableTun { return errors.New("EnableTun without sandbox") } if opts.EnableCgroups { return errors.New("EnableCgroups without sandbox") } if opts.EnableNetdev { return errors.New("EnableNetdev without sandbox") } } if opts.Sandbox == sandboxNamespace && !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") } if opts.EnableCgroups && !opts.UseTmpDir { return errors.New("EnableCgroups without UseTmpDir") } if opts.ResetNet && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) { return errors.New("ResetNet without sandbox") } return opts.checkLinuxOnly(OS) } func (opts Options) checkLinuxOnly(OS string) error { if OS == linux { return nil } if opts.EnableTun { return fmt.Errorf("EnableTun is not supported on %v", OS) } if opts.EnableCgroups { return fmt.Errorf("EnableCgroups is not supported on %v", OS) } if opts.EnableNetdev { return fmt.Errorf("EnableNetdev is not supported on %v", OS) } if opts.ResetNet { return fmt.Errorf("ResetNet is not supported on %v", OS) } if opts.Sandbox == sandboxNamespace || opts.Sandbox == sandboxSetuid { return fmt.Errorf("Sandbox=%v is not supported on %v", opts.Sandbox, OS) } if opts.Fault { return fmt.Errorf("Fault is not supported on %v", OS) } return nil } func DefaultOpts(cfg *mgrconfig.Config) Options { opts := Options{ Threaded: true, Collide: true, Repeat: true, Procs: cfg.Procs, Sandbox: cfg.Sandbox, EnableTun: true, EnableCgroups: true, EnableNetdev: true, ResetNet: true, UseTmpDir: true, HandleSegv: true, Repro: true, } if cfg.TargetOS != linux { opts.EnableTun = false opts.EnableCgroups = false opts.EnableNetdev = false opts.ResetNet = false } if cfg.Sandbox == "" || cfg.Sandbox == "setuid" { opts.ResetNet = false } if err := opts.Check(cfg.TargetOS); err != nil { panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err)) } return opts } func (opts Options) Serialize() []byte { data, err := json.Marshal(opts) if err != nil { panic(err) } return data } func DeserializeOptions(data []byte) (Options, error) { var opts Options if err := json.Unmarshal(data, &opts); err == nil { return opts, nil } // Support for legacy formats. data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1) waitRepeat, debug := false, false 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, &waitRepeat, &debug, &opts.Repro) if err == nil { 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 } 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"+ " EnableCgroups:%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.EnableCgroups, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) if err == nil { if want := 15; 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 } return opts, err }