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