// Copyright 2021 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 main import ( "flag" "fmt" "log" "runtime" "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/ipc/ipcconfig" "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/prog" ) // Runner is responsible of running programs sent by the host via RPC and // reporting the execution results back to the host. type Runner struct { vrf *rpctype.RPCClient target *prog.Target opts *ipc.ExecOpts config *ipc.Config pool, vm int newEnv bool } func main() { flagPool := flag.Int("pool", 0, "index of pool it corresponds to") flagVM := flag.Int("vm", 0, "index of VM that started the Runner") flagAddr := flag.String("addr", "", "verifier rpc address") flagOS := flag.String("os", runtime.GOOS, "target OS") flagArch := flag.String("arch", runtime.GOARCH, "target arch") flagEnv := flag.Bool("new-env", true, "create a new environment for each program") flag.Parse() target, err := prog.GetTarget(*flagOS, *flagArch) if err != nil { log.Fatalf("failed to configure target: %v", err) } config, opts, err := ipcconfig.Default(target) if err != nil { log.Fatalf("failed to create default ipc config: %v", err) } timeouts := config.Timeouts vrf, err := rpctype.NewRPCClient(*flagAddr, timeouts.Scale) if err != nil { log.Fatalf("failed to connect to verifier : %v", err) } rn := &Runner{ vrf: vrf, target: target, opts: opts, config: config, pool: *flagPool, vm: *flagVM, newEnv: *flagEnv, } a := &rpctype.RunnerConnectArgs{ Pool: rn.pool, VM: rn.vm, } r := &rpctype.RunnerConnectRes{} if err := vrf.Call("Verifier.Connect", a, r); err != nil { log.Fatalf("failed to connect to verifier: %v", err) } enabled := make(map[*prog.Syscall]bool) for _, c := range target.Syscalls { enabled[c] = true } if r.CheckUnsupportedCalls { _, unsupported, err := host.DetectSupportedSyscalls(target, ipc.FlagsToSandbox(config.Flags), enabled) if err != nil { log.Fatalf("failed to get unsupported system calls: %v", err) } calls := make([]rpctype.SyscallReason, 0) for c, reason := range unsupported { calls = append(calls, rpctype.SyscallReason{ ID: c.ID, Reason: fmt.Sprintf("%s (not supported on kernel %d)", reason, rn.pool)}) } a := &rpctype.UpdateUnsupportedArgs{Pool: rn.pool, UnsupportedCalls: calls} if err := vrf.Call("Verifier.UpdateUnsupported", a, nil); err != nil { log.Fatalf("failed to send unsupported system calls: %v", err) } } res := &rpctype.NextExchangeRes{} if err := rn.vrf.Call("Verifier.NextExchange", &rpctype.NextExchangeArgs{Pool: rn.pool, VM: rn.vm}, res); err != nil { log.Fatalf("failed to get initial program: %v", err) } rn.Run(res.Prog, res.ID) } // Run is responsible for requesting new programs from the verifier, executing them and then sending back the Result. // TODO: Implement functionality to execute several programs at once and send back a slice of results. func (rn *Runner) Run(firstProg []byte, taskID int64) { p, id := firstProg, taskID env, err := ipc.MakeEnv(rn.config, 0) if err != nil { log.Fatalf("failed to create initial execution environment: %v", err) } for { prog, err := rn.target.Deserialize(p, prog.NonStrict) if err != nil { log.Fatalf("failed to deserialise new program: %v", err) } log.Printf("executing program") // watchdog for monitor _, info, hanged, err := env.Exec(rn.opts, prog) if err != nil { log.Fatalf("failed to execute the program: %v", err) } a := &rpctype.NextExchangeArgs{ Pool: rn.pool, VM: rn.vm, Hanged: hanged, Info: *info, ExecTaskID: id, } r := &rpctype.NextExchangeRes{} if err := rn.vrf.Call("Verifier.NextExchange", a, r); err != nil { log.Fatalf("failed to make exchange with verifier: %v", err) } p, id = r.Prog, r.ID if !rn.newEnv { continue } err = env.Close() if err != nil { log.Fatalf("failed to close the execution environment: %v", err) } env, err = ipc.MakeEnv(rn.config, 0) if err != nil { log.Fatalf("failed to create new execution environmentL %v", err) } } }