1// Copyright 2015 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// execprog executes a single program or a set of programs 5// and optionally prints information about execution. 6package main 7 8import ( 9 "bytes" 10 "flag" 11 "fmt" 12 "io/ioutil" 13 "os" 14 "runtime" 15 "sync" 16 "time" 17 18 "github.com/google/syzkaller/pkg/cover" 19 "github.com/google/syzkaller/pkg/host" 20 "github.com/google/syzkaller/pkg/ipc" 21 "github.com/google/syzkaller/pkg/ipc/ipcconfig" 22 "github.com/google/syzkaller/pkg/log" 23 "github.com/google/syzkaller/pkg/osutil" 24 "github.com/google/syzkaller/prog" 25 _ "github.com/google/syzkaller/sys" 26) 27 28var ( 29 flagOS = flag.String("os", runtime.GOOS, "target os") 30 flagArch = flag.String("arch", runtime.GOARCH, "target arch") 31 flagCoverFile = flag.String("coverfile", "", "write coverage to the file") 32 flagRepeat = flag.Int("repeat", 1, "repeat execution that many times (0 for infinite loop)") 33 flagProcs = flag.Int("procs", 1, "number of parallel processes to execute programs") 34 flagOutput = flag.Bool("output", false, "write programs and results to stdout") 35 flagFaultCall = flag.Int("fault_call", -1, "inject fault into this call (0-based)") 36 flagFaultNth = flag.Int("fault_nth", 0, "inject fault on n-th operation (0-based)") 37 flagHints = flag.Bool("hints", false, "do a hints-generation run") 38) 39 40func main() { 41 flag.Parse() 42 if len(flag.Args()) == 0 { 43 fmt.Fprintf(os.Stderr, "usage: execprog [flags] file-with-programs+\n") 44 flag.PrintDefaults() 45 os.Exit(1) 46 } 47 48 target, err := prog.GetTarget(*flagOS, *flagArch) 49 if err != nil { 50 log.Fatalf("%v", err) 51 } 52 53 entries := loadPrograms(target, flag.Args()) 54 if len(entries) == 0 { 55 return 56 } 57 58 features, err := host.Check(target) 59 if err != nil { 60 log.Fatalf("%v", err) 61 } 62 if _, err = host.Setup(target, features); err != nil { 63 log.Fatalf("%v", err) 64 } 65 config, execOpts := createConfig(target, entries, features) 66 67 ctx := &Context{ 68 entries: entries, 69 config: config, 70 execOpts: execOpts, 71 gate: ipc.NewGate(2**flagProcs, nil), 72 shutdown: make(chan struct{}), 73 repeat: *flagRepeat, 74 } 75 var wg sync.WaitGroup 76 wg.Add(*flagProcs) 77 for p := 0; p < *flagProcs; p++ { 78 pid := p 79 go func() { 80 defer wg.Done() 81 ctx.run(pid) 82 }() 83 } 84 osutil.HandleInterrupts(ctx.shutdown) 85 wg.Wait() 86} 87 88type Context struct { 89 entries []*prog.LogEntry 90 config *ipc.Config 91 execOpts *ipc.ExecOpts 92 gate *ipc.Gate 93 shutdown chan struct{} 94 logMu sync.Mutex 95 posMu sync.Mutex 96 repeat int 97 pos int 98 lastPrint time.Time 99} 100 101func (ctx *Context) run(pid int) { 102 env, err := ipc.MakeEnv(ctx.config, pid) 103 if err != nil { 104 log.Fatalf("failed to create ipc env: %v", err) 105 } 106 defer env.Close() 107 for { 108 select { 109 case <-ctx.shutdown: 110 return 111 default: 112 } 113 idx := ctx.getProgramIndex() 114 if ctx.repeat > 0 && idx >= len(ctx.entries)*ctx.repeat { 115 return 116 } 117 entry := ctx.entries[idx%len(ctx.entries)] 118 ctx.execute(pid, env, entry) 119 } 120} 121 122func (ctx *Context) execute(pid int, env *ipc.Env, entry *prog.LogEntry) { 123 // Limit concurrency window. 124 ticket := ctx.gate.Enter() 125 defer ctx.gate.Leave(ticket) 126 127 callOpts := ctx.execOpts 128 if *flagFaultCall == -1 && entry.Fault { 129 newOpts := *ctx.execOpts 130 newOpts.Flags |= ipc.FlagInjectFault 131 newOpts.FaultCall = entry.FaultCall 132 newOpts.FaultNth = entry.FaultNth 133 callOpts = &newOpts 134 } 135 if *flagOutput { 136 ctx.logProgram(pid, entry.P, callOpts) 137 } 138 output, info, failed, hanged, err := env.Exec(callOpts, entry.P) 139 if failed { 140 log.Logf(0, "BUG: executor-detected bug:\n%s", output) 141 } 142 if ctx.config.Flags&ipc.FlagDebug != 0 || err != nil { 143 log.Logf(0, "result: failed=%v hanged=%v err=%v\n\n%s", 144 failed, hanged, err, output) 145 } 146 if len(info) != 0 { 147 ctx.printCallResults(info) 148 if *flagHints { 149 ctx.printHints(entry.P, info) 150 } 151 } else { 152 log.Logf(1, "RESULT: no calls executed") 153 } 154 if *flagCoverFile != "" { 155 ctx.dumpCoverage(*flagCoverFile, info) 156 } 157} 158 159func (ctx *Context) logProgram(pid int, p *prog.Prog, callOpts *ipc.ExecOpts) { 160 strOpts := "" 161 if callOpts.Flags&ipc.FlagInjectFault != 0 { 162 strOpts = fmt.Sprintf(" (fault-call:%v fault-nth:%v)", 163 callOpts.FaultCall, callOpts.FaultNth) 164 } 165 data := p.Serialize() 166 ctx.logMu.Lock() 167 log.Logf(0, "executing program %v%v:\n%s", pid, strOpts, data) 168 ctx.logMu.Unlock() 169} 170 171func (ctx *Context) printCallResults(info []ipc.CallInfo) { 172 for i, inf := range info { 173 if inf.Flags&ipc.CallExecuted == 0 { 174 continue 175 } 176 flags := "" 177 if inf.Flags&ipc.CallFinished == 0 { 178 flags += " unfinished" 179 } 180 if inf.Flags&ipc.CallBlocked != 0 { 181 flags += " blocked" 182 } 183 if inf.Flags&ipc.CallFaultInjected != 0 { 184 flags += " faulted" 185 } 186 log.Logf(1, "CALL %v: signal %v, coverage %v errno %v%v", 187 i, len(inf.Signal), len(inf.Cover), inf.Errno, flags) 188 } 189} 190 191func (ctx *Context) printHints(p *prog.Prog, info []ipc.CallInfo) { 192 ncomps, ncandidates := 0, 0 193 for i := range p.Calls { 194 if *flagOutput { 195 fmt.Printf("call %v:\n", i) 196 } 197 comps := info[i].Comps 198 for v, args := range comps { 199 ncomps += len(args) 200 if *flagOutput { 201 fmt.Printf("comp 0x%x:", v) 202 for arg := range args { 203 fmt.Printf(" 0x%x", arg) 204 } 205 fmt.Printf("\n") 206 } 207 } 208 p.MutateWithHints(i, comps, func(p *prog.Prog) { 209 ncandidates++ 210 if *flagOutput { 211 log.Logf(1, "PROGRAM:\n%s", p.Serialize()) 212 } 213 }) 214 } 215 log.Logf(0, "ncomps=%v ncandidates=%v", ncomps, ncandidates) 216} 217 218func (ctx *Context) dumpCoverage(coverFile string, info []ipc.CallInfo) { 219 for i, inf := range info { 220 log.Logf(0, "call #%v: signal %v, coverage %v", i, len(inf.Signal), len(inf.Cover)) 221 if len(inf.Cover) == 0 { 222 continue 223 } 224 buf := new(bytes.Buffer) 225 for _, pc := range inf.Cover { 226 fmt.Fprintf(buf, "0x%x\n", cover.RestorePC(pc, 0xffffffff)) 227 } 228 err := osutil.WriteFile(fmt.Sprintf("%v.%v", coverFile, i), buf.Bytes()) 229 if err != nil { 230 log.Fatalf("failed to write coverage file: %v", err) 231 } 232 } 233} 234 235func (ctx *Context) getProgramIndex() int { 236 ctx.posMu.Lock() 237 idx := ctx.pos 238 ctx.pos++ 239 if idx%len(ctx.entries) == 0 && time.Since(ctx.lastPrint) > 5*time.Second { 240 log.Logf(0, "executed programs: %v", idx) 241 ctx.lastPrint = time.Now() 242 } 243 ctx.posMu.Unlock() 244 return idx 245} 246 247func loadPrograms(target *prog.Target, files []string) []*prog.LogEntry { 248 var entries []*prog.LogEntry 249 for _, fn := range files { 250 data, err := ioutil.ReadFile(fn) 251 if err != nil { 252 log.Fatalf("failed to read log file: %v", err) 253 } 254 entries = append(entries, target.ParseLog(data)...) 255 } 256 log.Logf(0, "parsed %v programs", len(entries)) 257 return entries 258} 259 260func createConfig(target *prog.Target, entries []*prog.LogEntry, features *host.Features) ( 261 *ipc.Config, *ipc.ExecOpts) { 262 config, execOpts, err := ipcconfig.Default(target) 263 if err != nil { 264 log.Fatalf("%v", err) 265 } 266 if config.Flags&ipc.FlagSignal != 0 { 267 execOpts.Flags |= ipc.FlagCollectCover 268 } 269 if *flagCoverFile != "" { 270 config.Flags |= ipc.FlagSignal 271 execOpts.Flags |= ipc.FlagCollectCover 272 execOpts.Flags &^= ipc.FlagDedupCover 273 } 274 if *flagHints { 275 if execOpts.Flags&ipc.FlagCollectCover != 0 { 276 execOpts.Flags ^= ipc.FlagCollectCover 277 } 278 execOpts.Flags |= ipc.FlagCollectComps 279 } 280 if *flagFaultCall >= 0 { 281 config.Flags |= ipc.FlagEnableFault 282 execOpts.Flags |= ipc.FlagInjectFault 283 execOpts.FaultCall = *flagFaultCall 284 execOpts.FaultNth = *flagFaultNth 285 } 286 handled := make(map[string]bool) 287 for _, entry := range entries { 288 for _, call := range entry.P.Calls { 289 handled[call.Meta.CallName] = true 290 } 291 } 292 if features[host.FeatureNetworkInjection].Enabled { 293 config.Flags |= ipc.FlagEnableTun 294 } 295 if features[host.FeatureNetworkDevices].Enabled { 296 config.Flags |= ipc.FlagEnableNetDev 297 } 298 return config, execOpts 299} 300