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  }