github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/syz-verifier/rpcserver.go (about) 1 // Copyright 2021 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 package main 5 6 import ( 7 "errors" 8 "net" 9 "os" 10 "sync" 11 12 "github.com/google/syzkaller/pkg/log" 13 "github.com/google/syzkaller/pkg/rpctype" 14 ) 15 16 // RPCServer is a wrapper around the rpc.Server. It communicates with Runners, 17 // generates programs and sends complete Results for verification. 18 type RPCServer struct { 19 vrf *Verifier 20 port int 21 22 // protects next variables 23 mu sync.Mutex 24 // used to count the pools w/o UnsupportedCalls result 25 notChecked int 26 // vmTasks store the per-VM currently assigned tasks Ids 27 vmTasksInProgress map[int]map[int64]bool 28 } 29 30 func startRPCServer(vrf *Verifier) (*RPCServer, error) { 31 srv := &RPCServer{ 32 vrf: vrf, 33 notChecked: len(vrf.pools), 34 } 35 36 s, err := rpctype.NewRPCServer(vrf.addr, "Verifier", srv) 37 if err != nil { 38 return nil, err 39 } 40 41 log.Logf(0, "serving rpc on tcp://%v", s.Addr()) 42 srv.port = s.Addr().(*net.TCPAddr).Port 43 44 go s.Serve() 45 return srv, nil 46 } 47 48 // Connect notifies the RPCServer that a new Runner was started. 49 func (srv *RPCServer) Connect(a *rpctype.RunnerConnectArgs, r *rpctype.RunnerConnectRes) error { 50 r.CheckUnsupportedCalls = !srv.vrf.pools[a.Pool].checked 51 return nil 52 } 53 54 // UpdateUnsupported communicates to the server the list of system calls not 55 // supported by the kernel corresponding to this pool and updates the list of 56 // enabled system calls. This function is called once for each kernel. 57 // When all kernels have reported the list of unsupported system calls, the 58 // choice table will be created using only the system calls supported by all 59 // kernels. 60 func (srv *RPCServer) UpdateUnsupported(a *rpctype.UpdateUnsupportedArgs, r *int) error { 61 srv.mu.Lock() 62 defer srv.mu.Unlock() 63 64 if srv.vrf.pools[a.Pool].checked { 65 return nil 66 } 67 srv.vrf.pools[a.Pool].checked = true 68 vrf := srv.vrf 69 70 for _, unsupported := range a.UnsupportedCalls { 71 if c := vrf.target.Syscalls[unsupported.ID]; vrf.calls[c] { 72 vrf.reasons[c] = unsupported.Reason 73 } 74 } 75 76 srv.notChecked-- 77 if srv.notChecked == 0 { 78 vrf.finalizeCallSet(os.Stdout) 79 80 vrf.stats.SetSyscallMask(vrf.calls) 81 vrf.SetPrintStatAtSIGINT() 82 83 vrf.choiceTable = vrf.target.BuildChoiceTable(nil, vrf.calls) 84 vrf.progGeneratorInit.Done() 85 } 86 return nil 87 } 88 89 // NextExchange is called when a Runner requests a new program to execute and, 90 // potentially, wants to send a new Result to the RPCServer. 91 func (srv *RPCServer) NextExchange(a *rpctype.NextExchangeArgs, r *rpctype.NextExchangeRes) error { 92 if a.Info.Calls != nil { 93 srv.stopWaitResult(a.Pool, a.VM, a.ExecTaskID) 94 srv.vrf.PutExecResult(&ExecResult{ 95 Pool: a.Pool, 96 Hanged: a.Hanged, 97 Info: a.Info, 98 ExecTaskID: a.ExecTaskID, 99 }) 100 } 101 102 // TODO: NewEnvironment is the currently hardcoded logic. Relax it. 103 task := srv.vrf.GetRunnerTask(a.Pool, NewEnvironment) 104 srv.startWaitResult(a.Pool, a.VM, task.ID) 105 r.ExecTask = *task 106 107 return nil 108 } 109 110 func vmTasksKey(poolID, vmID int) int { 111 return poolID*1000 + vmID 112 } 113 114 func (srv *RPCServer) startWaitResult(poolID, vmID int, taskID int64) { 115 srv.mu.Lock() 116 defer srv.mu.Unlock() 117 118 if srv.vmTasksInProgress == nil { 119 srv.vmTasksInProgress = make(map[int]map[int64]bool) 120 } 121 122 if srv.vmTasksInProgress[vmTasksKey(poolID, vmID)] == nil { 123 srv.vmTasksInProgress[vmTasksKey(poolID, vmID)] = 124 make(map[int64]bool) 125 } 126 127 srv.vmTasksInProgress[vmTasksKey(poolID, vmID)][taskID] = true 128 } 129 130 func (srv *RPCServer) stopWaitResult(poolID, vmID int, taskID int64) { 131 srv.mu.Lock() 132 defer srv.mu.Unlock() 133 delete(srv.vmTasksInProgress[vmTasksKey(poolID, vmID)], taskID) 134 } 135 136 // cleanup is called when a vm.Instance crashes. 137 func (srv *RPCServer) cleanup(poolID, vmID int) { 138 srv.mu.Lock() 139 defer srv.mu.Unlock() 140 141 // Signal error for every VM related task and let upper level logic to process it. 142 for taskID := range srv.vmTasksInProgress[vmTasksKey(poolID, vmID)] { 143 srv.vrf.PutExecResult(&ExecResult{ 144 Pool: poolID, 145 ExecTaskID: taskID, 146 Crashed: true, 147 Error: errors.New("VM crashed during the task execution"), 148 }) 149 } 150 delete(srv.vmTasksInProgress, vmTasksKey(poolID, vmID)) 151 }