1// Copyright 2018 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// Runtest runs syzkaller test programs in sys/*/test/*. Start as:
5// $ syz-runtest -config manager.config
6// Also see pkg/runtest docs.
7package main
8
9import (
10	"errors"
11	"flag"
12	"fmt"
13	"io/ioutil"
14	"log"
15	"net"
16	"os"
17	"path/filepath"
18	"sync"
19	"time"
20
21	"github.com/google/syzkaller/pkg/instance"
22	"github.com/google/syzkaller/pkg/mgrconfig"
23	"github.com/google/syzkaller/pkg/osutil"
24	"github.com/google/syzkaller/pkg/report"
25	"github.com/google/syzkaller/pkg/rpctype"
26	"github.com/google/syzkaller/pkg/runtest"
27	"github.com/google/syzkaller/prog"
28	"github.com/google/syzkaller/sys"
29	"github.com/google/syzkaller/vm"
30)
31
32var (
33	flagConfig = flag.String("config", "", "manager config")
34	flagDebug  = flag.Bool("debug", false, "debug mode")
35)
36
37func main() {
38	flag.Parse()
39	cfg, err := mgrconfig.LoadFile(*flagConfig)
40	if err != nil {
41		log.Fatal(err)
42	}
43	target, err := prog.GetTarget(cfg.TargetOS, cfg.TargetArch)
44	if err != nil {
45		log.Fatal(err)
46	}
47	vmPool, err := vm.Create(cfg, *flagDebug)
48	if err != nil {
49		log.Fatal(err)
50	}
51	reporter, err := report.NewReporter(cfg)
52	if err != nil {
53		log.Fatal(err)
54	}
55	osutil.MkdirAll(cfg.Workdir)
56	mgr := &Manager{
57		cfg:              cfg,
58		target:           target,
59		vmPool:           vmPool,
60		reporter:         reporter,
61		debug:            *flagDebug,
62		requests:         make(chan *runtest.RunRequest, 2*vmPool.Count()),
63		checkResultC:     make(chan *rpctype.CheckArgs, 1),
64		checkResultReady: make(chan bool),
65		vmStop:           make(chan bool),
66		reqMap:           make(map[int]*runtest.RunRequest),
67		lastReq:          make(map[string]int),
68	}
69	s, err := rpctype.NewRPCServer(cfg.RPC, mgr)
70	if err != nil {
71		log.Fatalf("failed to create rpc server: %v", err)
72	}
73	mgr.port = s.Addr().(*net.TCPAddr).Port
74	go s.Serve()
75	var wg sync.WaitGroup
76	wg.Add(vmPool.Count())
77	fmt.Printf("booting VMs...\n")
78	for i := 0; i < vmPool.Count(); i++ {
79		i := i
80		go func() {
81			defer wg.Done()
82			name := fmt.Sprintf("vm-%v", i)
83			for {
84				rep, err := mgr.boot(name, i)
85				if err != nil {
86					log.Fatal(err)
87				}
88				if rep == nil {
89					return
90				}
91				if err := mgr.finishRequest(name, rep); err != nil {
92					log.Fatal(err)
93				}
94			}
95		}()
96	}
97	mgr.checkResult = <-mgr.checkResultC
98	close(mgr.checkResultReady)
99	enabledCalls := make(map[string]map[*prog.Syscall]bool)
100	for sandbox, ids := range mgr.checkResult.EnabledCalls {
101		calls := make(map[*prog.Syscall]bool)
102		for _, id := range ids {
103			calls[target.Syscalls[id]] = true
104		}
105		enabledCalls[sandbox] = calls
106	}
107	for _, feat := range mgr.checkResult.Features {
108		fmt.Printf("%-24v: %v\n", feat.Name, feat.Reason)
109	}
110	for sandbox, calls := range enabledCalls {
111		fmt.Printf("%-24v: %v calls enabled\n", sandbox+" sandbox", len(calls))
112	}
113	ctx := &runtest.Context{
114		Dir:          filepath.Join(cfg.Syzkaller, "sys", target.OS, "test"),
115		Target:       target,
116		Features:     mgr.checkResult.Features,
117		EnabledCalls: enabledCalls,
118		Requests:     mgr.requests,
119		LogFunc:      func(text string) { fmt.Println(text) },
120	}
121	err = ctx.Run()
122	close(vm.Shutdown)
123	wg.Wait()
124	if err != nil {
125		fmt.Println(err)
126		os.Exit(1)
127	}
128}
129
130type Manager struct {
131	cfg              *mgrconfig.Config
132	target           *prog.Target
133	vmPool           *vm.Pool
134	reporter         report.Reporter
135	requests         chan *runtest.RunRequest
136	checkResult      *rpctype.CheckArgs
137	checkResultReady chan bool
138	checkResultC     chan *rpctype.CheckArgs
139	vmStop           chan bool
140	port             int
141	debug            bool
142
143	reqMu   sync.Mutex
144	reqSeq  int
145	reqMap  map[int]*runtest.RunRequest
146	lastReq map[string]int
147}
148
149func (mgr *Manager) boot(name string, index int) (*report.Report, error) {
150	inst, err := mgr.vmPool.Create(index)
151	if err != nil {
152		return nil, fmt.Errorf("failed to create instance: %v", err)
153	}
154	defer inst.Close()
155
156	fwdAddr, err := inst.Forward(mgr.port)
157	if err != nil {
158		return nil, fmt.Errorf("failed to setup port forwarding: %v", err)
159	}
160	fuzzerBin, err := inst.Copy(mgr.cfg.SyzFuzzerBin)
161	if err != nil {
162		return nil, fmt.Errorf("failed to copy binary: %v", err)
163	}
164	executorBin, err := inst.Copy(mgr.cfg.SyzExecutorBin)
165	if err != nil {
166		return nil, fmt.Errorf("failed to copy binary: %v", err)
167	}
168	cmd := instance.FuzzerCmd(fuzzerBin, executorBin, name,
169		mgr.cfg.TargetOS, mgr.cfg.TargetArch, fwdAddr, mgr.cfg.Sandbox, mgr.cfg.Procs, 0,
170		mgr.cfg.Cover, mgr.debug, false, true)
171	outc, errc, err := inst.Run(time.Hour, mgr.vmStop, cmd)
172	if err != nil {
173		return nil, fmt.Errorf("failed to run fuzzer: %v", err)
174	}
175	rep := inst.MonitorExecution(outc, errc, mgr.reporter, true)
176	return rep, nil
177}
178
179func (mgr *Manager) finishRequest(name string, rep *report.Report) error {
180	mgr.reqMu.Lock()
181	defer mgr.reqMu.Unlock()
182	lastReq := mgr.lastReq[name]
183	req := mgr.reqMap[lastReq]
184	if lastReq == 0 || req == nil {
185		return fmt.Errorf("vm crash: %v\n%s\n%s", rep.Title, rep.Report, rep.Output)
186	}
187	delete(mgr.reqMap, lastReq)
188	delete(mgr.lastReq, name)
189	req.Err = fmt.Errorf("%v", rep.Title)
190	req.Output = rep.Report
191	if len(req.Output) == 0 {
192		req.Output = rep.Output
193	}
194	close(req.Done)
195	return nil
196}
197
198func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error {
199	r.GitRevision = sys.GitRevision
200	r.TargetRevision = mgr.target.Revision
201	r.AllSandboxes = true
202	select {
203	case <-mgr.checkResultReady:
204		r.CheckResult = mgr.checkResult
205	default:
206	}
207	return nil
208}
209
210func (mgr *Manager) Check(a *rpctype.CheckArgs, r *int) error {
211	if a.Error != "" {
212		log.Fatalf("machine check: %v", a.Error)
213	}
214	select {
215	case mgr.checkResultC <- a:
216	default:
217	}
218	return nil
219}
220
221func (mgr *Manager) Poll(a *rpctype.RunTestPollReq, r *rpctype.RunTestPollRes) error {
222	req := <-mgr.requests
223	if req == nil {
224		return nil
225	}
226	mgr.reqMu.Lock()
227	if mgr.lastReq[a.Name] != 0 {
228		log.Fatalf("double poll req from %v", a.Name)
229	}
230	mgr.reqSeq++
231	r.ID = mgr.reqSeq
232	mgr.reqMap[mgr.reqSeq] = req
233	mgr.lastReq[a.Name] = mgr.reqSeq
234	mgr.reqMu.Unlock()
235	if req.Bin != "" {
236		data, err := ioutil.ReadFile(req.Bin)
237		if err != nil {
238			log.Fatalf("failed to read bin file: %v", err)
239		}
240		r.Bin = data
241		return nil
242	}
243	r.Prog = req.P.Serialize()
244	r.Cfg = req.Cfg
245	r.Opts = req.Opts
246	r.Repeat = req.Repeat
247	return nil
248}
249
250func (mgr *Manager) Done(a *rpctype.RunTestDoneArgs, r *int) error {
251	mgr.reqMu.Lock()
252	lastReq := mgr.lastReq[a.Name]
253	if lastReq != a.ID {
254		log.Fatalf("wrong done id %v from %v", a.ID, a.Name)
255	}
256	req := mgr.reqMap[a.ID]
257	delete(mgr.reqMap, a.ID)
258	delete(mgr.lastReq, a.Name)
259	mgr.reqMu.Unlock()
260	if req == nil {
261		log.Fatalf("got done request for unknown id %v", a.ID)
262	}
263	req.Output = a.Output
264	req.Info = a.Info
265	if a.Error != "" {
266		req.Err = errors.New(a.Error)
267	}
268	close(req.Done)
269	return nil
270}
271