From a80ca54def962002ad5ebed54459dc1667de4385 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Thu, 18 Apr 2024 12:37:07 +0200 Subject: syz-manager: check revisions on the host Move revisions check to the host. Instead of sending host revisions to the VM, let the VM send own revisions to the host. This is a bit complicated by the fact that we still have the image testing procedure that also uses this code. For now keep it intact. --- pkg/rpctype/rpctype.go | 13 ++++++----- syz-fuzzer/fuzzer.go | 10 ++++++--- syz-fuzzer/testing.go | 53 ++++++++++++++++++++++++++------------------ syz-manager/rpc.go | 25 +++++++++++++++++++-- tools/syz-runtest/runtest.go | 2 -- 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/pkg/rpctype/rpctype.go b/pkg/rpctype/rpctype.go index b0d932714..fa728a494 100644 --- a/pkg/rpctype/rpctype.go +++ b/pkg/rpctype/rpctype.go @@ -67,14 +67,17 @@ type ExecTask struct { } type ConnectArgs struct { - Name string + Name string + GitRevision string + SyzRevision string + ExecutorArch string + ExecutorGitRevision string + ExecutorSyzRevision string } type ConnectRes struct { - EnabledCalls []int - GitRevision string - TargetRevision string - AllSandboxes bool + EnabledCalls []int + AllSandboxes bool // This is forwarded from CheckArgs, if checking was already done. Features *host.Features // Fuzzer reads these files inside of the VM and returns contents in CheckArgs.Files. diff --git a/syz-fuzzer/fuzzer.go b/syz-fuzzer/fuzzer.go index 13926ba0e..6068f6b62 100644 --- a/syz-fuzzer/fuzzer.go +++ b/syz-fuzzer/fuzzer.go @@ -132,7 +132,13 @@ func main() { log.Logf(1, "connecting to manager...") a := &rpctype.ConnectArgs{ - Name: *flagName, + Name: *flagName, + GitRevision: prog.GitRevision, + SyzRevision: target.Revision, + } + a.ExecutorArch, a.ExecutorSyzRevision, a.ExecutorGitRevision, err = executorVersion(executor) + if err != nil { + log.SyzFatalf("failed to run executor version: %v ", err) } r := &rpctype.ConnectRes{} if err := manager.Call("Manager.Connect", a, r); err != nil { @@ -144,8 +150,6 @@ func main() { } var checkReq *rpctype.CheckArgs if r.Features == nil { - checkArgs.gitRevision = r.GitRevision - checkArgs.targetRevision = r.TargetRevision checkArgs.enabledCalls = r.EnabledCalls checkArgs.allSandboxes = r.AllSandboxes checkArgs.featureFlags = featureFlags diff --git a/syz-fuzzer/testing.go b/syz-fuzzer/testing.go index 92c15b5e2..ba019fd56 100644 --- a/syz-fuzzer/testing.go +++ b/syz-fuzzer/testing.go @@ -38,6 +38,9 @@ func testImage(hostAddr string, args *checkArgs) { log.SyzFatalf("BUG: failed to connect to host: %v", err) } conn.Close() + if err := checkRevisions(args); err != nil { + log.SyzFatalf("BUG: %v", err) + } if _, err := checkMachine(args); err != nil { log.SyzFatalf("BUG: %v", err) } @@ -121,9 +124,6 @@ func checkMachine(args *checkArgs) (*rpctype.CheckArgs, error) { done := make(chan bool) defer close(done) go checkMachineHeartbeats(done) - if err := checkRevisions(args); err != nil { - return nil, err - } features, err := host.Check(args.target) if err != nil { return nil, err @@ -198,35 +198,24 @@ func checkCalls(args *checkArgs, res *rpctype.CheckArgs) error { func checkRevisions(args *checkArgs) error { log.Logf(0, "checking revisions...") - executorArgs := strings.Split(args.ipcConfig.Executor, " ") - executorArgs = append(executorArgs, "version") - cmd := osutil.Command(executorArgs[0], executorArgs[1:]...) - cmd.Stderr = io.Discard - if _, err := cmd.StdinPipe(); err != nil { // for the case executor is wrapped with ssh - return err - } - out, err := osutil.Run(time.Minute, cmd) + arch, syzRev, gitRev, err := executorVersion(args.ipcConfig.Executor) if err != nil { - return fmt.Errorf("failed to run executor version: %w", err) - } - vers := strings.Split(strings.TrimSpace(string(out)), " ") - if len(vers) != 4 { - return fmt.Errorf("executor version returned bad result: %q", string(out)) + return err } - if args.target.Arch != vers[1] { - return fmt.Errorf("mismatching target/executor arches: %v vs %v", args.target.Arch, vers[1]) + if args.target.Arch != arch { + return fmt.Errorf("mismatching target/executor arches: %v vs %v", args.target.Arch, arch) } - if prog.GitRevision != vers[3] { + if prog.GitRevision != gitRev { return fmt.Errorf("mismatching fuzzer/executor git revisions: %v vs %v", - prog.GitRevision, vers[3]) + prog.GitRevision, gitRev) } if args.gitRevision != prog.GitRevision { return fmt.Errorf("mismatching manager/fuzzer git revisions: %v vs %v", args.gitRevision, prog.GitRevision) } - if args.target.Revision != vers[2] { + if args.target.Revision != syzRev { return fmt.Errorf("mismatching fuzzer/executor system call descriptions: %v vs %v", - args.target.Revision, vers[2]) + args.target.Revision, syzRev) } if args.target.Revision != args.targetRevision { return fmt.Errorf("mismatching fuzzer/manager system call descriptions: %v vs %v", @@ -235,6 +224,26 @@ func checkRevisions(args *checkArgs) error { return nil } +func executorVersion(bin string) (string, string, string, error) { + args := strings.Split(bin, " ") + args = append(args, "version") + cmd := osutil.Command(args[0], args[1:]...) + cmd.Stderr = io.Discard + if _, err := cmd.StdinPipe(); err != nil { // for the case executor is wrapped with ssh + return "", "", "", err + } + out, err := osutil.Run(time.Minute, cmd) + if err != nil { + return "", "", "", fmt.Errorf("failed to run executor version: %w", err) + } + // Executor returns OS, arch, descriptions hash, git revision. + vers := strings.Split(strings.TrimSpace(string(out)), " ") + if len(vers) != 4 { + return "", "", "", fmt.Errorf("executor version returned bad result: %q", string(out)) + } + return vers[1], vers[2], vers[3], nil +} + func checkSimpleProgram(args *checkArgs, features *host.Features) error { log.Logf(0, "testing simple program...") if err := host.Setup(args.target, features, args.featureFlags, args.ipcConfig.Executor); err != nil { diff --git a/syz-manager/rpc.go b/syz-manager/rpc.go index 8656dcc2b..96283ee20 100644 --- a/syz-manager/rpc.go +++ b/syz-manager/rpc.go @@ -130,13 +130,12 @@ func startRPCServer(mgr *Manager) (*RPCServer, error) { func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error { log.Logf(1, "fuzzer %v connected", a.Name) + checkRevisions(a, serv.cfg.Target) serv.statVMRestarts.Add(1) serv.mu.Lock() defer serv.mu.Unlock() r.EnabledCalls = serv.cfg.Syscalls - r.GitRevision = prog.GitRevision - r.TargetRevision = serv.cfg.Target.Revision r.Features = serv.checkFeatures r.ReadFiles = serv.checker.RequiredFiles() if !serv.checkDone { @@ -145,6 +144,28 @@ func (serv *RPCServer) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) er return nil } +func checkRevisions(a *rpctype.ConnectArgs, target *prog.Target) { + if target.Arch != a.ExecutorArch { + log.Fatalf("mismatching target/executor arches: %v vs %v", target.Arch, a.ExecutorArch) + } + if prog.GitRevision != a.GitRevision { + log.Fatalf("mismatching manager/fuzzer git revisions: %v vs %v", + prog.GitRevision, a.GitRevision) + } + if prog.GitRevision != a.ExecutorGitRevision { + log.Fatalf("mismatching manager/executor git revisions: %v vs %v", + prog.GitRevision, a.ExecutorGitRevision) + } + if target.Revision != a.SyzRevision { + log.Fatalf("mismatching manager/fuzzer system call descriptions: %v vs %v", + target.Revision, a.SyzRevision) + } + if target.Revision != a.ExecutorSyzRevision { + log.Fatalf("mismatching manager/executor system call descriptions: %v vs %v", + target.Revision, a.ExecutorSyzRevision) + } +} + func (serv *RPCServer) Check(a *rpctype.CheckArgs, r *rpctype.CheckRes) error { serv.mu.Lock() defer serv.mu.Unlock() diff --git a/tools/syz-runtest/runtest.go b/tools/syz-runtest/runtest.go index b61dfdad8..3e410630b 100644 --- a/tools/syz-runtest/runtest.go +++ b/tools/syz-runtest/runtest.go @@ -221,8 +221,6 @@ func (mgr *Manager) finishRequest(name string, rep *report.Report) error { } func (mgr *Manager) Connect(a *rpctype.ConnectArgs, r *rpctype.ConnectRes) error { - r.GitRevision = prog.GitRevision - r.TargetRevision = mgr.cfg.Target.Revision r.AllSandboxes = true select { case <-mgr.checkFeaturesReady: -- cgit mrf-deployment