github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/ipc/ipc_test.go (about)

     1  // Copyright 2015 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 ipc_test
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"math/rand"
    10  	"runtime"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/google/syzkaller/pkg/csource"
    15  	"github.com/google/syzkaller/pkg/flatrpc"
    16  	"github.com/google/syzkaller/pkg/image"
    17  	. "github.com/google/syzkaller/pkg/ipc"
    18  	"github.com/google/syzkaller/pkg/ipc/ipcconfig"
    19  	"github.com/google/syzkaller/pkg/osutil"
    20  	"github.com/google/syzkaller/pkg/testutil"
    21  	"github.com/google/syzkaller/prog"
    22  	_ "github.com/google/syzkaller/sys"
    23  	"github.com/google/syzkaller/sys/targets"
    24  )
    25  
    26  func initTest(t *testing.T) (*prog.Target, rand.Source, int, bool, bool, targets.Timeouts) {
    27  	t.Parallel()
    28  	iters := 100
    29  	if testing.Short() {
    30  		iters = 10
    31  	}
    32  	target, err := prog.GetTarget(runtime.GOOS, runtime.GOARCH)
    33  	if err != nil {
    34  		t.Fatal(err)
    35  	}
    36  	cfg, _, err := ipcconfig.Default(target)
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	rs := testutil.RandSource(t)
    41  	return target, rs, iters, cfg.UseShmem, cfg.UseForkServer, cfg.Timeouts
    42  }
    43  
    44  // TestExecutor runs all internal executor unit tests.
    45  // We do it here because we already build executor binary here.
    46  func TestExecutor(t *testing.T) {
    47  	t.Parallel()
    48  	for _, sysTarget := range targets.List[runtime.GOOS] {
    49  		sysTarget := targets.Get(runtime.GOOS, sysTarget.Arch)
    50  		t.Run(sysTarget.Arch, func(t *testing.T) {
    51  			if sysTarget.BrokenCompiler != "" {
    52  				t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler)
    53  			}
    54  			t.Parallel()
    55  			target, err := prog.GetTarget(runtime.GOOS, sysTarget.Arch)
    56  			if err != nil {
    57  				t.Fatal(err)
    58  			}
    59  			bin := csource.BuildExecutor(t, target, "../..")
    60  			// qemu-user may allow us to run some cross-arch binaries.
    61  			if _, err := osutil.RunCmd(time.Minute, "", bin, "test"); err != nil {
    62  				if sysTarget.Arch == runtime.GOARCH || sysTarget.VMArch == runtime.GOARCH {
    63  					t.Fatal(err)
    64  				}
    65  				t.Skipf("skipping, cross-arch binary failed: %v", err)
    66  			}
    67  		})
    68  	}
    69  }
    70  
    71  func prepareTestProgram(target *prog.Target) *prog.Prog {
    72  	p := target.DataMmapProg()
    73  	if len(p.Calls) > 1 {
    74  		p.Calls[1].Props.Async = true
    75  	}
    76  	return p
    77  }
    78  
    79  func TestExecute(t *testing.T) {
    80  	target, _, _, useShmem, useForkServer, timeouts := initTest(t)
    81  
    82  	bin := csource.BuildExecutor(t, target, "../..")
    83  
    84  	flags := []flatrpc.ExecFlag{0, flatrpc.ExecFlagThreaded}
    85  	for _, flag := range flags {
    86  		t.Logf("testing flags 0x%x", flag)
    87  		cfg := &Config{
    88  			Executor:      bin,
    89  			UseShmem:      useShmem,
    90  			UseForkServer: useForkServer,
    91  			Timeouts:      timeouts,
    92  		}
    93  		env, err := MakeEnv(cfg, 0)
    94  		if err != nil {
    95  			t.Fatalf("failed to create env: %v", err)
    96  		}
    97  		defer env.Close()
    98  
    99  		for i := 0; i < 10; i++ {
   100  			p := prepareTestProgram(target)
   101  			opts := &ExecOpts{
   102  				ExecFlags: flag,
   103  			}
   104  			output, info, hanged, err := env.Exec(opts, p)
   105  			if err != nil {
   106  				t.Fatalf("failed to run executor: %v", err)
   107  			}
   108  			if hanged {
   109  				t.Fatalf("program hanged:\n%s", output)
   110  			}
   111  			if len(info.Calls) != len(p.Calls) {
   112  				t.Fatalf("executed less calls (%v) than prog len(%v):\n%s", len(info.Calls), len(p.Calls), output)
   113  			}
   114  			if info.Calls[0].Errno != 0 {
   115  				t.Fatalf("simple call failed: %v\n%s", info.Calls[0].Errno, output)
   116  			}
   117  			if len(output) != 0 {
   118  				t.Fatalf("output on empty program")
   119  			}
   120  		}
   121  	}
   122  }
   123  
   124  func TestParallel(t *testing.T) {
   125  	target, _, _, useShmem, useForkServer, timeouts := initTest(t)
   126  	bin := csource.BuildExecutor(t, target, "../..")
   127  	cfg := &Config{
   128  		Executor:      bin,
   129  		UseShmem:      useShmem,
   130  		UseForkServer: useForkServer,
   131  		Timeouts:      timeouts,
   132  	}
   133  	const P = 10
   134  	errs := make(chan error, P)
   135  	for p := 0; p < P; p++ {
   136  		p := p
   137  		go func() {
   138  			env, err := MakeEnv(cfg, p)
   139  			if err != nil {
   140  				errs <- fmt.Errorf("failed to create env: %w", err)
   141  				return
   142  			}
   143  			defer func() {
   144  				env.Close()
   145  				errs <- err
   146  			}()
   147  			p := target.DataMmapProg()
   148  			opts := &ExecOpts{}
   149  			output, info, hanged, err := env.Exec(opts, p)
   150  			if err != nil {
   151  				err = fmt.Errorf("failed to run executor: %w", err)
   152  				return
   153  			}
   154  			if hanged {
   155  				err = fmt.Errorf("program hanged:\n%s", output)
   156  				return
   157  			}
   158  			if len(info.Calls) == 0 {
   159  				err = fmt.Errorf("no calls executed:\n%s", output)
   160  				return
   161  			}
   162  			if info.Calls[0].Errno != 0 {
   163  				err = fmt.Errorf("simple call failed: %v\n%s", info.Calls[0].Errno, output)
   164  				return
   165  			}
   166  			if len(output) != 0 {
   167  				err = fmt.Errorf("output on empty program")
   168  				return
   169  			}
   170  		}()
   171  	}
   172  	for p := 0; p < P; p++ {
   173  		if err := <-errs; err != nil {
   174  			t.Fatal(err)
   175  		}
   176  	}
   177  }
   178  
   179  func TestZlib(t *testing.T) {
   180  	t.Parallel()
   181  	target, err := prog.GetTarget(targets.TestOS, targets.TestArch64)
   182  	if err != nil {
   183  		t.Fatal(err)
   184  	}
   185  	sysTarget := targets.Get(target.OS, target.Arch)
   186  	if sysTarget.BrokenCompiler != "" {
   187  		t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler)
   188  	}
   189  	cfg, opts, err := ipcconfig.Default(target)
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	opts.EnvFlags |= flatrpc.ExecEnvDebug
   194  	cfg.Executor = csource.BuildExecutor(t, target, "../..")
   195  	env, err := MakeEnv(cfg, 0)
   196  	if err != nil {
   197  		t.Fatalf("failed to create env: %v", err)
   198  	}
   199  	defer env.Close()
   200  	r := rand.New(testutil.RandSource(t))
   201  	for i := 0; i < 10; i++ {
   202  		data := testutil.RandMountImage(r)
   203  		compressed := image.Compress(data)
   204  		text := fmt.Sprintf(`syz_compare_zlib(&(0x7f0000000000)="$%s", AUTO, &(0x7f0000800000)="$%s", AUTO)`,
   205  			image.EncodeB64(data), image.EncodeB64(compressed))
   206  		p, err := target.Deserialize([]byte(text), prog.Strict)
   207  		if err != nil {
   208  			t.Fatalf("failed to deserialize empty program: %v", err)
   209  		}
   210  		output, info, _, err := env.Exec(opts, p)
   211  		if err != nil {
   212  			t.Fatalf("failed to run executor: %v", err)
   213  		}
   214  		if info.Calls[0].Errno != 0 {
   215  			t.Fatalf("data comparison failed: %v\n%s", info.Calls[0].Errno, output)
   216  		}
   217  	}
   218  }
   219  
   220  func TestExecutorCommonExt(t *testing.T) {
   221  	target, err := prog.GetTarget("test", "64_fork")
   222  	if err != nil {
   223  		t.Fatal(err)
   224  	}
   225  	sysTarget := targets.Get(target.OS, target.Arch)
   226  	if sysTarget.BrokenCompiler != "" {
   227  		t.Skipf("skipping, broken cross-compiler: %v", sysTarget.BrokenCompiler)
   228  	}
   229  	bin := csource.BuildExecutor(t, target, "../..", "-DSYZ_TEST_COMMON_EXT_EXAMPLE=1")
   230  	out, err := osutil.RunCmd(time.Minute, "", bin, "setup", "0")
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	if !bytes.Contains(out, []byte("example setup_ext called")) {
   235  		t.Fatalf("setup_ext wasn't called:\n%s", out)
   236  	}
   237  
   238  	// The example setup_ext_test does:
   239  	// *(uint64*)(SYZ_DATA_OFFSET + 0x1234) = 0xbadc0ffee;
   240  	// The following program tests that that value is present at 0x1234.
   241  	test := `syz_compare(&(0x7f0000001234)="", 0x8, &(0x7f0000000000)=@blob="eeffc0ad0b000000", AUTO)`
   242  	p, err := target.Deserialize([]byte(test), prog.Strict)
   243  	if err != nil {
   244  		t.Fatal(err)
   245  	}
   246  	cfg, opts, err := ipcconfig.Default(target)
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  	cfg.Executor = bin
   251  	opts.EnvFlags |= flatrpc.ExecEnvDebug
   252  	env, err := MakeEnv(cfg, 0)
   253  	if err != nil {
   254  		t.Fatalf("failed to create env: %v", err)
   255  	}
   256  	defer env.Close()
   257  	_, info, _, err := env.Exec(opts, p)
   258  	if err != nil {
   259  		t.Fatal(err)
   260  	}
   261  	if call := info.Calls[0]; (call.Flags&CallFinished) == 0 || call.Errno != 0 {
   262  		t.Fatalf("bad call result: flags=%x errno=%v", call.Flags, call.Errno)
   263  	}
   264  }