gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/runner/main.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Binary syscall_test_runner runs the syscall test suites in gVisor
    16  // containers and on the host platform.
    17  package main
    18  
    19  import (
    20  	"bufio"
    21  	"bytes"
    22  	"encoding/json"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"os/exec"
    28  	"os/signal"
    29  	"path/filepath"
    30  	"strings"
    31  	"syscall"
    32  	"testing"
    33  	"time"
    34  
    35  	specs "github.com/opencontainers/runtime-spec/specs-go"
    36  	"github.com/syndtr/gocapability/capability"
    37  	"golang.org/x/sys/unix"
    38  	"gvisor.dev/gvisor/pkg/cleanup"
    39  	"gvisor.dev/gvisor/pkg/log"
    40  	"gvisor.dev/gvisor/pkg/sentry/seccheck"
    41  	"gvisor.dev/gvisor/pkg/state/pretty"
    42  	"gvisor.dev/gvisor/pkg/state/statefile"
    43  	"gvisor.dev/gvisor/pkg/test/testutil"
    44  	"gvisor.dev/gvisor/runsc/specutils"
    45  	"gvisor.dev/gvisor/test/runner/gtest"
    46  	"gvisor.dev/gvisor/test/trace/config"
    47  	"gvisor.dev/gvisor/test/uds"
    48  )
    49  
    50  var (
    51  	debug              = flag.Bool("debug", false, "enable debug logs")
    52  	oneSandbox         = flag.Bool("one-sandbox", false, "run all test cases in one sandbox")
    53  	strace             = flag.Bool("strace", false, "enable strace logs")
    54  	platform           = flag.String("platform", "ptrace", "platform to run on")
    55  	platformSupport    = flag.String("platform-support", "", "String passed to the test as GVISOR_PLATFORM_SUPPORT environment variable. Used to determine which syscall tests are expected to work with the current platform.")
    56  	network            = flag.String("network", "none", "network stack to run on (sandbox, host, none)")
    57  	useTmpfs           = flag.Bool("use-tmpfs", false, "mounts tmpfs for /tmp")
    58  	fusefs             = flag.Bool("fusefs", false, "mounts a fusefs for /tmp")
    59  	fileAccess         = flag.String("file-access", "exclusive", "mounts root in exclusive or shared mode")
    60  	overlay            = flag.Bool("overlay", false, "wrap filesystem mounts with writable tmpfs overlay")
    61  	container          = flag.Bool("container", false, "run tests in their own namespaces (user ns, network ns, etc), pretending to be root. Implicitly enabled if network=host, or if using network namespaces")
    62  	setupContainerPath = flag.String("setup-container", "", "path to setup_container binary (for use with --container)")
    63  	trace              = flag.Bool("trace", false, "enables all trace points")
    64  	directfs           = flag.Bool("directfs", false, "enables directfs (for all gofer mounts)")
    65  
    66  	addHostUDS       = flag.Bool("add-host-uds", false, "expose a tree of UDS to test communication with the host")
    67  	addHostConnector = flag.Bool("add-host-connector", false, "create goroutines that connect to bound UDS that will be created by sandbox")
    68  	addHostFIFO      = flag.Bool("add-host-fifo", false, "expose a tree of FIFO to test communication with the host")
    69  	ioUring          = flag.Bool("iouring", false, "Enables IO_URING API for asynchronous I/O")
    70  	leakCheck        = flag.Bool("leak-check", false, "check for reference leaks")
    71  	waitForPid       = flag.Duration("delay-for-debugger", 0, "Print out the sandbox PID and wait for the specified duration to start the test. This is useful for attaching a debugger to the runsc-sandbox process.")
    72  	save             = flag.Bool("save", false, "enables save restore")
    73  	saveResume       = flag.Bool("save-resume", false, "enables save resume")
    74  )
    75  
    76  const (
    77  	// Environment variable used by platform_util.cc to determine platform capabilities.
    78  	platformSupportEnvVar = "GVISOR_PLATFORM_SUPPORT"
    79  
    80  	// checkpointFile is the name of the checkpoint/save state file.
    81  	checkpointFile = "checkpoint.img"
    82  )
    83  
    84  // getSetupContainerPath returns the path to the setup_container binary.
    85  func getSetupContainerPath() string {
    86  	if *setupContainerPath != "" {
    87  		return *setupContainerPath
    88  	}
    89  	setupContainer, err := testutil.FindFile("test/runner/setup_container/setup_container")
    90  	if err != nil {
    91  		fatalf("cannot find setup_container: %v", err)
    92  	}
    93  	return setupContainer
    94  }
    95  
    96  // runTestCaseNative runs the test case directly on the host machine.
    97  func runTestCaseNative(testBin string, tc *gtest.TestCase, args []string, t *testing.T) {
    98  	// These tests might be running in parallel, so make sure they have a
    99  	// unique test temp dir.
   100  	tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
   101  	if err != nil {
   102  		t.Fatalf("could not create temp dir: %v", err)
   103  	}
   104  	defer os.RemoveAll(tmpDir)
   105  
   106  	// Replace TEST_TMPDIR in the current environment with something
   107  	// unique.
   108  	env := os.Environ()
   109  	newEnvVar := "TEST_TMPDIR=" + tmpDir
   110  	var found bool
   111  	for i, kv := range env {
   112  		if strings.HasPrefix(kv, "TEST_TMPDIR=") {
   113  			env[i] = newEnvVar
   114  			found = true
   115  			break
   116  		}
   117  	}
   118  	if !found {
   119  		env = append(env, newEnvVar)
   120  	}
   121  	// Remove shard env variables so that the gunit binary does not try to
   122  	// interpret them.
   123  	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
   124  
   125  	if *addHostUDS {
   126  		socketDir, cleanup, err := uds.CreateBoundUDSTree("/tmp")
   127  		if err != nil {
   128  			t.Fatalf("failed to create socket tree: %v", err)
   129  		}
   130  		defer cleanup()
   131  
   132  		env = append(env, "TEST_UDS_TREE="+socketDir)
   133  		// On Linux, the concept of "attach" location doesn't exist.
   134  		// Just pass the same path to make these tests identical.
   135  		env = append(env, "TEST_UDS_ATTACH_TREE="+socketDir)
   136  	}
   137  
   138  	if *addHostConnector {
   139  		connectorDir, cleanup, err := uds.CreateSocketConnectors("/tmp")
   140  		if err != nil {
   141  			t.Fatalf("failed to create socket connectors: %v", err)
   142  		}
   143  		defer cleanup()
   144  
   145  		env = append(env, "TEST_CONNECTOR_TREE="+connectorDir)
   146  	}
   147  
   148  	if *addHostFIFO {
   149  		pipeDir, cleanup, err := uds.CreateFifoTree("/tmp")
   150  		if err != nil {
   151  			t.Fatalf("failed to create pipe tree: %v", err)
   152  		}
   153  		defer cleanup()
   154  
   155  		env = append(env, "TEST_FIFO_TREE="+pipeDir)
   156  		// On Linux, the concept of "attach" location doesn't exist.
   157  		// Just pass the same path to make these tests identical.
   158  		env = append(env, "TEST_FIFO_ATTACH_TREE="+pipeDir)
   159  	}
   160  
   161  	if *platformSupport != "" {
   162  		env = append(env, fmt.Sprintf("%s=%s", platformSupportEnvVar, *platformSupport))
   163  	}
   164  
   165  	if args == nil {
   166  		args = tc.Args()
   167  	}
   168  
   169  	args = append(args, gtest.TestFlags...)
   170  	cmd := exec.Command(testBin, args...)
   171  	cmd.Env = env
   172  	cmd.Stdout = os.Stdout
   173  	cmd.Stderr = os.Stderr
   174  	cmd.SysProcAttr = &unix.SysProcAttr{}
   175  
   176  	if specutils.HasCapabilities(capability.CAP_SYS_ADMIN) {
   177  		cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUTS
   178  	}
   179  
   180  	if specutils.HasCapabilities(capability.CAP_NET_ADMIN) {
   181  		cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWNET
   182  	}
   183  
   184  	if *container || (cmd.SysProcAttr.Cloneflags&unix.CLONE_NEWNET != 0) {
   185  		// setup_container takes in its target argv as positional arguments.
   186  		cmd.Path = getSetupContainerPath()
   187  		cmd.Args = append([]string{cmd.Path}, cmd.Args...)
   188  		cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWUSER | unix.CLONE_NEWNET | unix.CLONE_NEWIPC | unix.CLONE_NEWUTS
   189  		// Set current user/group as root inside the namespace.
   190  		cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{
   191  			{ContainerID: 0, HostID: os.Getuid(), Size: 1},
   192  		}
   193  		cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{
   194  			{ContainerID: 0, HostID: os.Getgid(), Size: 1},
   195  		}
   196  		cmd.SysProcAttr.GidMappingsEnableSetgroups = false
   197  		cmd.SysProcAttr.Credential = &syscall.Credential{
   198  			Uid: 0,
   199  			Gid: 0,
   200  		}
   201  	}
   202  
   203  	if err := cmd.Run(); err != nil {
   204  		ws := err.(*exec.ExitError).Sys().(syscall.WaitStatus)
   205  		t.Errorf("test %q exited with status %d, want 0", tc.FullName(), ws.ExitStatus())
   206  	}
   207  }
   208  
   209  func deleteIfEmptyFile(dir string) (bool, error) {
   210  	fName := filepath.Join(dir, checkpointFile)
   211  	fi, err := os.Stat(fName)
   212  	if err != nil {
   213  		return false, fmt.Errorf("stat error: %v", err)
   214  	}
   215  	if fi.Size() > 0 {
   216  		return false, nil
   217  	}
   218  	os.RemoveAll(dir)
   219  	return true, nil
   220  }
   221  
   222  func printAll(dirs []string) {
   223  	for _, dir := range dirs {
   224  		f := filepath.Join(dir, checkpointFile)
   225  		printOne(dir, f, false, ".txt")
   226  		printOne(dir, f, true, ".html")
   227  	}
   228  }
   229  
   230  func printOne(dir string, file string, html bool, postfix string) {
   231  	f, err := os.Open(file)
   232  	if err != nil {
   233  		return
   234  	}
   235  	defer f.Close()
   236  	r, m, err := statefile.NewReader(f, nil)
   237  	if err != nil {
   238  		return
   239  	}
   240  	w, err := os.Create(dir + postfix)
   241  	if err != nil {
   242  		return
   243  	}
   244  	defer w.Close()
   245  
   246  	cu := cleanup.Make(func() {
   247  		os.Remove(dir + postfix)
   248  	})
   249  	defer cu.Clean()
   250  	if html {
   251  		// Print just the HTML stream.
   252  		if err := pretty.PrintHTML(w, r); err != nil {
   253  			return
   254  		}
   255  	} else {
   256  		// Print the metadata first.
   257  		if _, err := fmt.Fprintf(w, "%v\n", m); err != nil {
   258  			return
   259  		}
   260  		// Then print the rest of the text.
   261  		if err := pretty.PrintText(w, r); err != nil {
   262  			return
   263  		}
   264  	}
   265  	cu.Release()
   266  }
   267  
   268  func removeAll(dirs []string) {
   269  	for _, dir := range dirs {
   270  		os.RemoveAll(dir)
   271  	}
   272  }
   273  
   274  func prepareSave(args []string, undeclaredOutputsDir string, dirs []string, index int) ([]string, []string, error) {
   275  	// Create the state file directory.
   276  	dir, err := os.MkdirTemp(undeclaredOutputsDir, fmt.Sprintf("state.%v.", index))
   277  	if err != nil {
   278  		return args, dirs, fmt.Errorf("failed to create state file directory: %v", err)
   279  	}
   280  	// Pass the directory path of the state file to the sandbox.
   281  	args = append(args, "-TESTONLY-autosave-image-path", dir)
   282  	dirs = append(dirs, dir)
   283  	return args, dirs, nil
   284  }
   285  
   286  func deleteSandbox(args []string, id string) error {
   287  	deleteArgs := append(args, "delete", "-force=true", id)
   288  	deleteCmd := exec.Command(specutils.ExePath, deleteArgs...)
   289  	if err := deleteCmd.Run(); err != nil {
   290  		return fmt.Errorf("delete error: %v", err)
   291  	}
   292  	return nil
   293  }
   294  
   295  // runRunsc runs spec in runsc in a standard test configuration.
   296  //
   297  // runsc logs will be saved to a path in TEST_UNDECLARED_OUTPUTS_DIR.
   298  //
   299  // Returns an error if the sandboxed application exits non-zero.
   300  func runRunsc(tc *gtest.TestCase, spec *specs.Spec) error {
   301  	bundleDir, cleanup, err := testutil.SetupBundleDir(spec)
   302  	if err != nil {
   303  		return fmt.Errorf("SetupBundleDir failed: %v", err)
   304  	}
   305  	defer cleanup()
   306  
   307  	rootDir, cleanup, err := testutil.SetupRootDir()
   308  	if err != nil {
   309  		return fmt.Errorf("SetupRootDir failed: %v", err)
   310  	}
   311  	defer cleanup()
   312  
   313  	name := tc.FullName()
   314  	id := testutil.RandomContainerID()
   315  	log.Infof("Running test %q in container %q", name, id)
   316  	specutils.LogSpecDebug(spec, false)
   317  
   318  	args := []string{
   319  		"-root", rootDir,
   320  		"-network", *network,
   321  		"-log-format=text",
   322  		"-TESTONLY-unsafe-nonroot=true",
   323  		"-TESTONLY-allow-packet-endpoint-write=true",
   324  		fmt.Sprintf("-panic-signal=%d", unix.SIGTERM),
   325  		fmt.Sprintf("-iouring=%t", *ioUring),
   326  		"-watchdog-action=panic",
   327  		"-platform", *platform,
   328  		"-file-access", *fileAccess,
   329  		"-gvisor-gro",
   330  	}
   331  
   332  	if *network == "host" && !testutil.TestEnvSupportsNetAdmin {
   333  		log.Warningf("Testing with network=host but test environment does not support net admin or raw sockets. Raw sockets will not be enabled.")
   334  	} else {
   335  		args = append(args, "-net-raw")
   336  	}
   337  	if *overlay {
   338  		args = append(args, "-overlay2=all:dir=/tmp")
   339  	} else {
   340  		args = append(args, "-overlay2=none")
   341  	}
   342  	if *debug {
   343  		args = append(args, "-debug", "-log-packets=true")
   344  	}
   345  	if *strace {
   346  		args = append(args, "-strace")
   347  	}
   348  	if *addHostUDS {
   349  		args = append(args, "-host-uds=open")
   350  	}
   351  	if *addHostConnector {
   352  		args = append(args, "-host-uds=create")
   353  	}
   354  	if *addHostFIFO {
   355  		args = append(args, "-host-fifo=open")
   356  	}
   357  	if *leakCheck {
   358  		args = append(args, "-ref-leak-mode=log-names")
   359  	}
   360  	if *trace {
   361  		flag, err := enableAllTraces(rootDir)
   362  		if err != nil {
   363  			return fmt.Errorf("enabling all traces: %w", err)
   364  		}
   365  		log.Infof("Enabling all trace points: %s", flag)
   366  		args = append(args, flag)
   367  	}
   368  	if *directfs {
   369  		args = append(args, "-directfs")
   370  	} else {
   371  		args = append(args, "-directfs=false")
   372  	}
   373  
   374  	testLogDir := ""
   375  	runscLogDir := ""
   376  	undeclaredOutputsDir := ""
   377  	dirs := []string{}
   378  	saveArgs := []string{}
   379  	var ok bool
   380  	undeclaredOutputsDir, ok = unix.Getenv("TEST_UNDECLARED_OUTPUTS_DIR")
   381  	if ok {
   382  		// Create log directory dedicated for this test.
   383  		testLogDir = filepath.Join(undeclaredOutputsDir, strings.Replace(name, "/", "_", -1))
   384  		if err := os.MkdirAll(testLogDir, 0755); err != nil {
   385  			return fmt.Errorf("could not create test dir: %v", err)
   386  		}
   387  		debugLogDir, err := ioutil.TempDir(testLogDir, "runsc")
   388  		if err != nil {
   389  			return fmt.Errorf("could not create temp dir: %v", err)
   390  		}
   391  		debugLogDir += "/"
   392  		runscLogDir = debugLogDir + "/runsc.log"
   393  		log.Infof("runsc logs: %s", debugLogDir)
   394  		args = append(args, "-debug-log", runscLogDir)
   395  		args = append(args, "-coverage-report", debugLogDir)
   396  
   397  		// Default -log sends messages to stderr which makes reading the test log
   398  		// difficult. Instead, drop them when debug log is enabled given it's a
   399  		// better place for these messages.
   400  		args = append(args, "-log=/dev/null")
   401  
   402  		// Create the state file.
   403  		if *save || *saveResume {
   404  			saveArgs = args
   405  			args, dirs, err = prepareSave(args, undeclaredOutputsDir, dirs, 0)
   406  			if err != nil {
   407  				return fmt.Errorf("prepareSave error: %v", err)
   408  			}
   409  			if *saveResume {
   410  				args = append(args, "-TESTONLY-autosave-resume=true")
   411  			}
   412  		}
   413  	} else if *save || *saveResume {
   414  		// TEST_UNDECLARED_OUTPUTS_DIR directory should be present with S/R to create
   415  		// the state file.
   416  		return fmt.Errorf("TEST_UNDECLARED_OUTPUTS_DIR is not set with S/R enabled")
   417  	}
   418  
   419  	// Current process doesn't have CAP_SYS_ADMIN, create user namespace and run
   420  	// as root inside that namespace to get it.
   421  	sysProcAttr := &unix.SysProcAttr{
   422  		Cloneflags: unix.CLONE_NEWUSER | unix.CLONE_NEWNS,
   423  		// Set current user/group as root inside the namespace.
   424  		UidMappings: []syscall.SysProcIDMap{
   425  			{ContainerID: 0, HostID: os.Getuid(), Size: 1},
   426  		},
   427  		GidMappings: []syscall.SysProcIDMap{
   428  			{ContainerID: 0, HostID: os.Getgid(), Size: 1},
   429  		},
   430  		GidMappingsEnableSetgroups: false,
   431  		Credential: &syscall.Credential{
   432  			Uid: 0,
   433  			Gid: 0,
   434  		},
   435  	}
   436  	var cmdArgs []string
   437  	if *waitForPid != 0 {
   438  		createArgs := append(args, "create", "-pid-file", filepath.Join(testLogDir, "pid"), "--bundle", bundleDir, id)
   439  		defer os.Remove(filepath.Join(testLogDir, "pid"))
   440  		createCmd := exec.Command(specutils.ExePath, createArgs...)
   441  		createCmd.SysProcAttr = sysProcAttr
   442  		createCmd.Stdout = os.Stdout
   443  		createCmd.Stderr = os.Stderr
   444  		if err := createCmd.Run(); err != nil {
   445  			return fmt.Errorf("could not create sandbox: %v", err)
   446  		}
   447  
   448  		sandboxPidBytes, err := os.ReadFile(filepath.Join(testLogDir, "pid"))
   449  		if err != nil {
   450  			return fmt.Errorf("could not read pid file: %v", err)
   451  		}
   452  		msg := `
   453  
   454  		Sandbox is running. You can now attach to it from a debugger of your choice.
   455  		For example, with Delve you can call: $ dlv attach %s.
   456  		The test will automatically start after %s.
   457  		You may also signal the test process to start the test immediately: $ kill -SIGUSR1 %d.
   458  
   459  		If you're running a test using Make/docker, you'll have to obtain the runsc and test PIDs manually.
   460  		To attach run: $ dlv attach $(ps aux | grep -m 1 -e 'runsc-sandbox' | awk '{print $2}')
   461  		To signal the test process run: $ kill -SIGUSR1 $(ps aux | grep -m 1 -e 'bash.*test/syscalls' | awk '{print $2}')`
   462  		log.Infof(msg, sandboxPidBytes, *waitForPid, os.Getpid())
   463  
   464  		sigCh := make(chan os.Signal, 1)
   465  		signal.Notify(sigCh, unix.SIGUSR1)
   466  		select {
   467  		case <-sigCh:
   468  		case <-time.After(*waitForPid):
   469  		}
   470  		signal.Reset(unix.SIGUSR1)
   471  
   472  		cmdArgs = append(args, "start", id)
   473  	} else {
   474  		cmdArgs = append(args, "run", "--bundle", bundleDir, id)
   475  	}
   476  	cmd := exec.Command(specutils.ExePath, cmdArgs...)
   477  	cmd.SysProcAttr = sysProcAttr
   478  	if *container || *network == "host" || (cmd.SysProcAttr.Cloneflags&unix.CLONE_NEWNET != 0) {
   479  		cmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWNET
   480  		cmd.Path = getSetupContainerPath()
   481  		cmd.Args = append([]string{cmd.Path}, cmd.Args...)
   482  	}
   483  	cmd.Stdout = os.Stdout
   484  	cmd.Stderr = os.Stderr
   485  	sig := make(chan os.Signal, 1)
   486  	defer close(sig)
   487  	signal.Notify(sig, unix.SIGTERM)
   488  	defer signal.Stop(sig)
   489  	go func() {
   490  		s, ok := <-sig
   491  		if !ok {
   492  			return
   493  		}
   494  		log.Warningf("%s: Got signal: %v", name, s)
   495  		done := make(chan bool, 1)
   496  		dArgs := append([]string{}, args...)
   497  		dArgs = append(dArgs, "debug", "--stacks", id)
   498  		go func(dArgs []string) {
   499  			debug := exec.Command(specutils.ExePath, dArgs...)
   500  			debug.Stdout = os.Stdout
   501  			debug.Stderr = os.Stderr
   502  			debug.Run()
   503  			done <- true
   504  		}(dArgs)
   505  
   506  		timeout := time.After(3 * time.Second)
   507  		select {
   508  		case <-timeout:
   509  			log.Infof("runsc debug --stacks is timeouted")
   510  		case <-done:
   511  		}
   512  
   513  		log.Warningf("Send SIGTERM to the sandbox process")
   514  		dArgs = append(args, "debug",
   515  			fmt.Sprintf("--signal=%d", unix.SIGTERM),
   516  			id)
   517  		signal := exec.Command(specutils.ExePath, dArgs...)
   518  		signal.Stdout = os.Stdout
   519  		signal.Stderr = os.Stderr
   520  		signal.Run()
   521  	}()
   522  
   523  	if *save {
   524  		err = cmd.Run()
   525  		if err != nil {
   526  			return fmt.Errorf("run error: %v", err)
   527  		}
   528  
   529  		// Restore the sandbox with the previous state file.
   530  		for i := 1; ; i++ {
   531  			// Check if the latest state file is valid. If the file
   532  			// is empty, delete it and exit the loop.
   533  			isEmpty, err := deleteIfEmptyFile(dirs[i-1])
   534  			if err != nil {
   535  				return err
   536  			}
   537  			if isEmpty {
   538  				dirs = dirs[:i-1]
   539  				break
   540  			}
   541  
   542  			// Delete the existing sandbox.
   543  			if err := deleteSandbox(saveArgs, id); err != nil {
   544  				printAll(dirs)
   545  				removeAll(dirs)
   546  				return fmt.Errorf("deleteSandbox error %v", err)
   547  			}
   548  
   549  			// Restore into new sandbox.
   550  			restoreArgs := saveArgs
   551  			restoreArgs, dirs, err = prepareSave(restoreArgs, undeclaredOutputsDir, dirs, i)
   552  			if err != nil {
   553  				printAll(dirs)
   554  				removeAll(dirs)
   555  				return fmt.Errorf("prepareSave error: %v", err)
   556  			}
   557  			restoreArgs = append(restoreArgs, "restore", "--image-path", dirs[i-1], "--bundle", bundleDir, id)
   558  			restoreCmd := exec.Command(specutils.ExePath, restoreArgs...)
   559  			restoreCmd.SysProcAttr = sysProcAttr
   560  			if *container || *network == "host" || (restoreCmd.SysProcAttr.Cloneflags&unix.CLONE_NEWNET != 0) {
   561  				restoreCmd.SysProcAttr.Cloneflags |= unix.CLONE_NEWNET
   562  				restoreCmd.Path = getSetupContainerPath()
   563  				restoreCmd.Args = append([]string{restoreCmd.Path}, restoreCmd.Args...)
   564  			}
   565  			restoreCmd.Stdout = os.Stdout
   566  			restoreCmd.Stderr = os.Stderr
   567  			if err := restoreCmd.Run(); err != nil {
   568  				printAll(dirs)
   569  				removeAll(dirs)
   570  				return fmt.Errorf("after restore error: %v", err)
   571  			}
   572  		}
   573  		// Do not output state files when the test succeeds.
   574  		removeAll(dirs)
   575  	} else if *saveResume {
   576  		err = cmd.Run()
   577  		if err != nil {
   578  			printAll(dirs)
   579  			removeAll(dirs)
   580  			return fmt.Errorf("run error: %v", err)
   581  		}
   582  		removeAll(dirs)
   583  	} else {
   584  		err = cmd.Run()
   585  		if *waitForPid != 0 {
   586  			if err != nil {
   587  				return fmt.Errorf("could not start container: %v", err)
   588  			}
   589  			waitArgs := append(args, "wait", id)
   590  			waitCmd := exec.Command(specutils.ExePath, waitArgs...)
   591  			waitCmd.SysProcAttr = sysProcAttr
   592  			waitCmd.Stderr = os.Stderr
   593  
   594  			buf := bytes.NewBuffer(nil)
   595  			waitCmd.Stdout = buf
   596  			err = waitCmd.Run()
   597  			wres := struct {
   598  				ID         string `json:"id"`
   599  				ExitStatus int    `json:"exitStatus"`
   600  			}{}
   601  			if err := json.NewDecoder(buf).Decode(&wres); err != nil {
   602  				return fmt.Errorf("could not decode wait result: %v", err)
   603  			}
   604  			if wres.ExitStatus != 0 {
   605  				return fmt.Errorf("test failed with status: %d", wres.ExitStatus)
   606  			}
   607  		}
   608  	}
   609  	if err == nil && len(testLogDir) > 0 {
   610  		var warningsFound []string
   611  		f, err := os.Open(runscLogDir)
   612  		if err != nil {
   613  			return err
   614  		}
   615  		scanner := bufio.NewScanner(f)
   616  		for scanner.Scan() {
   617  			// This is trivial match for Google's log file format.
   618  			line := scanner.Text()
   619  			if len(line) >= 5 && line[:5] == "panic" {
   620  				warningsFound = append(warningsFound, strings.TrimSpace(line))
   621  			}
   622  			if len(line) >= 2 && (line[0] == 'E' || line[0] == 'W') && (line[1] >= '0' && line[1] <= '9') {
   623  				// Ignore a basic set of warnings that we've
   624  				// determined to be fine. We want these to stay
   625  				// as warnings, even if they are constant.
   626  				switch {
   627  				// Reasonable warnings, allowed during tests.
   628  				case strings.Contains(line, "Will try waiting on the sandbox process instead."):
   629  				case strings.Contains(line, "lisafs: batch closing FDs"):
   630  				case strings.Contains(line, "This is only safe in tests!"):
   631  				case strings.Contains(line, "Capability \"checkpoint_restore\" is not permitted, dropping it."):
   632  				case strings.Contains(line, "syscall filters less restrictive!"):
   633  				case strings.Contains(line, "Getdent64: skipping file"):
   634  				// Capability "perfmon" is not permitted, dropping it.
   635  				case strings.Contains(line, "is not permitted, dropping it."):
   636  				case strings.Contains(line, "sndPrepopulatedMsg failed"):
   637  				case strings.Contains(line, "PR_SET_NO_NEW_PRIVS is assumed to always be set."):
   638  				case strings.Contains(line, "TSC snapshot unavailable"):
   639  				case strings.Contains(line, "copy up failed to copy up contents"):
   640  				case strings.Contains(line, "populate failed for"):
   641  				case strings.Contains(line, "ASAN is enabled: syscall filters less restrictive"):
   642  				case strings.Contains(line, "MSAN is enabled: syscall filters less restrictive"):
   643  				case strings.Contains(line, "TSAN is enabled: syscall filters less restrictive"):
   644  				case strings.Contains(line, "Optional feature EnablePCID not supported"):
   645  				case strings.Contains(line, "Optional feature EnableSMEP not supported"):
   646  				case strings.Contains(line, "Optional feature EnableVPID not supported"):
   647  				case strings.Contains(line, "Optional feature GMPWithVPID not supported"):
   648  				case strings.Contains(line, "Optional feature ValidateGMPPF not supported"):
   649  				case strings.Contains(line, "Pass-through networking enabled"):
   650  				// Expected in some tests that create files as 0755,
   651  				// ex. /gvisor/test/syscalls/linux/exec.cc
   652  				case strings.Contains(line, "Opened a writable executable"):
   653  				// Expected in some tests, eg. /gvisor/test/syscalls/linux/sysret.cc
   654  				case strings.Contains(line, "invalid rip for 64 bit mode"):
   655  				// Expected in some tests that create pipes or sockets.
   656  				case strings.Contains(line, "Rejecting attempt to open fifo/pipe"):
   657  				case strings.Contains(line, "Rejecting attempt to open unix domain socket"):
   658  				case strings.Contains(line, "Rejecting attempt to connect to unix domain socket"):
   659  				case strings.Contains(line, "Rejecting attempt to create unix domain socket"):
   660  
   661  				// Ignore clock frequency adjustment messages.
   662  				case strings.Contains(line, "adjusted frequency from"):
   663  
   664  				// FIXME(b/70990997): URPC error: possible race?
   665  				case strings.Contains(line, "urpc: error decoding: bad file descriptor"):
   666  
   667  				// FIXME(b/147228315): GVISOR_PREEMPTION_INTERRUPT not yet supported on AMD.
   668  				case strings.Contains(line, "Optional feature PreemptionInterrupt not supported"):
   669  
   670  				// Ignore denied dirty timestamp writebacks. It occurs because,
   671  				// in tests, gofer doesn't have permission to change atime.
   672  				case strings.Contains(line, "gofer.dentry.destroyLocked: failed to close file with write dirty timestamps: operation not permitted"):
   673  				case strings.Contains(line, "Tsetattrclunk failed, losing FID"):
   674  				// gsys_get_timekeeping_params hasn't been implemented for ARM.
   675  				case strings.Contains(line, "Error retrieving TSC snapshot, unable to save TSC: function not implemented"):
   676  
   677  				case *save:
   678  					// Ignore these warnings for S/R tests as we try to delete the sandbox
   679  					// after the sandbox has exited and before attempting to restore it.
   680  					if strings.Contains(line, "couldn't find container") ||
   681  						strings.Contains(line, "Container not found, creating new one, cid:") ||
   682  						strings.Contains(line, "Error sending signal") ||
   683  						strings.Contains(line, "Cannot signal container") {
   684  						continue
   685  					}
   686  
   687  				default:
   688  					warningsFound = append(warningsFound, strings.TrimSpace(line))
   689  				}
   690  			}
   691  		}
   692  		if len(warningsFound) > 0 {
   693  			return fmt.Errorf("%s", warningsFound)
   694  		}
   695  		// If the test passed, then we erase the log directory. This speeds up
   696  		// uploading logs in continuous integration & saves on disk space.
   697  		os.RemoveAll(testLogDir)
   698  	}
   699  
   700  	return err
   701  }
   702  
   703  // setupHostUDSTree updates the spec to expose a UDS files tree for testing
   704  // communication with the host.
   705  func setupHostUDSTree(spec *specs.Spec) (cleanup func(), err error) {
   706  	socketDir, cleanup, err := uds.CreateBoundUDSTree("/tmp")
   707  	if err != nil {
   708  		return nil, fmt.Errorf("failed to create socket tree: %v", err)
   709  	}
   710  
   711  	// Standard access to entire tree.
   712  	spec.Mounts = append(spec.Mounts, specs.Mount{
   713  		Destination: "/tmp/sockets",
   714  		Source:      socketDir,
   715  		Type:        "bind",
   716  	})
   717  
   718  	// Individial attach points for each socket to test mounts that attach
   719  	// directly to the sockets.
   720  	for _, protocol := range []string{"stream", "seqpacket"} {
   721  		for _, name := range []string{"echo", "nonlistening"} {
   722  			spec.Mounts = append(spec.Mounts, specs.Mount{
   723  				Destination: filepath.Join("/tmp/sockets-attach", protocol, name),
   724  				Source:      filepath.Join(socketDir, protocol, name),
   725  				Type:        "bind",
   726  			})
   727  		}
   728  	}
   729  	spec.Mounts = append(spec.Mounts, specs.Mount{
   730  		Destination: "/tmp/sockets-attach/dgram/null",
   731  		Source:      filepath.Join(socketDir, "dgram/null"),
   732  		Type:        "bind",
   733  	})
   734  
   735  	spec.Process.Env = append(spec.Process.Env, "TEST_UDS_TREE=/tmp/sockets")
   736  	spec.Process.Env = append(spec.Process.Env, "TEST_UDS_ATTACH_TREE=/tmp/sockets-attach")
   737  
   738  	return cleanup, nil
   739  }
   740  
   741  // setupHostFifoTree starts goroutines that will attempt to connect to sockets
   742  // in a directory that will be bind mounted into the container.
   743  func setupHostConnectorTree(spec *specs.Spec) (cleanup func(), err error) {
   744  	connectorDir, cleanup, err := uds.CreateSocketConnectors("/tmp")
   745  	if err != nil {
   746  		return nil, fmt.Errorf("failed to create connector tree: %v", err)
   747  	}
   748  
   749  	// Standard access to entire tree.
   750  	spec.Mounts = append(spec.Mounts, specs.Mount{
   751  		Destination: "/tmp/connectors",
   752  		Source:      connectorDir,
   753  		Type:        "bind",
   754  	})
   755  	// We can not create individual attach points for sockets that have not been
   756  	// created yet.
   757  	spec.Process.Env = append(spec.Process.Env, "TEST_CONNECTOR_TREE=/tmp/connectors")
   758  	return cleanup, nil
   759  }
   760  
   761  // setupHostFifoTree updates the spec to expose FIFO file tree for testing
   762  // communication with the host.
   763  func setupHostFifoTree(spec *specs.Spec) (cleanup func(), err error) {
   764  	fifoDir, cleanup, err := uds.CreateFifoTree("/tmp")
   765  	if err != nil {
   766  		return nil, fmt.Errorf("failed to create FIFO tree: %v", err)
   767  	}
   768  
   769  	// Standard access to entire tree.
   770  	spec.Mounts = append(spec.Mounts, specs.Mount{
   771  		Destination: "/tmp/pipes",
   772  		Source:      fifoDir,
   773  		Type:        "bind",
   774  	})
   775  
   776  	// Individual attach points for each pipe to test mounts that attach
   777  	// directly to the pipe.
   778  	for _, name := range []string{"in", "out"} {
   779  		spec.Mounts = append(spec.Mounts, specs.Mount{
   780  			Destination: filepath.Join("/tmp/pipes-attach", name),
   781  			Source:      filepath.Join(fifoDir, name),
   782  			Type:        "bind",
   783  		})
   784  	}
   785  
   786  	spec.Process.Env = append(spec.Process.Env, "TEST_FIFO_TREE=/tmp/pipes")
   787  	spec.Process.Env = append(spec.Process.Env, "TEST_FIFO_ATTACH_TREE=/tmp/pipes-attach")
   788  
   789  	return cleanup, nil
   790  }
   791  
   792  // runsTestCaseRunsc runs the test case in runsc.
   793  func runTestCaseRunsc(testBin string, tc *gtest.TestCase, args []string, t *testing.T) {
   794  	// Run a new container with the test executable and filter for the
   795  	// given test suite and name.
   796  	if args == nil {
   797  		args = tc.Args()
   798  	}
   799  	args = append(args, gtest.TestFlags...)
   800  	var spec *specs.Spec
   801  	if *fusefs {
   802  		fuseServer, err := testutil.FindFile("test/runner/fuse/fuse")
   803  		if err != nil {
   804  			fatalf("cannot find fuse: %v", err)
   805  		}
   806  		cmdArgs := append([]string{testBin}, args...)
   807  		cmd := strings.Join(cmdArgs, " ")
   808  		spec = testutil.NewSpecWithArgs([]string{fuseServer, fmt.Sprintf("--debug=%t", *debug), fmt.Sprintf("--cmd=\"%s\"", cmd)}...)
   809  	} else {
   810  		spec = testutil.NewSpecWithArgs(append([]string{testBin}, args...)...)
   811  	}
   812  	// Mark the root as writeable, as some tests attempt to
   813  	// write to the rootfs, and expect EACCES, not EROFS.
   814  	spec.Root.Readonly = false
   815  
   816  	// Test spec comes with pre-defined mounts that we don't want. Reset it.
   817  	spec.Mounts = nil
   818  	testTmpDir := "/tmp"
   819  	if *useTmpfs {
   820  		// Forces '/tmp' to be mounted as tmpfs, otherwise test that rely on
   821  		// features only available in gVisor's internal tmpfs may fail.
   822  		spec.Mounts = append(spec.Mounts, specs.Mount{
   823  			Destination: "/tmp",
   824  			Type:        "tmpfs",
   825  		})
   826  	} else {
   827  		// Use a gofer-backed directory for $TEST_TMPDIR.
   828  		//
   829  		// Tests might be running in parallel, so make sure each has a
   830  		// unique test temp dir.
   831  		//
   832  		// Some tests (e.g., sticky) access this mount from other
   833  		// users, so make sure it is world-accessible.
   834  		tmpDir, err := ioutil.TempDir(testutil.TmpDir(), "")
   835  		if err != nil {
   836  			t.Fatalf("could not create temp dir: %v", err)
   837  		}
   838  		defer os.RemoveAll(tmpDir)
   839  
   840  		if err := os.Chmod(tmpDir, 0777); err != nil {
   841  			t.Fatalf("could not chmod temp dir: %v", err)
   842  		}
   843  
   844  		testTmpDir = tmpDir
   845  		// Note that tmpDir exists in container rootfs mount, whose cacheability is
   846  		// set by fileAccess flag appropriately.
   847  	}
   848  	if *fusefs {
   849  		// In fuse tests, the fuse server forwards all filesystem ops from /tmp
   850  		// to /fuse.
   851  		spec.Mounts = append(spec.Mounts, specs.Mount{
   852  			Destination: "/fuse",
   853  			Type:        "tmpfs",
   854  		})
   855  	}
   856  	if *network == "host" && !testutil.TestEnvSupportsNetAdmin {
   857  		log.Warningf("Testing with network=host but test environment does not support net admin or raw sockets. Dropping CAP_NET_ADMIN and CAP_NET_RAW.")
   858  		specutils.DropCapability(spec.Process.Capabilities, "CAP_NET_ADMIN")
   859  		specutils.DropCapability(spec.Process.Capabilities, "CAP_NET_RAW")
   860  	}
   861  
   862  	// Set environment variables that indicate we are running in gVisor with
   863  	// the given platform, network, and filesystem stack.
   864  	const (
   865  		platformVar = "TEST_ON_GVISOR"
   866  		networkVar  = "GVISOR_NETWORK"
   867  		ioUringVar  = "IOURING_ENABLED"
   868  		fuseVar     = "GVISOR_FUSE_TEST"
   869  		saveVar     = "GVISOR_SAVE_TEST"
   870  	)
   871  	env := append(os.Environ(), platformVar+"="+*platform, networkVar+"="+*network)
   872  	if *platformSupport != "" {
   873  		env = append(env, fmt.Sprintf("%s=%s", platformSupportEnvVar, *platformSupport))
   874  	}
   875  	if *ioUring {
   876  		env = append(env, ioUringVar+"=TRUE")
   877  	} else {
   878  		env = append(env, ioUringVar+"=FALSE")
   879  	}
   880  	if *fusefs {
   881  		env = append(env, fuseVar+"=TRUE")
   882  	} else {
   883  		env = append(env, fuseVar+"=FALSE")
   884  	}
   885  	if *save || *saveResume {
   886  		env = append(env, saveVar+"=TRUE")
   887  	} else {
   888  		env = append(env, saveVar+"=FALSE")
   889  	}
   890  
   891  	// Remove shard env variables so that the gunit binary does not try to
   892  	// interpret them.
   893  	env = filterEnv(env, []string{"TEST_SHARD_INDEX", "TEST_TOTAL_SHARDS", "GTEST_SHARD_INDEX", "GTEST_TOTAL_SHARDS"})
   894  
   895  	// Set TEST_TMPDIR to testTmpDir, which has been appropriately configured.
   896  	env = filterEnv(env, []string{"TEST_TMPDIR"})
   897  	env = append(env, fmt.Sprintf("TEST_TMPDIR=%s", testTmpDir))
   898  
   899  	spec.Process.Env = env
   900  
   901  	if *addHostUDS {
   902  		cleanup, err := setupHostUDSTree(spec)
   903  		if err != nil {
   904  			t.Fatalf("error creating UDS tree: %v", err)
   905  		}
   906  		defer cleanup()
   907  	}
   908  	if *addHostConnector {
   909  		cleanup, err := setupHostConnectorTree(spec)
   910  		if err != nil {
   911  			t.Fatalf("error creating connector tree: %v", err)
   912  		}
   913  		defer cleanup()
   914  	}
   915  	if *addHostFIFO {
   916  		cleanup, err := setupHostFifoTree(spec)
   917  		if err != nil {
   918  			t.Fatalf("error creating FIFO tree: %v", err)
   919  		}
   920  		defer cleanup()
   921  	}
   922  
   923  	// Add cgroup mount to enable cgroups for all tests.
   924  	spec.Mounts = append(spec.Mounts, specs.Mount{
   925  		Destination: "/sys/fs/cgroup",
   926  		Type:        "cgroup",
   927  	})
   928  	if err := runRunsc(tc, spec); err != nil {
   929  		t.Errorf("test %q failed with error %v, want nil", tc.FullName(), err)
   930  	}
   931  }
   932  
   933  // filterEnv returns an environment with the excluded variables removed.
   934  func filterEnv(env, exclude []string) []string {
   935  	var out []string
   936  	for _, kv := range env {
   937  		ok := true
   938  		for _, k := range exclude {
   939  			if strings.HasPrefix(kv, k+"=") {
   940  				ok = false
   941  				break
   942  			}
   943  		}
   944  		if ok {
   945  			out = append(out, kv)
   946  		}
   947  	}
   948  	return out
   949  }
   950  
   951  func fatalf(s string, args ...any) {
   952  	fmt.Fprintf(os.Stderr, s+"\n", args...)
   953  	os.Exit(1)
   954  }
   955  
   956  func matchString(a, b string) (bool, error) {
   957  	return a == b, nil
   958  }
   959  
   960  func main() {
   961  	flag.Parse()
   962  	if flag.NArg() != 1 {
   963  		fatalf("test must be provided")
   964  	}
   965  	testBin := flag.Args()[0] // Only argument.
   966  
   967  	log.SetLevel(log.Info)
   968  	if *debug {
   969  		log.SetLevel(log.Debug)
   970  	}
   971  
   972  	if *platform != "native" {
   973  		if err := testutil.ConfigureExePath(); err != nil {
   974  			panic(err.Error())
   975  		}
   976  	}
   977  
   978  	// Make sure stdout and stderr are opened with O_APPEND, otherwise logs
   979  	// from outside the sandbox can (and will) stomp on logs from inside
   980  	// the sandbox.
   981  	for _, f := range []*os.File{os.Stdout, os.Stderr} {
   982  		flags, err := unix.FcntlInt(f.Fd(), unix.F_GETFL, 0)
   983  		if err != nil {
   984  			fatalf("error getting file flags for %v: %v", f, err)
   985  		}
   986  		if flags&unix.O_APPEND == 0 {
   987  			flags |= unix.O_APPEND
   988  			if _, err := unix.FcntlInt(f.Fd(), unix.F_SETFL, flags); err != nil {
   989  				fatalf("error setting file flags for %v: %v", f, err)
   990  			}
   991  		}
   992  	}
   993  
   994  	// Get all test cases in each binary.
   995  	testCases, err := gtest.ParseTestCases(testBin, true)
   996  	if err != nil {
   997  		fatalf("ParseTestCases(%q) failed: %v", testBin, err)
   998  	}
   999  
  1000  	// Get subset of tests corresponding to shard.
  1001  	indices, err := testutil.TestIndicesForShard(len(testCases))
  1002  	if err != nil {
  1003  		fatalf("TestsForShard() failed: %v", err)
  1004  	}
  1005  
  1006  	// Resolve the absolute path for the binary.
  1007  	testBin, err = filepath.Abs(testBin)
  1008  	if err != nil {
  1009  		fatalf("Abs() failed: %v", err)
  1010  	}
  1011  
  1012  	var tests []testing.InternalTest
  1013  	if *oneSandbox {
  1014  		tc := gtest.TestCase{
  1015  			Suite: "main",
  1016  			Name:  "test",
  1017  		}
  1018  
  1019  		tests = append(tests, testing.InternalTest{
  1020  			Name: fmt.Sprintf("%s_%s", tc.Suite, tc.Name),
  1021  			F: func(t *testing.T) {
  1022  				args := gtest.BuildTestArgs(indices, testCases)
  1023  				if *platform == "native" {
  1024  					// Run the test case on host.
  1025  					runTestCaseNative(testBin, &tc, args, t)
  1026  				} else {
  1027  					// Run the test case in runsc.
  1028  					runTestCaseRunsc(testBin, &tc, args, t)
  1029  				}
  1030  			},
  1031  		})
  1032  	} else {
  1033  		// Run the tests.
  1034  		for _, tci := range indices {
  1035  			// Capture tc.
  1036  			tc := testCases[tci]
  1037  			tests = append(tests, testing.InternalTest{
  1038  				Name: fmt.Sprintf("%s_%s", tc.Suite, tc.Name),
  1039  				F: func(t *testing.T) {
  1040  					if *platform == "native" {
  1041  						// Run the test case on host.
  1042  						runTestCaseNative(testBin, &tc, nil, t)
  1043  					} else {
  1044  						// Run the test case in runsc.
  1045  						runTestCaseRunsc(testBin, &tc, nil, t)
  1046  					}
  1047  				},
  1048  			})
  1049  		}
  1050  	}
  1051  
  1052  	testing.Main(matchString, tests, nil, nil)
  1053  }
  1054  
  1055  func enableAllTraces(dir string) (string, error) {
  1056  	builder := config.Builder{}
  1057  	if err := builder.LoadAllPoints(specutils.ExePath); err != nil {
  1058  		return "", err
  1059  	}
  1060  	builder.AddSink(seccheck.SinkConfig{
  1061  		Name: "null",
  1062  	})
  1063  	path := filepath.Join(dir, "pod_init.json")
  1064  	cfgFile, err := os.Create(path)
  1065  	if err != nil {
  1066  		return "", err
  1067  	}
  1068  	defer cfgFile.Close()
  1069  
  1070  	if err := builder.WriteInitConfig(cfgFile); err != nil {
  1071  		return "", fmt.Errorf("writing config file: %w", err)
  1072  	}
  1073  	return "--pod-init-config=" + path, nil
  1074  }