github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/rpcserver/rpcserver_test.go (about) 1 // Copyright 2024 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package rpcserver 5 6 import ( 7 "context" 8 "net" 9 "strings" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 14 "github.com/google/syzkaller/pkg/csource" 15 "github.com/google/syzkaller/pkg/flatrpc" 16 "github.com/google/syzkaller/pkg/fuzzer/queue" 17 "github.com/google/syzkaller/pkg/mgrconfig" 18 "github.com/google/syzkaller/pkg/rpcserver/mocks" 19 "github.com/google/syzkaller/pkg/vminfo" 20 "github.com/google/syzkaller/prog" 21 "github.com/google/syzkaller/sys/targets" 22 "golang.org/x/sync/errgroup" 23 ) 24 25 func getTestDefaultCfg() mgrconfig.Config { 26 return mgrconfig.Config{ 27 Type: targets.Linux, 28 Sandbox: "none", 29 Derived: mgrconfig.Derived{ 30 TargetOS: targets.TestOS, 31 TargetArch: targets.TestArch64, 32 TargetVMArch: targets.TestArch64, 33 Timeouts: targets.Timeouts{Slowdown: 1}, 34 }, 35 } 36 } 37 38 func TestNew(t *testing.T) { 39 defaultCfg := getTestDefaultCfg() 40 41 nilServer := func(s Server) { 42 assert.Nil(t, s) 43 } 44 45 tests := []struct { 46 name string 47 modifyCfg func() *mgrconfig.Config 48 debug bool 49 expectedServCheck func(Server) 50 expectsErr bool 51 expectedErr error 52 }{ 53 { 54 name: "unknown Sandbox", 55 modifyCfg: func() *mgrconfig.Config { 56 cfg := defaultCfg 57 cfg.Sandbox = "unknown" 58 return &cfg 59 }, 60 expectedServCheck: nilServer, 61 expectsErr: true, 62 }, 63 { 64 name: "experimental features", 65 modifyCfg: func() *mgrconfig.Config { 66 cfg := defaultCfg 67 cfg.Experimental = mgrconfig.Experimental{ 68 RemoteCover: false, 69 CoverEdges: true, 70 } 71 return &cfg 72 }, 73 expectedServCheck: func(srv Server) { 74 s := srv.(*server) 75 assert.Equal(t, s.cfg.Config.Features, flatrpc.AllFeatures&(^flatrpc.FeatureExtraCoverage)) 76 assert.Nil(t, s.serv) 77 }, 78 }, 79 } 80 81 for _, tt := range tests { 82 t.Run(tt.name, func(t *testing.T) { 83 cfg := tt.modifyCfg() 84 85 var err error 86 cfg.Target, err = prog.GetTarget(cfg.TargetOS, cfg.TargetArch) 87 assert.NoError(t, err) 88 89 serv, err := New(&RemoteConfig{ 90 Config: cfg, 91 Stats: NewStats(), 92 Debug: tt.debug, 93 }) 94 if tt.expectedErr != nil { 95 assert.Equal(t, tt.expectedErr, err) 96 } else if tt.expectsErr { 97 assert.Error(t, err) 98 } else { 99 assert.Nil(t, err) 100 } 101 tt.expectedServCheck(serv) 102 }) 103 } 104 } 105 106 func TestCheckRevisions(t *testing.T) { 107 tests := []struct { 108 name string 109 req *flatrpc.ConnectRequest 110 target *prog.Target 111 noError bool 112 }{ 113 { 114 name: "error - different Arch", 115 req: &flatrpc.ConnectRequest{ 116 Arch: "arch", 117 }, 118 target: &prog.Target{ 119 Arch: "arch2", 120 }, 121 }, 122 { 123 name: "error - different GitRevision", 124 req: &flatrpc.ConnectRequest{ 125 Arch: "arch", 126 GitRevision: "different", 127 }, 128 target: &prog.Target{ 129 Arch: "arch", 130 }, 131 }, 132 { 133 name: "error - different SyzRevision", 134 req: &flatrpc.ConnectRequest{ 135 Arch: "arch", 136 GitRevision: prog.GitRevision, 137 SyzRevision: "1", 138 }, 139 target: &prog.Target{ 140 Arch: "arch", 141 Revision: "2", 142 }, 143 }, 144 { 145 name: "ok", 146 req: &flatrpc.ConnectRequest{ 147 Arch: "arch", 148 GitRevision: prog.GitRevision, 149 SyzRevision: "1", 150 }, 151 target: &prog.Target{ 152 Arch: "arch", 153 Revision: "1", 154 }, 155 noError: true, 156 }, 157 } 158 159 for _, tt := range tests { 160 t.Run(tt.name, func(t *testing.T) { 161 err := checkRevisions(tt.req, tt.target) 162 if tt.noError { 163 assert.NoError(t, err) 164 } else { 165 assert.Error(t, err) 166 } 167 }) 168 } 169 } 170 171 func TestHandleConn(t *testing.T) { 172 inConn, outConn := net.Pipe() 173 serverConn := flatrpc.NewConn(inConn) 174 clientConn := flatrpc.NewConn(outConn) 175 176 managerMock := mocks.NewManager(t) 177 debug := false 178 defaultCfg := getTestDefaultCfg() 179 180 tests := []struct { 181 name string 182 wantErrMsg string 183 modifyCfg func() *mgrconfig.Config 184 req *flatrpc.ConnectRequest 185 }{ 186 { 187 name: "error, cfg.VMLess = false - unknown VM tries to connect", 188 wantErrMsg: "tries to connect", 189 modifyCfg: func() *mgrconfig.Config { 190 return &defaultCfg 191 }, 192 req: &flatrpc.ConnectRequest{ 193 Id: 2, // Valid Runner id is 1. 194 Arch: "64", 195 GitRevision: prog.GitRevision, 196 SyzRevision: "1", 197 }, 198 }, 199 } 200 201 for _, tt := range tests { 202 t.Run(tt.name, func(t *testing.T) { 203 cfg := tt.modifyCfg() 204 205 var err error 206 cfg.Target, err = prog.GetTarget(cfg.TargetOS, cfg.TargetArch) 207 cfg.Target.Revision = tt.req.SyzRevision 208 assert.NoError(t, err) 209 210 s, err := New(&RemoteConfig{ 211 Config: cfg, 212 Manager: managerMock, 213 Stats: NewStats(), 214 Debug: debug, 215 }) 216 assert.NoError(t, err) 217 serv := s.(*server) 218 219 injectExec := make(chan bool) 220 serv.CreateInstance(1, injectExec, nil) 221 g := errgroup.Group{} 222 g.Go(func() error { 223 hello, err := flatrpc.Recv[*flatrpc.ConnectHelloRaw](clientConn) 224 if err != nil { 225 return err 226 } 227 tt.req.Cookie = authHash(hello.Cookie) 228 flatrpc.Send(clientConn, tt.req) 229 return nil 230 }) 231 if err := serv.handleConn(context.Background(), serverConn); err != nil { 232 if !strings.Contains(err.Error(), tt.wantErrMsg) { 233 t.Fatal(err) 234 } 235 } 236 if err := g.Wait(); err != nil { 237 t.Fatal(err) 238 } 239 }) 240 } 241 } 242 243 func TestMachineCheckCrash(t *testing.T) { 244 target, err := prog.GetTarget(targets.TestOS, targets.TestArch64Fuzz) 245 if err != nil { 246 t.Fatal(err) 247 } 248 sysTarget := targets.Get(target.OS, target.Arch) 249 if sysTarget.BrokenCompiler != "" { 250 t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler) 251 } 252 executor := csource.BuildExecutor(t, target, "../..") 253 254 ctx, cancel := context.WithCancel(context.Background()) 255 defer cancel() 256 257 checkBegan := make(chan struct{}) 258 cfg := &LocalConfig{ 259 Config: Config{ 260 Config: vminfo.Config{ 261 Target: target, 262 Features: flatrpc.FeatureSandboxNone, 263 Sandbox: flatrpc.ExecEnvSandboxNone, 264 }, 265 Procs: 4, 266 Slowdown: 1, 267 machineCheckStarted: checkBegan, 268 }, 269 Executor: executor, 270 Dir: t.TempDir(), 271 } 272 cfg.MachineChecked = func(features flatrpc.Feature, syscalls map[*prog.Syscall]bool) queue.Source { 273 cancel() 274 return queue.Callback(func() *queue.Request { 275 return nil 276 }) 277 } 278 279 local, ctx, err := setupLocal(ctx, cfg) 280 if err != nil { 281 t.Fatal(err) 282 } 283 loopDone := make(chan error) 284 go func() { 285 loopDone <- local.Serve(ctx) 286 }() 287 288 t.Logf("starting the first instance") 289 firstCtx, firstCancel := context.WithCancel(ctx) 290 firstCh := make(chan error) 291 go func() { 292 firstCh <- local.RunInstance(firstCtx, 0) 293 }() 294 295 t.Logf("wait for the machine check to begin") 296 <-checkBegan 297 298 t.Logf("kill the first instance") 299 firstCancel() 300 if err := <-firstCh; err != nil { 301 t.Fatal(err) 302 } 303 304 t.Logf("restart the instance") 305 secondCh := make(chan error) 306 go func() { 307 secondCh <- local.RunInstance(ctx, 0) 308 }() 309 310 t.Logf("await the completion") 311 if err := <-loopDone; err != nil { 312 t.Fatal(err) 313 } 314 if err := <-secondCh; err != nil { 315 t.Fatal(err) 316 } 317 }