| // 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 |
| } |