diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-05-17 17:20:45 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-05-21 09:46:36 +0000 |
| commit | 1d3c25e7679384c386e7710ea11e364ce9f9e028 (patch) | |
| tree | c154a9578f63037babef096e85fe9a34183ee640 /pkg | |
| parent | a38fb99b3fbff0c988e64bf4bf277071e18b18af (diff) | |
pkg/ipc: remove ProgInfo
Switch to flatrpc.ProgInfo.
Note: this disables syz-runtest and syz-verifier.
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/flatrpc/helpers.go | 42 | ||||
| -rw-r--r-- | pkg/fuzzer/fuzzer.go | 17 | ||||
| -rw-r--r-- | pkg/fuzzer/fuzzer_test.go | 12 | ||||
| -rw-r--r-- | pkg/fuzzer/job.go | 38 | ||||
| -rw-r--r-- | pkg/fuzzer/job_test.go | 14 | ||||
| -rw-r--r-- | pkg/fuzzer/queue/queue.go | 6 | ||||
| -rw-r--r-- | pkg/ipc/ipc.go | 100 | ||||
| -rw-r--r-- | pkg/ipc/ipc_test.go | 16 | ||||
| -rw-r--r-- | pkg/rpctype/rpctype.go | 159 | ||||
| -rw-r--r-- | pkg/runtest/run.go | 63 | ||||
| -rw-r--r-- | pkg/vminfo/features.go | 4 | ||||
| -rw-r--r-- | pkg/vminfo/syscalls.go | 18 | ||||
| -rw-r--r-- | pkg/vminfo/vminfo_test.go | 8 |
13 files changed, 166 insertions, 331 deletions
diff --git a/pkg/flatrpc/helpers.go b/pkg/flatrpc/helpers.go index 92e583400..843b281d3 100644 --- a/pkg/flatrpc/helpers.go +++ b/pkg/flatrpc/helpers.go @@ -3,6 +3,11 @@ package flatrpc +import ( + "slices" + "syscall" +) + const AllFeatures = ^Feature(0) // Flatbuffers compiler adds T suffix to object API types, which are actual structs representing types. @@ -26,3 +31,40 @@ type CallInfo = CallInfoRawT type Comparison = ComparisonRawT type ProgInfo = ProgInfoRawT type ExecResult = ExecResultRawT + +func (pi *ProgInfo) Clone() *ProgInfo { + if pi == nil { + return nil + } + ret := *pi + ret.Extra = ret.Extra.clone() + ret.Calls = make([]*CallInfo, len(pi.Calls)) + for i, call := range pi.Calls { + ret.Calls[i] = call.clone() + } + return &ret +} + +func (ci *CallInfo) clone() *CallInfo { + if ci == nil { + return nil + } + ret := *ci + ret.Signal = slices.Clone(ret.Signal) + ret.Cover = slices.Clone(ret.Cover) + ret.Comps = slices.Clone(ret.Comps) + return &ret +} + +func EmptyProgInfo(calls int) *ProgInfo { + info := &ProgInfo{} + for i := 0; i < calls; i++ { + info.Calls = append(info.Calls, &CallInfo{ + // Store some unsuccessful errno in the case we won't get any result. + // It also won't have CallExecuted flag, but it's handy to make it + // look failed based on errno as well. + Error: int32(syscall.ENOSYS), + }) + } + return info +} diff --git a/pkg/fuzzer/fuzzer.go b/pkg/fuzzer/fuzzer.go index 92b4c8bf1..f079e1dc7 100644 --- a/pkg/fuzzer/fuzzer.go +++ b/pkg/fuzzer/fuzzer.go @@ -124,12 +124,12 @@ func (fuzzer *Fuzzer) processResult(req *queue.Request, res *queue.Result, flags // If we are already triaging this exact prog, this is flaky coverage. if req.ExecOpts.ExecFlags&flatrpc.ExecFlagCollectSignal > 0 && res.Info != nil && !inTriage { for call, info := range res.Info.Calls { - fuzzer.triageProgCall(req.Prog, &info, call, flags) + fuzzer.triageProgCall(req.Prog, info, call, flags) } - fuzzer.triageProgCall(req.Prog, &res.Info.Extra, -1, flags) + fuzzer.triageProgCall(req.Prog, res.Info.Extra, -1, flags) } if res.Info != nil { - fuzzer.statExecTime.Add(int(res.Info.Elapsed.Milliseconds())) + fuzzer.statExecTime.Add(int(res.Info.Elapsed / 1e6)) } } @@ -148,7 +148,10 @@ type Config struct { NewInputFilter func(call string) bool } -func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *ipc.CallInfo, call int, flags ProgTypes) { +func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *flatrpc.CallInfo, call int, flags ProgTypes) { + if info == nil { + return + } prio := signalPrio(p, info, call) newMaxSignal := fuzzer.Cover.addRawMaxSignal(info.Signal, prio) if newMaxSignal.Empty() { @@ -166,18 +169,18 @@ func (fuzzer *Fuzzer) triageProgCall(p *prog.Prog, info *ipc.CallInfo, call int, fuzzer.startJob(fuzzer.statJobsTriage, &triageJob{ p: p.Clone(), call: call, - info: *info, + info: info, newSignal: newMaxSignal, flags: flags, queue: queue.Append(), }) } -func signalPrio(p *prog.Prog, info *ipc.CallInfo, call int) (prio uint8) { +func signalPrio(p *prog.Prog, info *flatrpc.CallInfo, call int) (prio uint8) { if call == -1 { return 0 } - if info.Errno == 0 { + if info.Error == 0 { prio |= 1 << 1 } if !p.Target.CallContainsAny(p.Calls[call]) { diff --git a/pkg/fuzzer/fuzzer_test.go b/pkg/fuzzer/fuzzer_test.go index cb58a4e10..07e09e25a 100644 --- a/pkg/fuzzer/fuzzer_test.go +++ b/pkg/fuzzer/fuzzer_test.go @@ -186,16 +186,16 @@ var crc32q = crc32.MakeTable(0xD5828281) func emulateExec(req *queue.Request) (*queue.Result, string, error) { serializedLines := bytes.Split(req.Prog.Serialize(), []byte("\n")) - var info ipc.ProgInfo + var info flatrpc.ProgInfo for i, call := range req.Prog.Calls { - cover := uint32(call.Meta.ID*1024) + - crc32.Checksum(serializedLines[i], crc32q)%4 - callInfo := ipc.CallInfo{} + cover := []uint32{uint32(call.Meta.ID*1024) + + crc32.Checksum(serializedLines[i], crc32q)%4} + callInfo := &flatrpc.CallInfo{} if req.ExecOpts.ExecFlags&flatrpc.ExecFlagCollectCover > 0 { - callInfo.Cover = []uint32{cover} + callInfo.Cover = cover } if req.ExecOpts.ExecFlags&flatrpc.ExecFlagCollectSignal > 0 { - callInfo.Signal = []uint32{cover} + callInfo.Signal = cover } info.Calls = append(info.Calls, callInfo) } diff --git a/pkg/fuzzer/job.go b/pkg/fuzzer/job.go index 4e605f1c2..a5a22b2e8 100644 --- a/pkg/fuzzer/job.go +++ b/pkg/fuzzer/job.go @@ -11,7 +11,6 @@ import ( "github.com/google/syzkaller/pkg/cover" "github.com/google/syzkaller/pkg/flatrpc" "github.com/google/syzkaller/pkg/fuzzer/queue" - "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/pkg/stats" "github.com/google/syzkaller/prog" @@ -83,7 +82,7 @@ func candidateRequest(fuzzer *Fuzzer, input Candidate) (*queue.Request, ProgType type triageJob struct { p *prog.Prog call int - info ipc.CallInfo + info *flatrpc.CallInfo newSignal signal.Signal flags ProgTypes fuzzer *Fuzzer @@ -172,7 +171,7 @@ func (job *triageJob) deflake(exec func(*queue.Request, ProgTypes) *queue.Result stop = true return } - if !reexecutionSuccess(result.Info, &job.info, job.call) { + if !reexecutionSuccess(result.Info, job.info, job.call) { // The call was not executed or failed. continue } @@ -213,7 +212,7 @@ func (job *triageJob) minimize(newSignal signal.Signal) (stop bool) { return false } info := result.Info - if !reexecutionSuccess(info, &job.info, call1) { + if !reexecutionSuccess(info, job.info, call1) { // The call was not executed or failed. continue } @@ -227,25 +226,28 @@ func (job *triageJob) minimize(newSignal signal.Signal) (stop bool) { return stop } -func reexecutionSuccess(info *ipc.ProgInfo, oldInfo *ipc.CallInfo, call int) bool { +func reexecutionSuccess(info *flatrpc.ProgInfo, oldInfo *flatrpc.CallInfo, call int) bool { if info == nil || len(info.Calls) == 0 { return false } if call != -1 { // Don't minimize calls from successful to unsuccessful. // Successful calls are much more valuable. - if oldInfo.Errno == 0 && info.Calls[call].Errno != 0 { + if oldInfo.Error == 0 && info.Calls[call].Error != 0 { return false } return len(info.Calls[call].Signal) != 0 } - return len(info.Extra.Signal) != 0 + return info.Extra != nil && len(info.Extra.Signal) != 0 } -func getSignalAndCover(p *prog.Prog, info *ipc.ProgInfo, call int) (signal.Signal, []uint32) { - inf := &info.Extra +func getSignalAndCover(p *prog.Prog, info *flatrpc.ProgInfo, call int) (signal.Signal, []uint32) { + inf := info.Extra if call != -1 { - inf = &info.Calls[call] + inf = info.Calls[call] + } + if inf == nil { + return nil, nil } return signal.FromRaw(inf.Signal, signalPrio(p, inf, call)), inf.Cover } @@ -332,7 +334,7 @@ func (job *smashJob) faultInjection(fuzzer *Fuzzer) { } info := result.Info if info != nil && len(info.Calls) > job.call && - info.Calls[job.call].Flags&ipc.CallFaultInjected == 0 { + info.Calls[job.call].Flags&flatrpc.CallFlagFaultInjected == 0 { break } } @@ -358,13 +360,17 @@ func (job *hintsJob) run(fuzzer *Fuzzer) { if result.Stop() || result.Info == nil { return } + got := make(prog.CompMap) + for _, cmp := range result.Info.Calls[job.call].Comps { + got.AddComp(cmp.Op1, cmp.Op2) + } + if len(got) == 0 { + return + } if i == 0 { - comps = result.Info.Calls[job.call].Comps - if len(comps) == 0 { - return - } + comps = got } else { - comps.InplaceIntersect(result.Info.Calls[job.call].Comps) + comps.InplaceIntersect(got) } } diff --git a/pkg/fuzzer/job_test.go b/pkg/fuzzer/job_test.go index b7718134b..70d083be5 100644 --- a/pkg/fuzzer/job_test.go +++ b/pkg/fuzzer/job_test.go @@ -6,8 +6,8 @@ package fuzzer import ( "testing" + "github.com/google/syzkaller/pkg/flatrpc" "github.com/google/syzkaller/pkg/fuzzer/queue" - "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/signal" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" @@ -24,7 +24,7 @@ func TestDeflakeFail(t *testing.T) { testJob := &triageJob{ p: prog, - info: ipc.CallInfo{}, + info: &flatrpc.CallInfo{}, newSignal: signal.FromRaw([]uint32{0, 1, 2, 3, 4}, 0), } @@ -50,7 +50,7 @@ func TestDeflakeSuccess(t *testing.T) { testJob := &triageJob{ p: prog, - info: ipc.CallInfo{}, + info: &flatrpc.CallInfo{}, newSignal: signal.FromRaw([]uint32{0, 1, 2}, 0), } run := 0 @@ -80,12 +80,12 @@ func TestDeflakeSuccess(t *testing.T) { assert.ElementsMatch(t, []uint32{0, 2}, ret.newStableSignal.ToRaw()) } -func fakeResult(errno int, signal, cover []uint32) *queue.Result { +func fakeResult(errno int32, signal, cover []uint32) *queue.Result { return &queue.Result{ - Info: &ipc.ProgInfo{ - Calls: []ipc.CallInfo{ + Info: &flatrpc.ProgInfo{ + Calls: []*flatrpc.CallInfo{ { - Errno: errno, + Error: errno, Signal: signal, Cover: cover, }, diff --git a/pkg/fuzzer/queue/queue.go b/pkg/fuzzer/queue/queue.go index 36226299a..0e578c6f9 100644 --- a/pkg/fuzzer/queue/queue.go +++ b/pkg/fuzzer/queue/queue.go @@ -135,7 +135,7 @@ func (r *Request) initChannel() { } type Result struct { - Info *ipc.ProgInfo + Info *flatrpc.ProgInfo Output []byte Status Status Err error // More details in case of ExecFailure. @@ -143,9 +143,7 @@ type Result struct { func (r *Result) clone() *Result { ret := *r - if ret.Info != nil { - ret.Info = ret.Info.Clone() - } + ret.Info = ret.Info.Clone() return &ret } diff --git a/pkg/ipc/ipc.go b/pkg/ipc/ipc.go index 0e12fa2bd..9b4f77d66 100644 --- a/pkg/ipc/ipc.go +++ b/pkg/ipc/ipc.go @@ -6,14 +6,12 @@ package ipc import ( "fmt" "io" - "maps" "os" "os/exec" "path/filepath" "slices" "strings" "sync" - "syscall" "time" "unsafe" @@ -53,60 +51,6 @@ type Config struct { Timeouts targets.Timeouts } -type CallFlags uint32 - -const ( - CallExecuted CallFlags = 1 << iota // was started at all - CallFinished // finished executing (rather than blocked forever) - CallBlocked // finished but blocked during execution - CallFaultInjected // fault was injected into this call -) - -type CallInfo struct { - Flags CallFlags - Signal []uint32 // feedback signal, filled if FlagSignal is set - Cover []uint32 // per-call coverage, filled if FlagSignal is set and cover == true, - // if dedup == false, then cov effectively contains a trace, otherwise duplicates are removed - Comps prog.CompMap // per-call comparison operands - Errno int // call errno (0 if the call was successful) -} - -func (ci *CallInfo) Clone() CallInfo { - ret := *ci - ret.Signal = slices.Clone(ret.Signal) - ret.Cover = slices.Clone(ret.Cover) - ret.Comps = maps.Clone(ret.Comps) - return ret -} - -type ProgInfo struct { - Calls []CallInfo - Extra CallInfo // stores Signal and Cover collected from background threads - Elapsed time.Duration // total execution time of the program - Freshness int // number of programs executed in the same process before this one -} - -func (pi *ProgInfo) Clone() *ProgInfo { - ret := *pi - ret.Extra = ret.Extra.Clone() - ret.Calls = slices.Clone(ret.Calls) - for i, call := range ret.Calls { - ret.Calls[i] = call.Clone() - } - return &ret -} - -func EmptyProgInfo(calls int) *ProgInfo { - info := &ProgInfo{Calls: make([]CallInfo, calls)} - for i := range info.Calls { - // Store some unsuccessful errno in the case we won't get any result. - // It also won't have CallExecuted flag, but it's handy to make it - // look failed based on errno as well. - info.Calls[i].Errno = int(syscall.ENOSYS) - } - return info -} - type Env struct { in []byte out []byte @@ -312,7 +256,8 @@ func (env *Env) Close() error { // info: per-call info // hanged: program hanged and was killed // err0: failed to start the process or bug in executor itself. -func (env *Env) ExecProg(opts *ExecOpts, progData []byte) (output []byte, info *ProgInfo, hanged bool, err0 error) { +func (env *Env) ExecProg(opts *ExecOpts, progData []byte) ( + output []byte, info *flatrpc.ProgInfo, hanged bool, err0 error) { ncalls, err := prog.ExecCallCount(progData) if err != nil { err0 = err @@ -345,7 +290,7 @@ func (env *Env) ExecProg(opts *ExecOpts, progData []byte) (output []byte, info * info, err0 = env.parseOutput(opts, ncalls) if info != nil { - info.Elapsed = elapsed + info.Elapsed = uint64(elapsed) info.Freshness = env.cmd.freshness } env.cmd.freshness++ @@ -356,7 +301,7 @@ func (env *Env) ExecProg(opts *ExecOpts, progData []byte) (output []byte, info * return } -func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info *ProgInfo, hanged bool, err0 error) { +func (env *Env) Exec(opts *ExecOpts, p *prog.Prog) (output []byte, info *flatrpc.ProgInfo, hanged bool, err0 error) { progData, err := p.SerializeForExec() if err != nil { err0 = err @@ -396,21 +341,21 @@ var ( rateLimiter <-chan time.Time ) -func (env *Env) parseOutput(opts *ExecOpts, ncalls int) (*ProgInfo, error) { +func (env *Env) parseOutput(opts *ExecOpts, ncalls int) (*flatrpc.ProgInfo, error) { out := env.out ncmd, ok := readUint32(&out) if !ok { return nil, fmt.Errorf("failed to read number of calls") } - info := EmptyProgInfo(ncalls) - extraParts := make([]CallInfo, 0) + info := flatrpc.EmptyProgInfo(ncalls) + extraParts := make([]flatrpc.CallInfo, 0) for i := uint32(0); i < ncmd; i++ { if len(out) < int(unsafe.Sizeof(callReply{})) { return nil, fmt.Errorf("failed to read call %v reply", i) } reply := *(*callReply)(unsafe.Pointer(&out[0])) out = out[unsafe.Sizeof(callReply{}):] - var inf *CallInfo + var inf *flatrpc.CallInfo if reply.magic != outMagic { return nil, fmt.Errorf("bad reply magic 0x%x", reply.magic) } @@ -418,14 +363,14 @@ func (env *Env) parseOutput(opts *ExecOpts, ncalls int) (*ProgInfo, error) { if int(reply.index) >= len(info.Calls) { return nil, fmt.Errorf("bad call %v index %v/%v", i, reply.index, len(info.Calls)) } - inf = &info.Calls[reply.index] + inf = info.Calls[reply.index] if inf.Flags != 0 || inf.Signal != nil { return nil, fmt.Errorf("duplicate reply for call %v/%v/%v", i, reply.index, reply.num) } - inf.Errno = int(reply.errno) - inf.Flags = CallFlags(reply.flags) + inf.Error = int32(reply.errno) + inf.Flags = flatrpc.CallFlag(reply.flags) } else { - extraParts = append(extraParts, CallInfo{}) + extraParts = append(extraParts, flatrpc.CallInfo{}) inf = &extraParts[len(extraParts)-1] } if inf.Signal, ok = readUint32Array(&out, reply.signalSize); !ok { @@ -449,8 +394,8 @@ func (env *Env) parseOutput(opts *ExecOpts, ncalls int) (*ProgInfo, error) { return info, nil } -func convertExtra(extraParts []CallInfo, dedupCover bool) CallInfo { - var extra CallInfo +func convertExtra(extraParts []flatrpc.CallInfo, dedupCover bool) *flatrpc.CallInfo { + var extra flatrpc.CallInfo if dedupCover { extraCover := make(cover.Cover) for _, part := range extraParts { @@ -472,14 +417,11 @@ func convertExtra(extraParts []CallInfo, dedupCover bool) CallInfo { extra.Signal[i] = uint32(s) i++ } - return extra + return &extra } -func readComps(outp *[]byte, compsSize uint32) (prog.CompMap, error) { - if compsSize == 0 { - return nil, nil - } - compMap := make(prog.CompMap) +func readComps(outp *[]byte, compsSize uint32) ([]*flatrpc.Comparison, error) { + comps := make([]*flatrpc.Comparison, 0, 2*compsSize) for i := uint32(0); i < compsSize; i++ { typ, ok := readUint32(outp) if !ok { @@ -505,7 +447,7 @@ func readComps(outp *[]byte, compsSize uint32) (prog.CompMap, error) { if op1 == op2 { continue // it's useless to store such comparisons } - compMap.AddComp(op2, op1) + comps = append(comps, &flatrpc.Comparison{Op1: op2, Op2: op1}) if (typ & compConstMask) != 0 { // If one of the operands was const, then this operand is always // placed first in the instrumented callbacks. Such an operand @@ -513,9 +455,9 @@ func readComps(outp *[]byte, compsSize uint32) (prog.CompMap, error) { // it wouldn't be const), thus we simply ignore it. continue } - compMap.AddComp(op1, op2) + comps = append(comps, &flatrpc.Comparison{Op1: op1, Op2: op2}) } - return compMap, nil + return comps, nil } func readUint32(outp *[]byte) (uint32, bool) { @@ -567,7 +509,7 @@ type command struct { inrp *os.File outwp *os.File outmem []byte - freshness int + freshness uint64 } const ( diff --git a/pkg/ipc/ipc_test.go b/pkg/ipc/ipc_test.go index 30e97660f..af9557e1b 100644 --- a/pkg/ipc/ipc_test.go +++ b/pkg/ipc/ipc_test.go @@ -111,8 +111,8 @@ func TestExecute(t *testing.T) { if len(info.Calls) != len(p.Calls) { t.Fatalf("executed less calls (%v) than prog len(%v):\n%s", len(info.Calls), len(p.Calls), output) } - if info.Calls[0].Errno != 0 { - t.Fatalf("simple call failed: %v\n%s", info.Calls[0].Errno, output) + if info.Calls[0].Error != 0 { + t.Fatalf("simple call failed: %v\n%s", info.Calls[0].Error, output) } if len(output) != 0 { t.Fatalf("output on empty program") @@ -159,8 +159,8 @@ func TestParallel(t *testing.T) { err = fmt.Errorf("no calls executed:\n%s", output) return } - if info.Calls[0].Errno != 0 { - err = fmt.Errorf("simple call failed: %v\n%s", info.Calls[0].Errno, output) + if info.Calls[0].Error != 0 { + err = fmt.Errorf("simple call failed: %v\n%s", info.Calls[0].Error, output) return } if len(output) != 0 { @@ -211,8 +211,8 @@ func TestZlib(t *testing.T) { if err != nil { t.Fatalf("failed to run executor: %v", err) } - if info.Calls[0].Errno != 0 { - t.Fatalf("data comparison failed: %v\n%s", info.Calls[0].Errno, output) + if info.Calls[0].Error != 0 { + t.Fatalf("data comparison failed: %v\n%s", info.Calls[0].Error, output) } } } @@ -258,7 +258,7 @@ func TestExecutorCommonExt(t *testing.T) { if err != nil { t.Fatal(err) } - if call := info.Calls[0]; (call.Flags&CallFinished) == 0 || call.Errno != 0 { - t.Fatalf("bad call result: flags=%x errno=%v", call.Flags, call.Errno) + if call := info.Calls[0]; call.Flags&flatrpc.CallFlagFinished == 0 || call.Error != 0 { + t.Fatalf("bad call result: flags=%x errno=%v", call.Flags, call.Error) } } diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index 89ff419c1..e314424ea 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -2,166 +2,9 @@ // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // Package rpctype contains types of message passed via net/rpc connections -// between various parts of the system. +// between syz-manager and syz-hub. package rpctype -import ( - "math" - "time" - - "github.com/google/syzkaller/pkg/flatrpc" - "github.com/google/syzkaller/pkg/ipc" - "github.com/google/syzkaller/pkg/signal" -) - -// ExecutionRequest describes the task of executing a particular program. -// Corresponds to Fuzzer.Request. -type ExecutionRequest struct { - ID int64 - ProgData []byte - ExecOpts ipc.ExecOpts - NewSignal bool - // If set, ProgData contains compiled executable binary - // that needs to be written to disk and executed. - IsBinary bool - // If set, fully reset executor state befor executing the test. - ResetState bool - // If set, collect program output and return in ExecutionResult.Output. - ReturnOutput bool - // If set, don't fail on program failures, instead return the error in ExecutionResult.Error. - ReturnError bool - SignalFilter signal.Signal - SignalFilterCall int - // Repeat the program that many times (0 means 1). - Repeat int -} - -// ExecutionResult is sent after ExecutionRequest is completed. -type ExecutionResult struct { - ID int64 - ProcID int - Try int - Info ipc.ProgInfo - Output []byte - Error string -} - -// ExchangeInfoRequest is periodically sent by syz-fuzzer to syz-manager. -type ExchangeInfoRequest struct { - Name string - NeedProgs int - StatsDelta map[string]uint64 - Results []ExecutionResult - Latency time.Duration // latency of the previous ExchangeInfo request -} - -// ExchangeInfoReply is a reply to ExchangeInfoRequest. -type ExchangeInfoReply struct { - Requests []ExecutionRequest - NewMaxSignal []uint32 - DropMaxSignal []uint32 -} - -// ExecutingRequest is notification from the fuzzer that it started executing -// the program ProgID. We want this request to be as small and as fast as possible -// b/c we want it to reach manager (or at least leave the VM) before it crashes -// executing this program. -type ExecutingRequest struct { - Name string - ID int64 - ProcID int - Try int -} - -// TODO: merge ExecutionRequest and ExecTask. -type ExecTask struct { - Prog []byte - ID int64 -} - -type ConnectArgs struct { - Name string - GitRevision string - SyzRevision string - ExecutorArch string - ExecutorGitRevision string - ExecutorSyzRevision string -} - -type ConnectRes struct { - MemoryLeakFrames []string - DataRaceFrames []string - // Bitmask of features to try to setup. - Features flatrpc.Feature - // Fuzzer reads these files inside of the VM and returns contents in CheckArgs.Files. - ReadFiles []string - ReadGlobs []string -} - -type CheckArgs struct { - Name string - Error string - Features []*flatrpc.FeatureInfo - Globs map[string][]string - Files []*flatrpc.FileInfo -} - -type CheckRes struct { - CoverFilterBitmap []byte -} - -type SyscallReason struct { - ID int - Reason string -} - -type RunnerConnectArgs struct { - Pool, VM int -} - -type RunnerConnectRes struct { - // CheckUnsupportedCalls is set to true if the Runner needs to query the kernel - // for unsupported system calls and report them back to the server. - CheckUnsupportedCalls bool -} - -// UpdateUnsupportedArgs contains the data passed from client to server in an -// UpdateSupported call, namely the system calls not supported by the client's -// kernel. -type UpdateUnsupportedArgs struct { - // Pool is used to identify the checked kernel. - Pool int - // UnsupportedCalls contains the ID's of system calls not supported by the - // client and the reason for this. - UnsupportedCalls []SyscallReason -} - -// NextExchangeArgs contains the data passed from client to server namely -// identification information of the VM and program execution results. -type NextExchangeArgs struct { - // Pool/VM are used to identify the instance on which the client is running. - Pool, VM int - // ExecTaskID is used to uniquely identify the program for which the client is - // sending results. - ExecTaskID int64 - // Hanged is set to true if the program for which we are sending results - // was killed due to hanging. - Hanged bool - // Info contains information about the execution of each system call in the - // program. - Info ipc.ProgInfo -} - -// NextExchaneRes contains the data passed from server to client namely -// programs to execute on the VM. -type NextExchangeRes struct { - ExecTask -} - -const ( - NoTask int64 = math.MaxInt64 -) - type HubConnectArgs struct { // Client/Key are used for authentication. Client string diff --git a/pkg/runtest/run.go b/pkg/runtest/run.go index 29fa9d0cf..2c6a60d4a 100644 --- a/pkg/runtest/run.go +++ b/pkg/runtest/run.go @@ -38,7 +38,7 @@ type runRequest struct { err error result *queue.Result - results *ipc.ProgInfo // the expected results + results *flatrpc.ProgInfo // the expected results name string broken string @@ -291,7 +291,7 @@ nextSandbox: return nil } -func parseProg(target *prog.Target, dir, filename string) (*prog.Prog, map[string]bool, *ipc.ProgInfo, error) { +func parseProg(target *prog.Target, dir, filename string) (*prog.Prog, map[string]bool, *flatrpc.ProgInfo, error) { data, err := os.ReadFile(filepath.Join(dir, filename)) if err != nil { return nil, nil, nil, fmt.Errorf("failed to read %v: %w", filename, err) @@ -306,7 +306,7 @@ func parseProg(target *prog.Target, dir, filename string) (*prog.Prog, map[strin if err != nil { return nil, nil, nil, fmt.Errorf("failed to deserialize %v: %w", filename, err) } - errnos := map[string]int{ + errnos := map[string]int32{ "": 0, "EPERM": 1, "ENOENT": 2, @@ -332,24 +332,27 @@ func parseProg(target *prog.Target, dir, filename string) (*prog.Prog, map[strin "ZX_ERR_ALREADY_EXISTS": 26, "ZX_ERR_ACCESS_DENIED": 30, } - info := &ipc.ProgInfo{Calls: make([]ipc.CallInfo, len(p.Calls))} - for i, call := range p.Calls { - info.Calls[i].Flags |= ipc.CallExecuted | ipc.CallFinished + info := &flatrpc.ProgInfo{} + for _, call := range p.Calls { + ci := &flatrpc.CallInfo{ + Flags: flatrpc.CallFlagExecuted | flatrpc.CallFlagFinished, + } switch call.Comment { case "blocked": - info.Calls[i].Flags |= ipc.CallBlocked + ci.Flags |= flatrpc.CallFlagBlocked case "unfinished": - info.Calls[i].Flags &^= ipc.CallFinished + ci.Flags &^= flatrpc.CallFlagFinished case "unexecuted": - info.Calls[i].Flags &^= ipc.CallExecuted | ipc.CallFinished + ci.Flags &^= flatrpc.CallFlagExecuted | flatrpc.CallFlagFinished default: res, ok := errnos[call.Comment] if !ok { return nil, nil, nil, fmt.Errorf("%v: unknown call comment %q", filename, call.Comment) } - info.Calls[i].Errno = res + ci.Error = res } + info.Calls = append(info.Calls, ci) } return p, requires, info, nil } @@ -386,7 +389,7 @@ func checkArch(requires map[string]bool, arch string) bool { } func (ctx *Context) produceTest(progs chan *runRequest, req *runRequest, name string, - properties, requires map[string]bool, results *ipc.ProgInfo) { + properties, requires map[string]bool, results *flatrpc.ProgInfo) { req.name = name req.results = results if !match(properties, requires) { @@ -505,7 +508,7 @@ func checkResult(req *runRequest) error { if req.result.Status != queue.Success { return fmt.Errorf("non-successful result status (%v)", req.result.Status) } - var infos []ipc.ProgInfo + var infos []*flatrpc.ProgInfo isC := req.BinaryFile != "" if isC { var err error @@ -516,7 +519,7 @@ func checkResult(req *runRequest) error { raw := req.result.Info for len(raw.Calls) != 0 { ncalls := min(len(raw.Calls), len(req.Prog.Calls)) - infos = append(infos, ipc.ProgInfo{ + infos = append(infos, &flatrpc.ProgInfo{ Extra: raw.Extra, Calls: raw.Calls[:ncalls], }) @@ -538,15 +541,11 @@ func checkResult(req *runRequest) error { return nil } -func checkCallResult(req *runRequest, isC bool, run, call int, info ipc.ProgInfo, calls map[string]bool) error { +func checkCallResult(req *runRequest, isC bool, run, call int, info *flatrpc.ProgInfo, calls map[string]bool) error { inf := info.Calls[call] want := req.results.Calls[call] - for flag, what := range map[ipc.CallFlags]string{ - ipc.CallExecuted: "executed", - ipc.CallBlocked: "blocked", - ipc.CallFinished: "finished", - } { - if flag != ipc.CallFinished { + for flag, what := range flatrpc.EnumNamesCallFlag { + if flag != flatrpc.CallFlagFinished { if isC { // C code does not detect blocked/non-finished calls. continue @@ -557,7 +556,7 @@ func checkCallResult(req *runRequest, isC bool, run, call int, info ipc.ProgInfo continue } } - if runtime.GOOS == targets.FreeBSD && flag == ipc.CallBlocked { + if runtime.GOOS == targets.FreeBSD && flag == flatrpc.CallFlagBlocked { // Blocking detection is flaky on freebsd. // TODO(dvyukov): try to increase the timeout in executor to make it non-flaky. continue @@ -570,11 +569,11 @@ func checkCallResult(req *runRequest, isC bool, run, call int, info ipc.ProgInfo return fmt.Errorf("run %v: call %v is%v %v", run, call, not, what) } } - if inf.Flags&ipc.CallFinished != 0 && inf.Errno != want.Errno { + if inf.Flags&flatrpc.CallFlagFinished != 0 && inf.Error != want.Error { return fmt.Errorf("run %v: wrong call %v result %v, want %v", - run, call, inf.Errno, want.Errno) + run, call, inf.Error, want.Error) } - if isC || inf.Flags&ipc.CallExecuted == 0 { + if isC || inf.Flags&flatrpc.CallFlagExecuted == 0 { return nil } if req.ExecOpts.EnvFlags&flatrpc.ExecEnvSignal != 0 { @@ -600,13 +599,17 @@ func checkCallResult(req *runRequest, isC bool, run, call int, info ipc.ProgInfo return nil } -func parseBinOutput(req *runRequest) ([]ipc.ProgInfo, error) { - var infos []ipc.ProgInfo +func parseBinOutput(req *runRequest) ([]*flatrpc.ProgInfo, error) { + var infos []*flatrpc.ProgInfo s := bufio.NewScanner(bytes.NewReader(req.result.Output)) re := regexp.MustCompile("^### call=([0-9]+) errno=([0-9]+)$") for s.Scan() { if s.Text() == "### start" { - infos = append(infos, ipc.ProgInfo{Calls: make([]ipc.CallInfo, len(req.Prog.Calls))}) + pi := &flatrpc.ProgInfo{} + for range req.Prog.Calls { + pi.Calls = append(pi.Calls, &flatrpc.CallInfo{}) + } + infos = append(infos, pi) } match := re.FindSubmatch(s.Bytes()) if match == nil { @@ -625,15 +628,15 @@ func parseBinOutput(req *runRequest) ([]ipc.ProgInfo, error) { return nil, fmt.Errorf("failed to parse errno %q in %q", string(match[2]), s.Text()) } - info := &infos[len(infos)-1] + info := infos[len(infos)-1] if call >= uint64(len(info.Calls)) { return nil, fmt.Errorf("bad call index %v", call) } if info.Calls[call].Flags != 0 { return nil, fmt.Errorf("double result for call %v", call) } - info.Calls[call].Flags |= ipc.CallExecuted | ipc.CallFinished - info.Calls[call].Errno = int(errno) + info.Calls[call].Flags |= flatrpc.CallFlagExecuted | flatrpc.CallFlagFinished + info.Calls[call].Error = int32(errno) } return infos, nil } diff --git a/pkg/vminfo/features.go b/pkg/vminfo/features.go index 3b7bae8d5..905fc630f 100644 --- a/pkg/vminfo/features.go +++ b/pkg/vminfo/features.go @@ -186,8 +186,8 @@ func (ctx *checkContext) featureSucceeded(feat flatrpc.Feature, testProg *prog.P len(res.Info.Calls), len(testProg.Calls)) } for i, call := range res.Info.Calls { - if call.Errno != 0 { - return fmt.Sprintf("call %v failed with errno %v", i, call.Errno) + if call.Error != 0 { + return fmt.Sprintf("call %v failed with errno %v", i, call.Error) } } call := res.Info.Calls[0] diff --git a/pkg/vminfo/syscalls.go b/pkg/vminfo/syscalls.go index f6ab24a04..23c9504e2 100644 --- a/pkg/vminfo/syscalls.go +++ b/pkg/vminfo/syscalls.go @@ -140,7 +140,7 @@ func (ctx *checkContext) canOpenImpl(file string, modes []uint64, root bool) str } info := ctx.execRaw(calls, prog.StrictUnsafe, root) for _, call := range info.Calls { - if call.Errno == 0 { + if call.Error == 0 { return "" } } @@ -148,7 +148,7 @@ func (ctx *checkContext) canOpenImpl(file string, modes []uint64, root bool) str if root { who = "root " } - return fmt.Sprintf("%vfailed to open %s: %v", who, file, syscall.Errno(info.Calls[0].Errno)) + return fmt.Sprintf("%vfailed to open %s: %v", who, file, syscall.Errno(info.Calls[0].Error)) } func (ctx *checkContext) supportedSyscalls(names []string) string { @@ -161,7 +161,7 @@ func (ctx *checkContext) supportedSyscalls(names []string) string { } info := ctx.execRaw(calls, prog.NonStrictUnsafe, false) for i, res := range info.Calls { - if res.Errno == int(syscall.ENOSYS) { + if res.Error == int32(syscall.ENOSYS) { return fmt.Sprintf("syscall %v is not present", names[i]) } } @@ -201,17 +201,17 @@ func (ctx *checkContext) callSucceeds(call string) string { func (ctx *checkContext) execCall(call string) syscall.Errno { info := ctx.execRaw([]string{call}, prog.StrictUnsafe, false) - return syscall.Errno(info.Calls[0].Errno) + return syscall.Errno(info.Calls[0].Error) } func (ctx *checkContext) anyCallSucceeds(calls []string, msg string) string { info := ctx.execRaw(calls, prog.StrictUnsafe, false) for _, call := range info.Calls { - if call.Errno == 0 { + if call.Error == 0 { return "" } } - return fmt.Sprintf("%s: %v", msg, syscall.Errno(info.Calls[0].Errno)) + return fmt.Sprintf("%s: %v", msg, syscall.Errno(info.Calls[0].Error)) } func (ctx *checkContext) onlySandboxNone() string { @@ -236,12 +236,12 @@ func (ctx *checkContext) val(name string) uint64 { return val } -func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root bool) *ipc.ProgInfo { +func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root bool) *flatrpc.ProgInfo { sandbox := ctx.sandbox if root { sandbox = 0 } - info := &ipc.ProgInfo{} + info := &flatrpc.ProgInfo{} for remain := calls; len(remain) != 0; { // Don't put too many syscalls into a single program, // it will have higher chances to time out. @@ -267,7 +267,7 @@ func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root info.Calls = append(info.Calls, res.Info.Calls...) } else if res.Status == queue.Crashed { // Pretend these calls were not executed. - info.Calls = append(info.Calls, ipc.EmptyProgInfo(ncalls).Calls...) + info.Calls = append(info.Calls, flatrpc.EmptyProgInfo(ncalls).Calls...) } else { // The program must have been either executed or not due to a crash. panic(fmt.Sprintf("got unexpected execution status (%d) for the prog %s", diff --git a/pkg/vminfo/vminfo_test.go b/pkg/vminfo/vminfo_test.go index b73a6ad66..0e5d17ef2 100644 --- a/pkg/vminfo/vminfo_test.go +++ b/pkg/vminfo/vminfo_test.go @@ -103,14 +103,12 @@ func createSuccessfulResults(source queue.Source, stop chan struct{}) { // Currently we have 641 (when we failed to properly dedup syscall tests, it was 4349). panic("too many test programs") } - info := &ipc.ProgInfo{} + info := &flatrpc.ProgInfo{} for range req.Prog.Calls { - info.Calls = append(info.Calls, ipc.CallInfo{ + info.Calls = append(info.Calls, &flatrpc.CallInfo{ Cover: []uint32{1}, Signal: []uint32{1}, - Comps: map[uint64]map[uint64]bool{ - 1: {2: true}, - }, + Comps: []*flatrpc.Comparison{{Op1: 1, Op2: 2}}, }) } req.Done(&queue.Result{ |
