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 /tools | |
| 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 'tools')
| -rw-r--r-- | tools/syz-execprog/execprog.go | 112 | ||||
| -rw-r--r-- | tools/syz-runtest/runtest.go | 76 |
2 files changed, 105 insertions, 83 deletions
diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index 7f328e31a..518e7511f 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -21,13 +21,16 @@ import ( "github.com/google/syzkaller/pkg/cover/backend" "github.com/google/syzkaller/pkg/csource" "github.com/google/syzkaller/pkg/db" + "github.com/google/syzkaller/pkg/flatrpc" "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/ipc" "github.com/google/syzkaller/pkg/ipc/ipcconfig" "github.com/google/syzkaller/pkg/log" "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/rpctype" "github.com/google/syzkaller/pkg/tool" + "github.com/google/syzkaller/pkg/vminfo" "github.com/google/syzkaller/prog" _ "github.com/google/syzkaller/sys" "github.com/google/syzkaller/sys/targets" @@ -91,24 +94,23 @@ func main() { flag.Usage() os.Exit(1) } - features, err := host.Check(target) - if err != nil { - log.Fatalf("%v", err) - } - if *flagOutput { - for _, feat := range features.Supported() { - log.Logf(0, "%-24v: %v", feat.Name, feat.Reason) - } - } if *flagCollide { log.Logf(0, "note: setting -collide to true is deprecated now and has no effect") } - config, execOpts := createConfig(target, features, featuresFlags) - if err = host.Setup(target, features, featuresFlags, config.Executor); err != nil { - log.Fatal(err) + var requestedSyscalls []int + if *flagStress { + syscallList := strings.Split(*flagSyscalls, ",") + if *flagSyscalls == "" { + syscallList = nil + } + requestedSyscalls, err = mgrconfig.ParseEnabledSyscalls(target, syscallList, nil) + if err != nil { + log.Fatalf("failed to parse enabled syscalls: %v", err) + } } + config, execOpts, syscalls, features := createConfig(target, featuresFlags, requestedSyscalls) var gateCallback func() - if features[host.FeatureLeak].Enabled { + if features&flatrpc.FeatureLeak != 0 { gateCallback = func() { output, err := osutil.RunCmd(10*time.Minute, "", config.Executor, "leak") if err != nil { @@ -119,12 +121,7 @@ func main() { } var choiceTable *prog.ChoiceTable if *flagStress { - var syscalls []string - if *flagSyscalls != "" { - syscalls = strings.Split(*flagSyscalls, ",") - } - calls := buildCallList(target, syscalls) - choiceTable = target.BuildChoiceTable(progs, calls) + choiceTable = target.BuildChoiceTable(progs, syscalls) } sysTarget := targets.Get(*flagOS, *flagArch) upperBase := getKernelUpperBase(sysTarget) @@ -398,8 +395,8 @@ func loadPrograms(target *prog.Target, files []string) []*prog.Prog { return progs } -func createConfig(target *prog.Target, features *host.Features, featuresFlags csource.Features) ( - *ipc.Config, *ipc.ExecOpts) { +func createConfig(target *prog.Target, featuresFlags csource.Features, syscalls []int) ( + *ipc.Config, *ipc.ExecOpts, map[*prog.Syscall]bool, flatrpc.Feature) { config, execOpts, err := ipcconfig.Default(target) if err != nil { log.Fatalf("%v", err) @@ -418,29 +415,64 @@ func createConfig(target *prog.Target, features *host.Features, featuresFlags cs } execOpts.ExecFlags |= ipc.FlagCollectComps } - execOpts.EnvFlags |= ipc.FeaturesToFlags(features.ToFlatRPC(), featuresFlags) - return config, execOpts -} - -func buildCallList(target *prog.Target, enabled []string) map[*prog.Syscall]bool { - syscallsIDs, err := mgrconfig.ParseEnabledSyscalls(target, enabled, nil) + cfg := &mgrconfig.Config{ + Sandbox: ipc.FlagsToSandbox(execOpts.EnvFlags), + SandboxArg: execOpts.SandboxArg, + Derived: mgrconfig.Derived{ + TargetOS: target.OS, + TargetArch: target.Arch, + TargetVMArch: target.Arch, + Target: target, + SysTarget: targets.Get(target.OS, target.Arch), + Syscalls: syscalls, + }, + } + checker := vminfo.New(cfg) + files, requests := checker.StartCheck() + fileInfos := host.ReadFiles(files) + featureInfos, err := host.SetupFeatures(target, config.Executor, flatrpc.AllFeatures, featuresFlags) if err != nil { - log.Fatalf("failed to parse enabled syscalls: %v", err) - } - enabledSyscalls := make(map[*prog.Syscall]bool) - for _, id := range syscallsIDs { - enabledSyscalls[target.Syscalls[id]] = true + log.Fatal(err) } - calls, disabled, err := host.DetectSupportedSyscalls(target, "none", enabledSyscalls) + env, err := ipc.MakeEnv(config, 0) if err != nil { - log.Fatalf("failed to detect host supported syscalls: %v", err) + log.Fatalf("failed to create ipc env: %v", err) } - for c, reason := range disabled { - log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason) + defer env.Close() + var results []rpctype.ExecutionResult + for _, req := range requests { + output, info, hanged, err := env.ExecProg(&req.ExecOpts, req.ProgData) + res := rpctype.ExecutionResult{ + ID: req.ID, + Output: output, + } + if info != nil { + res.Info = *info + } + if err != nil { + res.Error = err.Error() + } + if hanged && err == nil { + res.Error = "hanged" + } + results = append(results, res) } - calls, disabled = target.TransitivelyEnabledCalls(calls) - for c, reason := range disabled { - log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason) + enabledSyscalls, disabledSyscalls, features, err := checker.FinishCheck(fileInfos, results, featureInfos) + if err != nil { + log.Fatal(err) + } + if *flagOutput { + for feat, info := range features { + log.Logf(0, "%-24v: %v", flatrpc.EnumNamesFeature[feat], info.Reason) + } + for c, reason := range disabledSyscalls { + log.Logf(0, "unsupported syscall: %v: %v", c.Name, reason) + } + enabledSyscalls, disabledSyscalls = target.TransitivelyEnabledCalls(enabledSyscalls) + for c, reason := range disabledSyscalls { + log.Logf(0, "transitively unsupported: %v: %v", c.Name, reason) + } } - return calls + execOpts.EnvFlags |= ipc.FeaturesToFlags(features.Enabled(), featuresFlags) + return config, execOpts, enabledSyscalls, features.Enabled() } diff --git a/tools/syz-runtest/runtest.go b/tools/syz-runtest/runtest.go index 1203abbb5..6f71519c7 100644 --- a/tools/syz-runtest/runtest.go +++ b/tools/syz-runtest/runtest.go @@ -19,7 +19,6 @@ import ( "time" "github.com/google/syzkaller/pkg/flatrpc" - "github.com/google/syzkaller/pkg/host" "github.com/google/syzkaller/pkg/instance" "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/osutil" @@ -54,18 +53,17 @@ func main() { } osutil.MkdirAll(cfg.Workdir) mgr := &Manager{ - cfg: cfg, - vmPool: vmPool, - checker: vminfo.New(cfg), - reporter: reporter, - debug: *flagDebug, - requests: make(chan *runtest.RunRequest, 4*vmPool.Count()*cfg.Procs), - checkResultC: make(chan *rpctype.CheckArgs, 1), - checkProgsDone: make(chan bool), - checkFeaturesReady: make(chan bool), - vmStop: make(chan bool), - reqMap: make(map[int64]*runtest.RunRequest), - pending: make(map[string]map[int64]bool), + cfg: cfg, + vmPool: vmPool, + checker: vminfo.New(cfg), + reporter: reporter, + debug: *flagDebug, + requests: make(chan *runtest.RunRequest, 4*vmPool.Count()*cfg.Procs), + checkResultC: make(chan *rpctype.CheckArgs, 1), + checkProgsDone: make(chan bool), + vmStop: make(chan bool), + reqMap: make(map[int64]*runtest.RunRequest), + pending: make(map[string]map[int64]bool), } mgr.checkFiles, mgr.checkProgs = mgr.checker.StartCheck() mgr.needCheckResults = len(mgr.checkProgs) @@ -99,11 +97,8 @@ func main() { }() } checkResult := <-mgr.checkResultC - mgr.checkFeatures = checkResult.Features - mgr.checkFilesInfo = checkResult.Files - close(mgr.checkFeaturesReady) <-mgr.checkProgsDone - calls, _, err := mgr.checker.FinishCheck(mgr.checkFilesInfo, mgr.checkResults) + calls, _, features, err := mgr.checker.FinishCheck(checkResult.Files, mgr.checkResults, checkResult.Features) if err != nil { log.Fatalf("failed to detect enabled syscalls: %v", err) } @@ -113,8 +108,8 @@ func main() { // Note: syz_emit_ethernet/syz_extract_tcp_res were manually disabled for "" ("no") sandbox, // b/c tun is not setup without sandbox. enabledCalls[mgr.cfg.Sandbox] = calls - for _, feat := range checkResult.Features.Supported() { - fmt.Printf("%-24v: %v\n", feat.Name, feat.Reason) + for feat, info := range features { + fmt.Printf("%-24v: %v\n", flatrpc.EnumNamesFeature[feat], info.Reason) } for sandbox, calls := range enabledCalls { if sandbox == "" { @@ -125,7 +120,7 @@ func main() { ctx := &runtest.Context{ Dir: filepath.Join(cfg.Syzkaller, "sys", cfg.Target.OS, "test"), Target: cfg.Target, - Features: checkResult.Features.ToFlatRPC(), + Features: features.Enabled(), EnabledCalls: enabledCalls, Requests: mgr.requests, LogFunc: func(text string) { fmt.Println(text) }, @@ -143,23 +138,20 @@ func main() { } type Manager struct { - cfg *mgrconfig.Config - vmPool *vm.Pool - checker *vminfo.Checker - checkFiles []string - checkFilesInfo []flatrpc.FileInfo - checkProgs []rpctype.ExecutionRequest - checkResults []rpctype.ExecutionResult - needCheckResults int - checkProgsDone chan bool - reporter *report.Reporter - requests chan *runtest.RunRequest - checkFeatures *host.Features - checkFeaturesReady chan bool - checkResultC chan *rpctype.CheckArgs - vmStop chan bool - port int - debug bool + cfg *mgrconfig.Config + vmPool *vm.Pool + checker *vminfo.Checker + checkFiles []string + checkProgs []rpctype.ExecutionRequest + checkResults []rpctype.ExecutionResult + needCheckResults int + checkProgsDone chan bool + reporter *report.Reporter + requests chan *runtest.RunRequest + checkResultC chan *rpctype.CheckArgs + vmStop chan bool + port int + debug bool reqMu sync.Mutex reqSeq int64 @@ -240,12 +232,10 @@ func (mgr *Manager) finishRequests(name string, rep *report.Report) error { } func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error { - select { - case <-mgr.checkFeaturesReady: - r.Features = mgr.checkFeatures - default: - r.ReadFiles = append(mgr.checker.RequiredFiles(), mgr.checkFiles...) - r.ReadGlobs = mgr.cfg.Target.RequiredGlobs() + r.ReadFiles = append(mgr.checker.RequiredFiles(), mgr.checkFiles...) + r.ReadGlobs = mgr.cfg.Target.RequiredGlobs() + for feat := range flatrpc.EnumNamesFeature { + r.Features |= feat } return nil } |
