diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2024-11-26 12:01:29 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2024-11-26 11:32:06 +0000 |
| commit | 5f6d557d4454398c38d85f6774152d5e0a4436d7 (patch) | |
| tree | 9d5fbc2f1fe2c8ef08c64ff0a4c511ddd41bc3fa | |
| parent | 7de7a5ecf43a5c41b5170d0cb70cb744fdf9de9f (diff) | |
pkg/rpcserver: refactoring in preparation for dynamic interface extraction
Few assorted changes to reduce future diffs:
- add rpcserver.RemoteConfig similar to LocalConfig
(there are too many parameters)
- add CheckGlobs to requesting additional globs from VMs
- pass whole InfoRequest to the MachineChecked callback
so that it's possible to read globs information
- add per-mode config checking in the manager
- add Manager.saveJson helper
| -rw-r--r-- | pkg/rpcserver/local.go | 3 | ||||
| -rw-r--r-- | pkg/rpcserver/mocks/Manager.go | 10 | ||||
| -rw-r--r-- | pkg/rpcserver/rpcserver.go | 40 | ||||
| -rw-r--r-- | pkg/rpcserver/rpcserver_test.go | 13 | ||||
| -rw-r--r-- | syz-manager/manager.go | 37 | ||||
| -rw-r--r-- | tools/syz-diff/diff.go | 10 |
6 files changed, 79 insertions, 34 deletions
diff --git a/pkg/rpcserver/local.go b/pkg/rpcserver/local.go index 5faa8334b..c8052138a 100644 --- a/pkg/rpcserver/local.go +++ b/pkg/rpcserver/local.go @@ -112,7 +112,8 @@ type local struct { setupDone chan bool } -func (ctx *local) MachineChecked(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source { +func (ctx *local) MachineChecked(info *flatrpc.InfoRequest, features flatrpc.Feature, + syscalls map[*prog.Syscall]bool) queue.Source { <-ctx.setupDone ctx.serv.TriagedCorpus() return ctx.cfg.MachineChecked(features, syscalls) diff --git a/pkg/rpcserver/mocks/Manager.go b/pkg/rpcserver/mocks/Manager.go index 810b5028f..0c14c8c9f 100644 --- a/pkg/rpcserver/mocks/Manager.go +++ b/pkg/rpcserver/mocks/Manager.go @@ -72,17 +72,17 @@ func (_m *Manager) CoverageFilter(modules []*vminfo.KernelModule) []uint64 { return r0 } -// MachineChecked provides a mock function with given fields: features, syscalls -func (_m *Manager) MachineChecked(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source { - ret := _m.Called(features, syscalls) +// MachineChecked provides a mock function with given fields: info, features, syscalls +func (_m *Manager) MachineChecked(info *flatrpc.InfoRequestRawT, features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source { + ret := _m.Called(info, features, syscalls) if len(ret) == 0 { panic("no return value specified for MachineChecked") } var r0 queue.Source - if rf, ok := ret.Get(0).(func(flatrpc.Feature, map[*prog.Syscall]bool) queue.Source); ok { - r0 = rf(features, syscalls) + if rf, ok := ret.Get(0).(func(*flatrpc.InfoRequestRawT, flatrpc.Feature, map[*prog.Syscall]bool) queue.Source); ok { + r0 = rf(info, features, syscalls) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(queue.Source) diff --git a/pkg/rpcserver/rpcserver.go b/pkg/rpcserver/rpcserver.go index 69fd9e179..c064e6938 100644 --- a/pkg/rpcserver/rpcserver.go +++ b/pkg/rpcserver/rpcserver.go @@ -48,15 +48,26 @@ type Config struct { DebugTimeouts bool Procs int Slowdown int - pcBase uint64 - localModules []*vminfo.KernelModule + // Extra globs that will be requested during machine checking, + // and will be passed to MachineChecked callback. + CheckGlobs []string + pcBase uint64 + localModules []*vminfo.KernelModule +} + +type RemoteConfig struct { + *mgrconfig.Config + Manager Manager + Stats Stats + CheckGlobs []string + Debug bool } //go:generate ../../tools/mockery.sh --name Manager --output ./mocks type Manager interface { MaxSignal() signal.Signal BugFrames() (leaks []string, races []string) - MachineChecked(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source + MachineChecked(info *flatrpc.InfoRequest, features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source CoverageFilter(modules []*vminfo.KernelModule) []uint64 } @@ -129,11 +140,11 @@ func NewNamedStats(name string) Stats { } } -func New(cfg *mgrconfig.Config, mgr Manager, stats Stats, debug bool) (Server, error) { +func New(cfg *RemoteConfig) (Server, error) { var pcBase uint64 if cfg.KernelObj != "" { var err error - pcBase, err = cover.GetPCBase(cfg) + pcBase, err = cover.GetPCBase(cfg.Config) if err != nil { return nil, err } @@ -152,12 +163,12 @@ func New(cfg *mgrconfig.Config, mgr Manager, stats Stats, debug bool) (Server, e VMType: cfg.Type, Features: features, Syscalls: cfg.Syscalls, - Debug: debug, + Debug: cfg.Debug, Cover: cfg.Cover, Sandbox: sandbox, SandboxArg: cfg.SandboxArg, }, - Stats: stats, + Stats: cfg.Stats, VMArch: cfg.TargetVMArch, RPC: cfg.RPC, VMLess: cfg.VMLess, @@ -170,7 +181,8 @@ func New(cfg *mgrconfig.Config, mgr Manager, stats Stats, debug bool) (Server, e Slowdown: cfg.Timeouts.Slowdown, pcBase: pcBase, localModules: cfg.LocalModules, - }, mgr), nil + CheckGlobs: cfg.CheckGlobs, + }, cfg.Manager), nil } func newImpl(ctx context.Context, cfg *Config, mgr Manager) *server { @@ -268,7 +280,7 @@ func (serv *server) handleRunnerConn(runner *Runner, conn *flatrpc.Conn) error { opts.Features = serv.setupFeatures } else { opts.Files = append(opts.Files, serv.checker.CheckFiles()...) - opts.Globs = serv.target.RequiredGlobs() + opts.Globs = append(serv.target.RequiredGlobs(), serv.cfg.CheckGlobs...) opts.Features = serv.cfg.Features } @@ -321,7 +333,7 @@ func (serv *server) handleMachineInfo(infoReq *flatrpc.InfoRequestRawT) (handsha } // Now execute check programs. go func() { - if err := serv.runCheck(infoReq.Files, infoReq.Features); err != nil { + if err := serv.runCheck(infoReq); err != nil { log.Fatalf("check failed: %v", err) } }() @@ -370,20 +382,20 @@ func checkRevisions(a *flatrpc.ConnectRequest, target *prog.Target) error { return nil } -func (serv *server) runCheck(checkFilesInfo []*flatrpc.FileInfo, checkFeatureInfo []*flatrpc.FeatureInfo) error { - enabledCalls, disabledCalls, features, checkErr := serv.checker.Run(checkFilesInfo, checkFeatureInfo) +func (serv *server) runCheck(info *flatrpc.InfoRequest) error { + enabledCalls, disabledCalls, features, checkErr := serv.checker.Run(info.Files, info.Features) enabledCalls, transitivelyDisabled := serv.target.TransitivelyEnabledCalls(enabledCalls) // Note: need to print disbled syscalls before failing due to an error. // This helps to debug "all system calls are disabled". if serv.cfg.PrintMachineCheck { - serv.printMachineCheck(checkFilesInfo, enabledCalls, disabledCalls, transitivelyDisabled, features) + serv.printMachineCheck(info.Files, enabledCalls, disabledCalls, transitivelyDisabled, features) } if checkErr != nil { return checkErr } enabledFeatures := features.Enabled() serv.setupFeatures = features.NeedSetup() - newSource := serv.mgr.MachineChecked(enabledFeatures, enabledCalls) + newSource := serv.mgr.MachineChecked(info, enabledFeatures, enabledCalls) serv.baseSource.Store(newSource) serv.checkDone.Store(true) return nil diff --git a/pkg/rpcserver/rpcserver_test.go b/pkg/rpcserver/rpcserver_test.go index 760ae5e13..a885ad720 100644 --- a/pkg/rpcserver/rpcserver_test.go +++ b/pkg/rpcserver/rpcserver_test.go @@ -80,7 +80,11 @@ func TestNew(t *testing.T) { cfg.Target, err = prog.GetTarget(cfg.TargetOS, cfg.TargetArch) assert.NoError(t, err) - serv, err := New(cfg, nil, NewStats(), tt.debug) + serv, err := New(&RemoteConfig{ + Config: cfg, + Stats: NewStats(), + Debug: tt.debug, + }) if tt.expectedErr != nil { assert.Equal(t, tt.expectedErr, err) } else if tt.expectsErr { @@ -195,7 +199,12 @@ func TestHandleConn(t *testing.T) { cfg.Target.Revision = tt.req.SyzRevision assert.NoError(t, err) - s, err := New(cfg, managerMock, NewStats(), debug) + s, err := New(&RemoteConfig{ + Config: cfg, + Manager: managerMock, + Stats: NewStats(), + Debug: debug, + }) assert.NoError(t, err) serv := s.(*server) diff --git a/syz-manager/manager.go b/syz-manager/manager.go index 4feb3214b..aed721a79 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -118,6 +118,7 @@ type Mode struct { ExitAfterMachineCheck bool // exit with 0 status when machine check is done // Exit with non-zero status and save the report to workdir/report.json if any kernel crash happens. FailOnCrashes bool + CheckConfig func(cfg *mgrconfig.Config) error } var ( @@ -211,6 +212,11 @@ func main() { flag.PrintDefaults() log.Fatalf("unknown mode: %v", *flagMode) } + if mode.CheckConfig != nil { + if err := mode.CheckConfig(cfg); err != nil { + log.Fatalf("%v mode: %v", mode.Name, err) + } + } if !mode.UseDashboard { cfg.DashboardClient = "" cfg.HubClient = "" @@ -273,7 +279,13 @@ func RunManager(mode *Mode, cfg *mgrconfig.Config) { // Create RPC server for fuzzers. mgr.servStats = rpcserver.NewStats() - mgr.serv, err = rpcserver.New(mgr.cfg, mgr, mgr.servStats, *flagDebug) + rpcCfg := &rpcserver.RemoteConfig{ + Config: mgr.cfg, + Manager: mgr, + Stats: mgr.servStats, + Debug: *flagDebug, + } + mgr.serv, err = rpcserver.New(rpcCfg) if err != nil { log.Fatalf("failed to create rpc server: %v", err) } @@ -659,13 +671,7 @@ func (mgr *Manager) saveCrash(crash *manager.Crash) bool { log.Logf(0, "VM %v: crash: %v%v", crash.InstanceIndex, crash.Title, flags) if mgr.mode.FailOnCrashes { - data, err := json.Marshal(crash.Report) - if err != nil { - log.Fatalf("failed to serialize crash report: %v", err) - } - if err := osutil.WriteFile(filepath.Join(mgr.cfg.Workdir, "report.json"), data); err != nil { - log.Fatal(err) - } + mgr.saveJSON("report.json", crash.Report) log.Fatalf("kernel crashed in smoke testing mode, exiting") } @@ -719,6 +725,16 @@ func (mgr *Manager) saveCrash(crash *manager.Crash) bool { return mgr.NeedRepro(crash) } +func (mgr *Manager) saveJSON(filename string, object any) { + data, err := json.MarshalIndent(object, "", "\t") + if err != nil { + log.Fatalf("failed to serialize json data: %v", err) + } + if err := osutil.WriteFile(filepath.Join(mgr.cfg.Workdir, filename), data); err != nil { + log.Fatal(err) + } +} + func (mgr *Manager) needLocalRepro(crash *manager.Crash) bool { if !mgr.cfg.Reproduce || crash.Corrupted || crash.Suppressed { return false @@ -1039,12 +1055,13 @@ func (mgr *Manager) BugFrames() (leaks, races []string) { return } -func (mgr *Manager) MachineChecked(features flatrpc.Feature, enabledSyscalls map[*prog.Syscall]bool) queue.Source { +func (mgr *Manager) MachineChecked(info *flatrpc.InfoRequest, features flatrpc.Feature, + enabledSyscalls map[*prog.Syscall]bool) queue.Source { if len(enabledSyscalls) == 0 { log.Fatalf("all system calls are disabled") } if mgr.mode.ExitAfterMachineCheck { - mgr.exit("done") + mgr.exit(mgr.mode.Name) } mgr.mu.Lock() diff --git a/tools/syz-diff/diff.go b/tools/syz-diff/diff.go index 6549aed70..a7609fb2a 100644 --- a/tools/syz-diff/diff.go +++ b/tools/syz-diff/diff.go @@ -273,7 +273,12 @@ func setup(ctx context.Context, name string, cfg *mgrconfig.Config) *kernelConte log.Fatalf("failed to create reporter for %q: %v", name, err) } - kernelCtx.serv, err = rpcserver.New(cfg, kernelCtx, kernelCtx.servStats, *flagDebug) + kernelCtx.serv, err = rpcserver.New(&rpcserver.RemoteConfig{ + Config: cfg, + Manager: kernelCtx, + Stats: kernelCtx.servStats, + Debug: *flagDebug, + }) if err != nil { log.Fatalf("failed to create rpc server for %q: %v", name, err) } @@ -305,7 +310,8 @@ func (kc *kernelContext) BugFrames() (leaks, races []string) { return nil, nil } -func (kc *kernelContext) MachineChecked(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source { +func (kc *kernelContext) MachineChecked(_ *flatrpc.InfoRequestRawT, features flatrpc.Feature, + syscalls map[*prog.Syscall]bool) queue.Source { if len(syscalls) == 0 { log.Fatalf("all system calls are disabled") } |
