diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-05-03 10:16:58 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-05-15 12:55:36 +0000 |
| commit | 0b3dad4606c0984ce2d81ba5dd698fa248ce91b8 (patch) | |
| tree | d732c2d7c4096a3a3223529088725c1adb54e3e0 /pkg/vminfo/syscalls.go | |
| parent | 94b087b1f1dce14942bc35bb35a8f58e57b1fc63 (diff) | |
pkg/vminfo: move feature checking to host
Feature checking procedure is split into 2 phases:
1. syz-fuzzer invokes "syz-executor setup feature" for each feature one-by-one,
and checks if executor does not fail.
Executor can also return a special "this feature does not need custom setup",
this allows to not call setup of these features in each new VM.
2. pkg/vminfo runs a simple program with ipc.ExecOpts specific for a concrete feature,
e.g. for wifi injection it will try to run a program with wifi feature enabled,
if setup of the feature fails, executor should also exit with an error.
For coverage features we also additionally check that we actually got coverage.
Then pkg/vminfo combines results of these 2 checks into final result.
syz-execprog now also uses vminfo package and mimics the same checking procedure.
Update #1541
Diffstat (limited to 'pkg/vminfo/syscalls.go')
| -rw-r--r-- | pkg/vminfo/syscalls.go | 73 |
1 files changed, 61 insertions, 12 deletions
diff --git a/pkg/vminfo/syscalls.go b/pkg/vminfo/syscalls.go index 60112ddac..78cc973c8 100644 --- a/pkg/vminfo/syscalls.go +++ b/pkg/vminfo/syscalls.go @@ -62,12 +62,13 @@ type checkContext struct { // Ready channel is closed after we've recevied results of execution of test // programs and file contents. After this results maps and fs are populated. ready chan bool - results map[int64]*ipc.ProgInfo + results map[int64]rpctype.ExecutionResult fs filesystem // Once checking of a syscall is finished, the result is sent to syscalls. // The main goroutine will wait for exactly pendingSyscalls messages. syscalls chan syscallResult pendingSyscalls int + features chan featureResult } type syscallResult struct { @@ -86,8 +87,9 @@ func newCheckContext(cfg *mgrconfig.Config, impl checker) *checkContext { target: cfg.Target, sandbox: sandbox, requests: make(chan []*rpctype.ExecutionRequest), - results: make(map[int64]*ipc.ProgInfo), + results: make(map[int64]rpctype.ExecutionResult), syscalls: make(chan syscallResult), + features: make(chan featureResult, 100), ready: make(chan bool), } } @@ -130,6 +132,7 @@ func (ctx *checkContext) startCheck() []rpctype.ExecutionRequest { ctx.syscalls <- syscallResult{call, reason} }() } + ctx.checkFeatures() var progs []rpctype.ExecutionRequest dedup := make(map[hash.Sig]int64) for i := 0; i < ctx.pendingRequests; i++ { @@ -148,12 +151,11 @@ func (ctx *checkContext) startCheck() []rpctype.ExecutionRequest { return progs } -func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpctype.ExecutionResult) ( - map[*prog.Syscall]bool, map[*prog.Syscall]string, error) { +func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpctype.ExecutionResult, + featureInfos []flatrpc.FeatureInfo) (map[*prog.Syscall]bool, map[*prog.Syscall]string, Features, error) { ctx.fs = createVirtualFilesystem(fileInfos) - for i := range progs { - res := &progs[i] - ctx.results[res.ID] = &res.Info + for _, res := range progs { + ctx.results[res.ID] = res } close(ctx.ready) enabled := make(map[*prog.Syscall]bool) @@ -166,7 +168,8 @@ func (ctx *checkContext) finishCheck(fileInfos []flatrpc.FileInfo, progs []rpcty disabled[res.call] = res.reason } } - return enabled, disabled, nil + features, err := ctx.finishFeatures(featureInfos) + return enabled, disabled, features, err } func (ctx *checkContext) rootCanOpen(file string) string { @@ -220,6 +223,24 @@ func (ctx *checkContext) supportedSyscalls(names []string) string { return "" } +func supportedOpenat(ctx *checkContext, call *prog.Syscall) string { + fname, ok := extractStringConst(call.Args[1].Type) + if !ok || fname[0] != '/' { + return "" + } + modes := ctx.allOpenModes() + // Attempt to extract flags from the syscall description. + if mode, ok := call.Args[2].Type.(*prog.ConstType); ok { + modes = []uint64{mode.Val} + } + var calls []string + for _, mode := range modes { + call := fmt.Sprintf("openat(0x%0x, &AUTO='%v', 0x%x, 0x0)", ctx.val("AT_FDCWD"), fname, mode) + calls = append(calls, call) + } + return ctx.anyCallSucceeds(calls, fmt.Sprintf("failed to open %v", fname)) +} + func (ctx *checkContext) allOpenModes() []uint64 { // Various open modes we need to try if we don't have a concrete mode. // Some files can be opened only for reading, some only for writing, @@ -307,14 +328,14 @@ func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root <-ctx.ready info := &ipc.ProgInfo{} for _, req := range requests { - res := ctx.results[req.ID] - if res == nil { + res, ok := ctx.results[req.ID] + if !ok { panic(fmt.Sprintf("no result for request %v", req.ID)) } - if len(res.Calls) == 0 { + if len(res.Info.Calls) == 0 { panic(fmt.Sprintf("result for request %v has no calls", req.ID)) } - info.Calls = append(info.Calls, res.Calls...) + info.Calls = append(info.Calls, res.Info.Calls...) } if len(info.Calls) != len(calls) { panic(fmt.Sprintf("got only %v results for program %v with %v calls:\n%s", @@ -323,6 +344,34 @@ func (ctx *checkContext) execRaw(calls []string, mode prog.DeserializeMode, root return info } +func (ctx *checkContext) execProg(p *prog.Prog, envFlags ipc.EnvFlags, + execFlags ipc.ExecFlags) rpctype.ExecutionResult { + if ctx.requests == nil { + panic("only one test execution per checker is supported") + } + data, err := p.SerializeForExec() + if err != nil { + panic(fmt.Sprintf("failed to serialize test program: %v\n%s", err, p.Serialize())) + } + req := &rpctype.ExecutionRequest{ + ProgData: data, + ReturnOutput: true, + ReturnError: true, + ExecOpts: ipc.ExecOpts{ + EnvFlags: envFlags, + ExecFlags: execFlags, + SandboxArg: ctx.cfg.SandboxArg, + }, + } + ctx.requests <- []*rpctype.ExecutionRequest{req} + <-ctx.ready + res, ok := ctx.results[req.ID] + if !ok { + panic(fmt.Sprintf("no result for request %v", req.ID)) + } + return res +} + func (ctx *checkContext) readFile(name string) ([]byte, error) { ctx.waitForResults() return ctx.fs.ReadFile(name) |
