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 "bytes" 8 "encoding/json" 9 "errors" 10 "fmt" 11 12 "github.com/google/syzkaller/pkg/mgrconfig" 13) 14 15// Options control various aspects of source generation. 16// Dashboard also provides serialized Options along with syzkaller reproducers. 17type Options struct { 18 Threaded bool `json:"threaded,omitempty"` 19 Collide bool `json:"collide,omitempty"` 20 Repeat bool `json:"repeat,omitempty"` 21 RepeatTimes int `json:"repeat_times,omitempty"` // if non-0, repeat that many times 22 Procs int `json:"procs"` 23 Sandbox string `json:"sandbox"` 24 25 Fault bool `json:"fault,omitempty"` // inject fault into FaultCall/FaultNth 26 FaultCall int `json:"fault_call,omitempty"` 27 FaultNth int `json:"fault_nth,omitempty"` 28 29 // These options allow for a more fine-tuned control over the generated C code. 30 EnableTun bool `json:"tun,omitempty"` 31 UseTmpDir bool `json:"tmpdir,omitempty"` 32 EnableCgroups bool `json:"cgroups,omitempty"` 33 EnableNetdev bool `json:"netdev,omitempty"` 34 ResetNet bool `json:"resetnet,omitempty"` 35 HandleSegv bool `json:"segv,omitempty"` 36 37 // Generate code for use with repro package to prints log messages, 38 // which allows to detect hangs. 39 Repro bool `json:"repro,omitempty"` 40 Trace bool `json:"trace,omitempty"` 41} 42 43// Check checks if the opts combination is valid or not. 44// For example, Collide without Threaded is not valid. 45// Invalid combinations must not be passed to Write. 46func (opts Options) Check(OS string) error { 47 switch opts.Sandbox { 48 case "", sandboxNone, sandboxNamespace, sandboxSetuid: 49 default: 50 return fmt.Errorf("unknown sandbox %v", opts.Sandbox) 51 } 52 if !opts.Threaded && opts.Collide { 53 // Collide requires threaded. 54 return errors.New("Collide without Threaded") 55 } 56 if !opts.Repeat { 57 if opts.Procs > 1 { 58 // This does not affect generated code. 59 return errors.New("Procs>1 without Repeat") 60 } 61 if opts.ResetNet { 62 return errors.New("ResetNet without Repeat") 63 } 64 if opts.RepeatTimes > 1 { 65 return errors.New("RepeatTimes without Repeat") 66 } 67 } 68 if opts.Sandbox == "" { 69 if opts.EnableTun { 70 return errors.New("EnableTun without sandbox") 71 } 72 if opts.EnableCgroups { 73 return errors.New("EnableCgroups without sandbox") 74 } 75 if opts.EnableNetdev { 76 return errors.New("EnableNetdev without sandbox") 77 } 78 } 79 if opts.Sandbox == sandboxNamespace && !opts.UseTmpDir { 80 // This is borken and never worked. 81 // This tries to create syz-tmp dir in cwd, 82 // which will fail if procs>1 and on second run of the program. 83 return errors.New("Sandbox=namespace without UseTmpDir") 84 } 85 if opts.EnableCgroups && !opts.UseTmpDir { 86 return errors.New("EnableCgroups without UseTmpDir") 87 } 88 if opts.ResetNet && (opts.Sandbox == "" || opts.Sandbox == sandboxSetuid) { 89 return errors.New("ResetNet without sandbox") 90 } 91 return opts.checkLinuxOnly(OS) 92} 93 94func (opts Options) checkLinuxOnly(OS string) error { 95 if OS == linux { 96 return nil 97 } 98 if opts.EnableTun { 99 return fmt.Errorf("EnableTun is not supported on %v", OS) 100 } 101 if opts.EnableCgroups { 102 return fmt.Errorf("EnableCgroups is not supported on %v", OS) 103 } 104 if opts.EnableNetdev { 105 return fmt.Errorf("EnableNetdev is not supported on %v", OS) 106 } 107 if opts.ResetNet { 108 return fmt.Errorf("ResetNet is not supported on %v", OS) 109 } 110 if opts.Sandbox == sandboxNamespace || opts.Sandbox == sandboxSetuid { 111 return fmt.Errorf("Sandbox=%v is not supported on %v", opts.Sandbox, OS) 112 } 113 if opts.Fault { 114 return fmt.Errorf("Fault is not supported on %v", OS) 115 } 116 return nil 117} 118 119func DefaultOpts(cfg *mgrconfig.Config) Options { 120 opts := Options{ 121 Threaded: true, 122 Collide: true, 123 Repeat: true, 124 Procs: cfg.Procs, 125 Sandbox: cfg.Sandbox, 126 EnableTun: true, 127 EnableCgroups: true, 128 EnableNetdev: true, 129 ResetNet: true, 130 UseTmpDir: true, 131 HandleSegv: true, 132 Repro: true, 133 } 134 if cfg.TargetOS != linux { 135 opts.EnableTun = false 136 opts.EnableCgroups = false 137 opts.EnableNetdev = false 138 opts.ResetNet = false 139 } 140 if cfg.Sandbox == "" || cfg.Sandbox == "setuid" { 141 opts.ResetNet = false 142 } 143 if err := opts.Check(cfg.TargetOS); err != nil { 144 panic(fmt.Sprintf("DefaultOpts created bad opts: %v", err)) 145 } 146 return opts 147} 148 149func (opts Options) Serialize() []byte { 150 data, err := json.Marshal(opts) 151 if err != nil { 152 panic(err) 153 } 154 return data 155} 156 157func DeserializeOptions(data []byte) (Options, error) { 158 var opts Options 159 if err := json.Unmarshal(data, &opts); err == nil { 160 return opts, nil 161 } 162 // Support for legacy formats. 163 data = bytes.Replace(data, []byte("Sandbox: "), []byte("Sandbox:empty "), -1) 164 waitRepeat, debug := false, false 165 n, err := fmt.Sscanf(string(data), 166 "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+ 167 " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ 168 " HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", 169 &opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox, 170 &opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.EnableTun, &opts.UseTmpDir, 171 &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) 172 if err == nil { 173 if want := 14; n != want { 174 return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) 175 } 176 if opts.Sandbox == "empty" { 177 opts.Sandbox = "" 178 } 179 return opts, nil 180 } 181 n, err = fmt.Sscanf(string(data), 182 "{Threaded:%t Collide:%t Repeat:%t Procs:%d Sandbox:%s"+ 183 " Fault:%t FaultCall:%d FaultNth:%d EnableTun:%t UseTmpDir:%t"+ 184 " EnableCgroups:%t HandleSegv:%t WaitRepeat:%t Debug:%t Repro:%t}", 185 &opts.Threaded, &opts.Collide, &opts.Repeat, &opts.Procs, &opts.Sandbox, 186 &opts.Fault, &opts.FaultCall, &opts.FaultNth, &opts.EnableTun, &opts.UseTmpDir, 187 &opts.EnableCgroups, &opts.HandleSegv, &waitRepeat, &debug, &opts.Repro) 188 if err == nil { 189 if want := 15; n != want { 190 return opts, fmt.Errorf("failed to parse repro options: got %v fields, want %v", n, want) 191 } 192 if opts.Sandbox == "empty" { 193 opts.Sandbox = "" 194 } 195 return opts, nil 196 } 197 return opts, err 198} 199