github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/targets/targets.go (about)

     1  // Copyright 2017 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 targets
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/binary"
     9  	"fmt"
    10  	"os"
    11  	"os/exec"
    12  	"runtime"
    13  	"strings"
    14  	"sync"
    15  	"time"
    16  )
    17  
    18  type Target struct {
    19  	osCommon
    20  	OS               string
    21  	Arch             string
    22  	VMArch           string // e.g. amd64 for 386, or arm64 for arm
    23  	PtrSize          uint64
    24  	PageSize         uint64
    25  	NumPages         uint64
    26  	DataOffset       uint64
    27  	Int64Alignment   uint64
    28  	LittleEndian     bool
    29  	CFlags           []string
    30  	Triple           string
    31  	CCompiler        string
    32  	Objdump          string // name of objdump executable
    33  	KernelCompiler   string // override CC when running kernel make
    34  	KernelLinker     string // override LD when running kernel make
    35  	KernelArch       string
    36  	KernelHeaderArch string
    37  	BrokenCompiler   string
    38  	// NeedSyscallDefine is used by csource package to decide when to emit __NR_* defines.
    39  	NeedSyscallDefine  func(nr uint64) bool
    40  	HostEndian         binary.ByteOrder
    41  	SyscallTrampolines map[string]string
    42  	Addr2Line          func() (string, error)
    43  
    44  	init      *sync.Once
    45  	initOther *sync.Once
    46  	// Target for the other compiler. If SYZ_CLANG says to use gcc, this will be clang. Or the other way around.
    47  	other    *Target
    48  	timeouts Timeouts
    49  }
    50  
    51  func (target *Target) HasCallNumber(callName string) bool {
    52  	return target.SyscallNumbers && !strings.HasPrefix(callName, "syz_")
    53  }
    54  
    55  type osCommon struct {
    56  	// What OS can build native binaries for this OS.
    57  	// If not set, defaults to itself (i.e. native build).
    58  	// Later we can extend this to be a list, but so far we don't have more than one OS.
    59  	BuildOS string
    60  	// Does the OS use syscall numbers (e.g. Linux) or has interface based on functions (e.g. fuchsia).
    61  	SyscallNumbers bool
    62  	// Syscalls accept int64 arguments (>sizeof(void*)).
    63  	Int64SyscallArgs bool
    64  	// E.g. "__NR_" or "SYS_".
    65  	SyscallPrefix string
    66  	// ipc<->executor communication tuning.
    67  	// If ExecutorUsesShmem, programs and coverage are passed through shmem, otherwise via pipes.
    68  	ExecutorUsesShmem bool
    69  	// If ExecutorUsesForkServer, executor uses extended protocol with handshake.
    70  	ExecutorUsesForkServer bool
    71  	// Special mode for OSes that do not have support for building Go binaries.
    72  	// In this mode we run Go binaries on the host machine, only executor runs on target.
    73  	HostFuzzer bool
    74  	// How to run syz-executor directly.
    75  	// Some systems build syz-executor into their images.
    76  	// If this flag is not empty, syz-executor will not be copied to the machine, and will be run using
    77  	// this command instead.
    78  	ExecutorBin string
    79  	// Extension of executable files (notably, .exe for windows).
    80  	ExeExtension string
    81  	// Name of the kernel object file.
    82  	KernelObject string
    83  	// Name of cpp(1) executable.
    84  	CPP string
    85  	// Syscalls on which pseudo syscalls depend. Syzkaller will make sure that __NR* or SYS* definitions
    86  	// for those syscalls are enabled.
    87  	PseudoSyscallDeps map[string][]string
    88  	// Common CFLAGS for this OS.
    89  	cflags []string
    90  }
    91  
    92  // Timeouts structure parametrizes timeouts throughout the system.
    93  // It allows to support different operating system, architectures and execution environments
    94  // (emulation, models, etc) without scattering and duplicating knowledge about their execution
    95  // performance everywhere.
    96  // Timeouts calculation consists of 2 parts: base values and scaling.
    97  // Base timeout values consist of a single syscall timeout, program timeout and "no output" timeout
    98  // and are specified by the target (OS/arch), or defaults are used.
    99  // Scaling part is calculated from the execution environment in pkg/mgrconfig based on VM type,
   100  // kernel build type, emulation, etc. Scaling is specifically converged to a single number so that
   101  // it can be specified/overridden for command line tools (e.g. syz-execprog -slowdown=10).
   102  type Timeouts struct {
   103  	// Base scaling factor, used only for a single syscall timeout.
   104  	Slowdown int
   105  	// Capped scaling factor used for timeouts other than syscall timeout.
   106  	// It's already applied to all values in this struct, but can be used for one-off timeout values
   107  	// in the system. This should also be applied to syscall/program timeout attributes in syscall descriptions.
   108  	// Derived from Slowdown and should not be greater than Slowdown.
   109  	// The idea behind capping is that slowdown can be large (10-20) and most timeouts already
   110  	// include some safety margin. If we just multiply them we will get too large timeouts,
   111  	// e.g. program timeout can become 5s*20 = 100s, or "no output" timeout: 5m*20 = 100m.
   112  	Scale time.Duration
   113  	// Timeout for a single syscall, after this time the syscall is considered "blocked".
   114  	Syscall time.Duration
   115  	// Timeout for a single program execution.
   116  	Program time.Duration
   117  	// Timeout for "no output" detection.
   118  	NoOutput time.Duration
   119  	// Limit on a single VM running time, after this time a VM is restarted.
   120  	VMRunningTime time.Duration
   121  	// How long we should test to get "no output" error (derivative of NoOutput, here to avoid duplication).
   122  	NoOutputRunningTime time.Duration
   123  }
   124  
   125  const (
   126  	FreeBSD = "freebsd"
   127  	Darwin  = "darwin"
   128  	Fuchsia = "fuchsia"
   129  	Linux   = "linux"
   130  	NetBSD  = "netbsd"
   131  	OpenBSD = "openbsd"
   132  	TestOS  = "test"
   133  	Trusty  = "trusty"
   134  	Windows = "windows"
   135  
   136  	AMD64               = "amd64"
   137  	ARM64               = "arm64"
   138  	ARM                 = "arm"
   139  	I386                = "386"
   140  	MIPS64LE            = "mips64le"
   141  	PPC64LE             = "ppc64le"
   142  	S390x               = "s390x"
   143  	RiscV64             = "riscv64"
   144  	TestArch64          = "64"
   145  	TestArch64Fuzz      = "64_fuzz"
   146  	TestArch64Fork      = "64_fork"
   147  	TestArch32Shmem     = "32_shmem"
   148  	TestArch32ForkShmem = "32_fork_shmem"
   149  )
   150  
   151  func Get(OS, arch string) *Target {
   152  	return GetEx(OS, arch, useClang)
   153  }
   154  
   155  func GetEx(OS, arch string, clang bool) *Target {
   156  	target := List[OS][arch]
   157  	if target == nil {
   158  		return nil
   159  	}
   160  	target.init.Do(target.lazyInit)
   161  	if clang == useClang {
   162  		return target
   163  	}
   164  	target.initOther.Do(func() {
   165  		other := new(Target)
   166  		*other = *target
   167  		other.setCompiler(clang)
   168  		other.lazyInit()
   169  		target.other = other
   170  	})
   171  	return target.other
   172  }
   173  
   174  // nolint: lll
   175  var List = map[string]map[string]*Target{
   176  	TestOS: {
   177  		TestArch64: {
   178  			PtrSize:  8,
   179  			PageSize: 4 << 10,
   180  			CFlags: []string{
   181  				"-fsanitize=address",
   182  				// Compile with -no-pie due to issues with ASan + ASLR on ppc64le.
   183  				"-no-pie",
   184  				// Otherwise it conflicts with -fsanitize-coverage=trace-pc.
   185  				"-fno-exceptions",
   186  			},
   187  			osCommon: osCommon{
   188  				SyscallNumbers:         true,
   189  				SyscallPrefix:          "SYS_",
   190  				ExecutorUsesShmem:      false,
   191  				ExecutorUsesForkServer: false,
   192  			},
   193  		},
   194  		TestArch64Fuzz: {
   195  			PtrSize:  8,
   196  			PageSize: 8 << 10,
   197  			// -fsanitize=address causes SIGSEGV.
   198  			CFlags: []string{"-no-pie"},
   199  			osCommon: osCommon{
   200  				SyscallNumbers:         true,
   201  				SyscallPrefix:          "SYS_",
   202  				ExecutorUsesShmem:      true,
   203  				ExecutorUsesForkServer: true,
   204  			},
   205  		},
   206  		TestArch64Fork: {
   207  			PtrSize:  8,
   208  			PageSize: 8 << 10,
   209  			CFlags: []string{
   210  				"-fsanitize=address",
   211  				// Compile with -no-pie due to issues with ASan + ASLR on ppc64le.
   212  				"-no-pie",
   213  				// Otherwise it conflicts with -fsanitize-coverage=trace-pc.
   214  				"-fno-exceptions",
   215  			},
   216  			osCommon: osCommon{
   217  				SyscallNumbers:         true,
   218  				SyscallPrefix:          "SYS_",
   219  				ExecutorUsesShmem:      false,
   220  				ExecutorUsesForkServer: true,
   221  			},
   222  		},
   223  		TestArch32Shmem: {
   224  			PtrSize:        4,
   225  			PageSize:       8 << 10,
   226  			Int64Alignment: 4,
   227  			CFlags:         []string{"-static"},
   228  			osCommon: osCommon{
   229  				SyscallNumbers:         true,
   230  				Int64SyscallArgs:       true,
   231  				SyscallPrefix:          "SYS_",
   232  				ExecutorUsesShmem:      true,
   233  				ExecutorUsesForkServer: false,
   234  			},
   235  		},
   236  		TestArch32ForkShmem: {
   237  			PtrSize:  4,
   238  			PageSize: 4 << 10,
   239  			CFlags:   []string{"-static-pie"},
   240  			osCommon: osCommon{
   241  				SyscallNumbers:         true,
   242  				Int64SyscallArgs:       true,
   243  				SyscallPrefix:          "SYS_",
   244  				ExecutorUsesShmem:      true,
   245  				ExecutorUsesForkServer: true,
   246  				HostFuzzer:             true,
   247  			},
   248  		},
   249  	},
   250  	Linux: {
   251  		AMD64: {
   252  			PtrSize:          8,
   253  			PageSize:         4 << 10,
   254  			LittleEndian:     true,
   255  			CFlags:           []string{"-m64"},
   256  			Triple:           "x86_64-linux-gnu",
   257  			KernelArch:       "x86_64",
   258  			KernelHeaderArch: "x86",
   259  			NeedSyscallDefine: func(nr uint64) bool {
   260  				// Only generate defines for new syscalls
   261  				// (added after commit 8a1ab3155c2ac on 2012-10-04).
   262  				return nr >= 313
   263  			},
   264  		},
   265  		I386: {
   266  			VMArch:           AMD64,
   267  			PtrSize:          4,
   268  			PageSize:         4 << 10,
   269  			Int64Alignment:   4,
   270  			LittleEndian:     true,
   271  			CFlags:           []string{"-m32"},
   272  			Triple:           "x86_64-linux-gnu",
   273  			KernelArch:       "i386",
   274  			KernelHeaderArch: "x86",
   275  		},
   276  		ARM64: {
   277  			PtrSize:          8,
   278  			PageSize:         4 << 10,
   279  			LittleEndian:     true,
   280  			Triple:           "aarch64-linux-gnu",
   281  			KernelArch:       "arm64",
   282  			KernelHeaderArch: "arm64",
   283  		},
   284  		ARM: {
   285  			VMArch:           ARM64,
   286  			PtrSize:          4,
   287  			PageSize:         4 << 10,
   288  			LittleEndian:     true,
   289  			CFlags:           []string{"-D__LINUX_ARM_ARCH__=6", "-march=armv6"},
   290  			Triple:           "arm-linux-gnueabi",
   291  			KernelArch:       "arm",
   292  			KernelHeaderArch: "arm",
   293  		},
   294  		MIPS64LE: {
   295  			PtrSize:          8,
   296  			PageSize:         4 << 10,
   297  			LittleEndian:     true,
   298  			CFlags:           []string{"-march=mips64r2", "-mabi=64", "-EL"},
   299  			Triple:           "mips64el-linux-gnuabi64",
   300  			KernelArch:       "mips",
   301  			KernelHeaderArch: "mips",
   302  		},
   303  		PPC64LE: {
   304  			PtrSize:          8,
   305  			PageSize:         64 << 10,
   306  			LittleEndian:     true,
   307  			CFlags:           []string{"-D__powerpc64__"},
   308  			Triple:           "powerpc64le-linux-gnu",
   309  			KernelArch:       "powerpc",
   310  			KernelHeaderArch: "powerpc",
   311  		},
   312  		S390x: {
   313  			PtrSize:          8,
   314  			PageSize:         4 << 10,
   315  			DataOffset:       0xfffff000,
   316  			CFlags:           []string{"-fPIE"},
   317  			LittleEndian:     false,
   318  			Triple:           "s390x-linux-gnu",
   319  			KernelArch:       "s390",
   320  			KernelHeaderArch: "s390",
   321  			SyscallTrampolines: map[string]string{
   322  				// The s390x Linux syscall ABI allows for upto 5 input parameters passed in registers, and this is not enough
   323  				// for the mmap syscall. Therefore, all input parameters for the mmap syscall are packed into a struct
   324  				// on user stack and the pointer to the struct is passed as an input parameter to the syscall.
   325  				// To work around this problem we therefore reroute the mmap syscall to the glibc mmap wrapper.
   326  				"mmap": "mmap",
   327  			},
   328  		},
   329  		RiscV64: {
   330  			PtrSize:          8,
   331  			PageSize:         4 << 10,
   332  			LittleEndian:     true,
   333  			Triple:           "riscv64-linux-gnu",
   334  			KernelArch:       "riscv",
   335  			KernelHeaderArch: "riscv",
   336  		},
   337  	},
   338  	FreeBSD: {
   339  		AMD64: {
   340  			PtrSize:      8,
   341  			PageSize:     4 << 10,
   342  			LittleEndian: true,
   343  			CCompiler:    "clang",
   344  			CFlags:       []string{"-m64", "--target=x86_64-unknown-freebsd14.0"},
   345  			NeedSyscallDefine: func(nr uint64) bool {
   346  				// freebsd_12_shm_open, shm_open2, shm_rename, __realpathat, close_range, copy_file_range
   347  				return nr == 482 || nr >= 569
   348  			},
   349  		},
   350  		ARM64: {
   351  			PtrSize:      8,
   352  			PageSize:     4 << 10,
   353  			LittleEndian: true,
   354  			CCompiler:    "clang",
   355  			CFlags:       []string{"-m64", "--target=aarch64-unknown-freebsd14.0"},
   356  			NeedSyscallDefine: func(nr uint64) bool {
   357  				// freebsd_12_shm_open, shm_open2, shm_rename, __realpathat, close_range, copy_file_range
   358  				return nr == 482 || nr >= 569
   359  			},
   360  		},
   361  		I386: {
   362  			VMArch:   AMD64,
   363  			PtrSize:  4,
   364  			PageSize: 4 << 10,
   365  			// The default DataOffset doesn't work with 32-bit
   366  			// FreeBSD and using ld.lld due to collisions.
   367  			DataOffset:     256 << 20,
   368  			Int64Alignment: 4,
   369  			LittleEndian:   true,
   370  			CCompiler:      "clang",
   371  			CFlags:         []string{"-m32", "--target=i386-unknown-freebsd14.0"},
   372  			NeedSyscallDefine: func(nr uint64) bool {
   373  				// freebsd_12_shm_open, shm_open2, shm_rename, __realpathat, close_range, copy_file_range
   374  				return nr == 482 || nr >= 569
   375  			},
   376  		},
   377  		RiscV64: {
   378  			PtrSize:      8,
   379  			PageSize:     4 << 10,
   380  			LittleEndian: true,
   381  			CCompiler:    "clang",
   382  			CFlags:       []string{"-m64", "--target=riscv64-unknown-freebsd14.0"},
   383  			NeedSyscallDefine: func(nr uint64) bool {
   384  				// freebsd_12_shm_open, shm_open2, shm_rename, __realpathat, close_range, copy_file_range
   385  				return nr == 482 || nr >= 569
   386  			},
   387  		},
   388  	},
   389  	Darwin: {
   390  		AMD64: {
   391  			PtrSize:      8,
   392  			PageSize:     4 << 10,
   393  			DataOffset:   512 << 24,
   394  			LittleEndian: true,
   395  			CCompiler:    "clang",
   396  			CFlags: []string{
   397  				"-m64",
   398  				"-I", sourceDirVar + "/san",
   399  				// FIXME(HerrSpace): syscall was marked as deprecated on macos
   400  				"-Wno-deprecated-declarations",
   401  			},
   402  			NeedSyscallDefine: dontNeedSyscallDefine,
   403  		},
   404  	},
   405  	NetBSD: {
   406  		AMD64: {
   407  			PtrSize:      8,
   408  			PageSize:     4 << 10,
   409  			LittleEndian: true,
   410  			CFlags: []string{
   411  				"-m64",
   412  				"-static-pie",
   413  				"--sysroot", sourceDirVar + "/dest/",
   414  			},
   415  			CCompiler: sourceDirVar + "/tools/bin/x86_64--netbsd-g++",
   416  		},
   417  	},
   418  	OpenBSD: {
   419  		AMD64: {
   420  			PtrSize:      8,
   421  			PageSize:     4 << 10,
   422  			LittleEndian: true,
   423  			CCompiler:    "c++",
   424  			// PIE is enabled on OpenBSD by default, so no need for -static-pie.
   425  			CFlags: []string{"-m64", "-static", "-lutil"},
   426  			NeedSyscallDefine: func(nr uint64) bool {
   427  				switch nr {
   428  				case 8: // SYS___tfork
   429  					return true
   430  				case 94: // SYS___thrsleep
   431  					return true
   432  				case 198: // SYS___syscall
   433  					return true
   434  				case 295: // SYS___semctl
   435  					return true
   436  				case 301: // SYS___thrwakeup
   437  					return true
   438  				case 302: // SYS___threxit
   439  					return true
   440  				case 303: // SYS___thrsigdivert
   441  					return true
   442  				case 304: // SYS___getcwd
   443  					return true
   444  				case 329: // SYS___set_tcb
   445  					return true
   446  				case 330: // SYS___get_tcb
   447  					return true
   448  				}
   449  				return false
   450  			},
   451  		},
   452  	},
   453  	Fuchsia: {
   454  		AMD64: {
   455  			PtrSize:          8,
   456  			PageSize:         4 << 10,
   457  			LittleEndian:     true,
   458  			KernelHeaderArch: "x64",
   459  			CCompiler:        sourceDirVar + "/prebuilt/third_party/clang/linux-x64/bin/clang",
   460  			Objdump:          sourceDirVar + "/prebuilt/third_party/clang/linux-x64/bin/llvm-objdump",
   461  			CFlags:           fuchsiaCFlags("x64", "x86_64"),
   462  		},
   463  		ARM64: {
   464  			PtrSize:          8,
   465  			PageSize:         4 << 10,
   466  			LittleEndian:     true,
   467  			KernelHeaderArch: ARM64,
   468  			CCompiler:        sourceDirVar + "/prebuilt/third_party/clang/linux-x64/bin/clang",
   469  			Objdump:          sourceDirVar + "/prebuilt/third_party/clang/linux-x64/bin/llvm-objdump",
   470  			CFlags:           fuchsiaCFlags(ARM64, "aarch64"),
   471  		},
   472  	},
   473  	Windows: {
   474  		AMD64: {
   475  			PtrSize: 8,
   476  			// TODO(dvyukov): what should we do about 4k vs 64k?
   477  			PageSize:     4 << 10,
   478  			LittleEndian: true,
   479  		},
   480  	},
   481  	Trusty: {
   482  		ARM: {
   483  			PtrSize:           4,
   484  			PageSize:          4 << 10,
   485  			LittleEndian:      true,
   486  			NeedSyscallDefine: dontNeedSyscallDefine,
   487  		},
   488  	},
   489  }
   490  
   491  var oses = map[string]osCommon{
   492  	Linux: {
   493  		SyscallNumbers:         true,
   494  		SyscallPrefix:          "__NR_",
   495  		ExecutorUsesShmem:      true,
   496  		ExecutorUsesForkServer: true,
   497  		KernelObject:           "vmlinux",
   498  		PseudoSyscallDeps: map[string][]string{
   499  			"syz_read_part_table": {"memfd_create"},
   500  			"syz_mount_image":     {"memfd_create"},
   501  			"syz_io_uring_setup":  {"io_uring_setup"},
   502  			"syz_clone3":          {"clone3", "exit"},
   503  			"syz_clone":           {"clone", "exit"},
   504  			"syz_pidfd_open":      {"pidfd_open"},
   505  		},
   506  		cflags: []string{"-static-pie"},
   507  	},
   508  	FreeBSD: {
   509  		SyscallNumbers:         true,
   510  		Int64SyscallArgs:       true,
   511  		SyscallPrefix:          "SYS_",
   512  		ExecutorUsesShmem:      true,
   513  		ExecutorUsesForkServer: true,
   514  		KernelObject:           "kernel.full",
   515  		CPP:                    "g++",
   516  		// FreeBSD is missing toolchain support for static PIEs.
   517  		cflags: []string{
   518  			"-static",
   519  			"-lc++",
   520  			// For some configurations -no-pie is passed to the compiler,
   521  			// which is not used by clang.
   522  			// Ensure clang does not complain about it.
   523  			"-Wno-unused-command-line-argument",
   524  		},
   525  	},
   526  	Darwin: {
   527  		SyscallNumbers:    true,
   528  		Int64SyscallArgs:  true,
   529  		SyscallPrefix:     "SYS_",
   530  		ExecutorUsesShmem: true,
   531  		// FIXME(HerrSpace): ForkServer is b0rked in a peculiar way. I did some
   532  		// printf debugging in parseOutput in ipc.go. It usually works for a
   533  		// few executions. Eventually the reported ncmd stops making sense and
   534  		// the resulting replies are partially garbage. I also looked at the
   535  		// executor side of things, but that's harder to track as we are just
   536  		// banging bytes in the shmem there and don't use structs like on the
   537  		// go side.
   538  		ExecutorUsesForkServer: false,
   539  		KernelObject:           "kernel.kasan",
   540  		// Note: We need a real g++ here, not the symlink to clang++ common on
   541  		// macOS systems. Homebrews gcc package suffixes these with the gcc
   542  		// version to avoid conflicting with the macOS symlink. Currently -11.
   543  		CPP:    "g++-11",
   544  		cflags: []string{"-lc++"},
   545  	},
   546  	NetBSD: {
   547  		BuildOS:                Linux,
   548  		SyscallNumbers:         true,
   549  		SyscallPrefix:          "SYS_",
   550  		ExecutorUsesShmem:      true,
   551  		ExecutorUsesForkServer: true,
   552  		KernelObject:           "netbsd.gdb",
   553  	},
   554  	OpenBSD: {
   555  		SyscallNumbers:         false,
   556  		SyscallPrefix:          "SYS_",
   557  		ExecutorUsesShmem:      true,
   558  		ExecutorUsesForkServer: true,
   559  		KernelObject:           "bsd.gdb",
   560  		CPP:                    "ecpp",
   561  	},
   562  	Fuchsia: {
   563  		BuildOS:                Linux,
   564  		SyscallNumbers:         false,
   565  		ExecutorUsesShmem:      false,
   566  		ExecutorUsesForkServer: false,
   567  		HostFuzzer:             true,
   568  		ExecutorBin:            "syz-executor",
   569  		KernelObject:           "zircon.elf",
   570  	},
   571  	Windows: {
   572  		SyscallNumbers:         false,
   573  		ExecutorUsesShmem:      false,
   574  		ExecutorUsesForkServer: false,
   575  		ExeExtension:           ".exe",
   576  		KernelObject:           "vmlinux",
   577  	},
   578  	Trusty: {
   579  		SyscallNumbers:   true,
   580  		Int64SyscallArgs: true,
   581  		SyscallPrefix:    "__NR_",
   582  	},
   583  }
   584  
   585  var (
   586  	commonCFlags = []string{
   587  		"-std=c++11",
   588  		"-I.",
   589  		"-Iexecutor/_include",
   590  		"-O2",
   591  		"-pthread",
   592  		"-Wall",
   593  		"-Werror",
   594  		"-Wparentheses",
   595  		"-Wunused-const-variable",
   596  		"-Wframe-larger-than=16384", // executor uses stacks of limited size, so no jumbo frames
   597  		"-Wno-stringop-overflow",
   598  		"-Wno-array-bounds",
   599  		"-Wno-format-overflow",
   600  		"-Wno-unused-but-set-variable",
   601  		"-Wno-unused-command-line-argument",
   602  	}
   603  	optionalCFlags = map[string]bool{
   604  		"-static":                           true, // some distributions don't have static libraries
   605  		"-static-pie":                       true, // this flag is also not supported everywhere
   606  		"-Wunused-const-variable":           true, // gcc 5 does not support this flag
   607  		"-fsanitize=address":                true, // some OSes don't have ASAN
   608  		"-Wno-stringop-overflow":            true,
   609  		"-Wno-array-bounds":                 true,
   610  		"-Wno-format-overflow":              true,
   611  		"-Wno-unused-but-set-variable":      true,
   612  		"-Wno-unused-command-line-argument": true,
   613  	}
   614  	fallbackCFlags = map[string]string{
   615  		"-static-pie": "-static", // if an ASLR static binary is impossible, build just a static one
   616  	}
   617  )
   618  
   619  func fuchsiaCFlags(arch, clangArch string) []string {
   620  	out := sourceDirVar + "/out/" + arch
   621  	return []string{
   622  		"-Wno-deprecated",
   623  		"-target", clangArch + "-fuchsia",
   624  		"-ldriver",
   625  		"-lfdio",
   626  		"-lzircon",
   627  		"--sysroot", out + "/zircon_toolchain/obj/zircon/public/sysroot/sysroot",
   628  		"-I", sourceDirVar + "/sdk/lib/fdio/include",
   629  		"-I", sourceDirVar + "/zircon/system/ulib/fidl/include",
   630  		"-I", sourceDirVar + "/src/lib/ddk/include",
   631  		"-I", out + "/fidling/gen/sdk/fidl/fuchsia.device",
   632  		"-I", out + "/fidling/gen/sdk/fidl/fuchsia.device.manager",
   633  		"-I", out + "/fidling/gen/sdk/fidl/fuchsia.hardware.nand",
   634  		"-I", out + "/fidling/gen/sdk/fidl/fuchsia.hardware.power.statecontrol",
   635  		"-I", out + "/fidling/gen/sdk/fidl/fuchsia.hardware.usb.peripheral",
   636  		"-I", out + "/fidling/gen/zircon/vdso/zx",
   637  		"-L", out + "/" + arch + "-shared",
   638  	}
   639  }
   640  
   641  func init() {
   642  	for OS, archs := range List {
   643  		for arch, target := range archs {
   644  			initTarget(target, OS, arch)
   645  		}
   646  	}
   647  	arch32, arch64 := splitArch(runtime.GOARCH)
   648  	goos := runtime.GOOS
   649  	if goos == "android" {
   650  		goos = Linux
   651  	}
   652  	for _, target := range List[TestOS] {
   653  		if List[goos] == nil {
   654  			continue
   655  		}
   656  		arch := arch64
   657  		if target.PtrSize == 4 {
   658  			arch = arch32
   659  		}
   660  		host := List[goos][arch]
   661  		if host == nil {
   662  			continue
   663  		}
   664  		target.CCompiler = host.CCompiler
   665  		target.CPP = host.CPP
   666  		target.CFlags = append(append([]string{}, host.CFlags...), target.CFlags...)
   667  		target.CFlags = processMergedFlags(target.CFlags)
   668  		// At least FreeBSD/386 requires a non-default DataOffset value.
   669  		target.DataOffset = host.DataOffset
   670  		// In ESA/390 mode, the CPU is able to address only 31bit of memory but
   671  		// arithmetic operations are still 32bit
   672  		// Fix cflags by replacing compiler's -m32 option with -m31
   673  		if arch == S390x {
   674  			for i := range target.CFlags {
   675  				target.CFlags[i] = strings.Replace(target.CFlags[i], "-m32", "-m31", -1)
   676  			}
   677  		}
   678  		if runtime.GOOS == OpenBSD {
   679  			target.BrokenCompiler = "can't build TestOS on OpenBSD due to missing syscall function."
   680  		}
   681  		target.BuildOS = goos
   682  	}
   683  }
   684  
   685  func initTarget(target *Target, OS, arch string) {
   686  	if common, ok := oses[OS]; ok {
   687  		target.osCommon = common
   688  	}
   689  	target.init = new(sync.Once)
   690  	target.initOther = new(sync.Once)
   691  	target.OS = OS
   692  	target.Arch = arch
   693  	if target.KernelArch == "" {
   694  		target.KernelArch = target.Arch
   695  	}
   696  	if target.NeedSyscallDefine == nil {
   697  		target.NeedSyscallDefine = needSyscallDefine
   698  	}
   699  	if target.DataOffset == 0 {
   700  		target.DataOffset = 512 << 20
   701  	}
   702  	target.NumPages = (16 << 20) / target.PageSize
   703  	sourceDir := getSourceDir(target)
   704  	for sourceDir != "" && sourceDir[len(sourceDir)-1] == '/' {
   705  		sourceDir = sourceDir[:len(sourceDir)-1]
   706  	}
   707  	target.replaceSourceDir(&target.CCompiler, sourceDir)
   708  	target.replaceSourceDir(&target.Objdump, sourceDir)
   709  	for i := range target.CFlags {
   710  		target.replaceSourceDir(&target.CFlags[i], sourceDir)
   711  	}
   712  	if OS == Linux && arch == runtime.GOARCH {
   713  		// Don't use cross-compiler for native compilation, there are cases when this does not work:
   714  		// https://github.com/google/syzkaller/pull/619
   715  		// https://github.com/google/syzkaller/issues/387
   716  		// https://github.com/google/syzkaller/commit/06db3cec94c54e1cf720cdd5db72761514569d56
   717  		target.Triple = ""
   718  	}
   719  	if target.CCompiler == "" {
   720  		target.setCompiler(useClang)
   721  	}
   722  	if target.CPP == "" {
   723  		target.CPP = "cpp"
   724  	}
   725  	if target.Objdump == "" {
   726  		target.Objdump = "objdump"
   727  		if target.Triple != "" {
   728  			target.Objdump = target.Triple + "-objdump"
   729  		}
   730  	}
   731  	if target.BuildOS == "" {
   732  		target.BuildOS = OS
   733  	}
   734  	if runtime.GOOS != target.BuildOS {
   735  		// Spoil native binaries if they are not usable, so that nobody tries to use them later.
   736  		target.CCompiler = fmt.Sprintf("cant-build-%v-on-%v", target.OS, runtime.GOOS)
   737  		target.CPP = target.CCompiler
   738  	}
   739  	for _, flags := range [][]string{commonCFlags, target.osCommon.cflags} {
   740  		target.CFlags = append(target.CFlags, flags...)
   741  	}
   742  	if OS == TestOS {
   743  		if runtime.GOARCH != S390x {
   744  			target.LittleEndian = true
   745  		} else {
   746  			target.LittleEndian = false
   747  		}
   748  	}
   749  	if target.LittleEndian {
   750  		target.HostEndian = binary.LittleEndian
   751  	} else {
   752  		target.HostEndian = binary.BigEndian
   753  	}
   754  	// Temporal hack.
   755  	if OS == Linux && os.Getenv("SYZ_STARNIX_HACK") != "" {
   756  		target.ExecutorUsesShmem = false
   757  		target.ExecutorUsesForkServer = false
   758  		target.HostFuzzer = true
   759  	}
   760  	target.initAddr2Line()
   761  }
   762  
   763  func (target *Target) initAddr2Line() {
   764  	// Initialize addr2line lazily since lots of tests don't need it,
   765  	// but we invoke a number of external binaries during addr2line detection.
   766  	var (
   767  		init sync.Once
   768  		bin  string
   769  		err  error
   770  	)
   771  	target.Addr2Line = func() (string, error) {
   772  		init.Do(func() { bin, err = target.findAddr2Line() })
   773  		return bin, err
   774  	}
   775  }
   776  
   777  func (target *Target) findAddr2Line() (string, error) {
   778  	// Try llvm-addr2line first as it's significantly faster on large binaries.
   779  	// But it's unclear if it works for darwin binaries.
   780  	if target.OS != Darwin {
   781  		if path, err := exec.LookPath("llvm-addr2line"); err == nil {
   782  			return path, nil
   783  		}
   784  	}
   785  	bin := "addr2line"
   786  	if target.Triple != "" {
   787  		bin = target.Triple + "-" + bin
   788  	}
   789  	if target.OS != Darwin || target.Arch != AMD64 {
   790  		return bin, nil
   791  	}
   792  	// A special check for darwin kernel to produce a more useful error.
   793  	cmd := exec.Command(bin, "--help")
   794  	cmd.Env = append(os.Environ(), "LC_ALL=C")
   795  	out, err := cmd.CombinedOutput()
   796  	if err != nil {
   797  		return "", fmt.Errorf("addr2line execution failed: %w", err)
   798  	}
   799  	if !bytes.Contains(out, []byte("supported targets:")) {
   800  		return "", fmt.Errorf("addr2line output didn't contain supported targets")
   801  	}
   802  	if !bytes.Contains(out, []byte("mach-o-x86-64")) {
   803  		return "", fmt.Errorf("addr2line was built without mach-o-x86-64 support")
   804  	}
   805  	return bin, nil
   806  }
   807  
   808  func (target *Target) Timeouts(slowdown int) Timeouts {
   809  	if slowdown <= 0 {
   810  		panic(fmt.Sprintf("bad slowdown %v", slowdown))
   811  	}
   812  	timeouts := target.timeouts
   813  	timeouts.Slowdown = slowdown
   814  	timeouts.Scale = time.Duration(slowdown)
   815  	if timeouts.Scale > 3 {
   816  		timeouts.Scale = 3
   817  	}
   818  	if timeouts.Syscall == 0 {
   819  		timeouts.Syscall = 50 * time.Millisecond
   820  	}
   821  	if timeouts.Program == 0 {
   822  		timeouts.Program = 5 * time.Second
   823  	}
   824  	if timeouts.NoOutput == 0 {
   825  		// The timeout used to be 3 mins for a long time.
   826  		// But (1) we were seeing flakes on linux where net namespace
   827  		// destruction can be really slow, and (2) gVisor watchdog timeout
   828  		// is 3 mins + 1/4 of that for checking period = 3m45s.
   829  		// Current linux max timeout is CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=140
   830  		// and workqueue.watchdog_thresh=140 which both actually result
   831  		// in 140-280s detection delay.
   832  		// So the current timeout is 5 mins (300s).
   833  		// We don't want it to be too long too because it will waste time on real hangs.
   834  		timeouts.NoOutput = 5 * time.Minute
   835  	}
   836  	if timeouts.VMRunningTime == 0 {
   837  		timeouts.VMRunningTime = time.Hour
   838  	}
   839  	timeouts.Syscall *= time.Duration(slowdown)
   840  	timeouts.Program *= timeouts.Scale
   841  	timeouts.NoOutput *= timeouts.Scale
   842  	timeouts.VMRunningTime *= timeouts.Scale
   843  	timeouts.NoOutputRunningTime = timeouts.NoOutput + time.Minute
   844  	return timeouts
   845  }
   846  
   847  func (target *Target) setCompiler(clang bool) {
   848  	// setCompiler may be called effectively twice for target.other,
   849  	// so first we remove flags the previous call may have added.
   850  	pos := 0
   851  	for _, flag := range target.CFlags {
   852  		if flag == "-ferror-limit=0" ||
   853  			strings.HasPrefix(flag, "--target=") {
   854  			continue
   855  		}
   856  		target.CFlags[pos] = flag
   857  		pos++
   858  	}
   859  	target.CFlags = target.CFlags[:pos]
   860  	if clang {
   861  		target.CCompiler = "clang"
   862  		target.KernelCompiler = "clang"
   863  		target.KernelLinker = "ld.lld"
   864  		if target.Triple != "" {
   865  			target.CFlags = append(target.CFlags, "--target="+target.Triple)
   866  		}
   867  		target.CFlags = append(target.CFlags, "-ferror-limit=0")
   868  	} else {
   869  		target.CCompiler = "gcc"
   870  		target.KernelCompiler = ""
   871  		target.KernelLinker = ""
   872  		if target.Triple != "" {
   873  			target.CCompiler = target.Triple + "-" + target.CCompiler
   874  		}
   875  	}
   876  }
   877  
   878  func (target *Target) replaceSourceDir(param *string, sourceDir string) {
   879  	if !strings.Contains(*param, sourceDirVar) {
   880  		return
   881  	}
   882  	if sourceDir == "" {
   883  		target.BrokenCompiler = "SOURCEDIR is not set"
   884  		return
   885  	}
   886  	*param = strings.Replace(*param, sourceDirVar, sourceDir, -1)
   887  }
   888  
   889  func (target *Target) lazyInit() {
   890  	if runtime.GOOS != target.BuildOS || target.BrokenCompiler != "" {
   891  		return
   892  	}
   893  	// Only fail on CI for native build.
   894  	// On CI we want to fail loudly if cross-compilation breaks.
   895  	// Also fail if SOURCEDIR_GOOS is set b/c in that case user probably assumes it will work.
   896  	if (target.OS != runtime.GOOS || !runningOnCI) && getSourceDir(target) == "" {
   897  		if _, err := exec.LookPath(target.CCompiler); err != nil {
   898  			target.BrokenCompiler = fmt.Sprintf("%v is missing (%v)", target.CCompiler, err)
   899  			return
   900  		}
   901  	}
   902  
   903  	flagsToCheck := append([]string{}, target.CFlags...)
   904  	for _, value := range fallbackCFlags {
   905  		flagsToCheck = append(flagsToCheck, value)
   906  	}
   907  
   908  	flags := make(map[string]*bool)
   909  	commonCFlags := []string{}
   910  	uncommonCFlags := []string{}
   911  	var wg sync.WaitGroup
   912  	for _, flag := range flagsToCheck {
   913  		if !optionalCFlags[flag] {
   914  			commonCFlags = append(commonCFlags, flag)
   915  			continue
   916  		}
   917  		uncommonCFlags = append(uncommonCFlags, flag)
   918  	}
   919  	for _, flag := range uncommonCFlags {
   920  		_, exists := flags[flag]
   921  		if exists {
   922  			continue
   923  		}
   924  		res := new(bool)
   925  		flags[flag] = res
   926  		wg.Add(1)
   927  		go func(flag string) {
   928  			defer wg.Done()
   929  			*res = checkFlagSupported(target, commonCFlags, flag)
   930  		}(flag)
   931  	}
   932  	wg.Wait()
   933  	newCFlags := []string{}
   934  	for _, flag := range target.CFlags {
   935  		for {
   936  			if res := flags[flag]; res == nil || *res {
   937  				// The flag is either verified to be supported or must be supported.
   938  				newCFlags = append(newCFlags, flag)
   939  			} else if fallback := fallbackCFlags[flag]; fallback != "" {
   940  				// The flag is not supported, but probably we can replace it by another one.
   941  				flag = fallback
   942  				continue
   943  			}
   944  			break
   945  		}
   946  	}
   947  	target.CFlags = newCFlags
   948  	// Check that the compiler is actually functioning. It may be present, but still broken.
   949  	// Common for Linux distros, over time we've seen:
   950  	//	Error: alignment too large: 15 assumed
   951  	//	fatal error: asm/unistd.h: No such file or directory
   952  	//	fatal error: asm/errno.h: No such file or directory
   953  	//	collect2: error: ld terminated with signal 11 [Segmentation fault]
   954  	if runningOnCI || getSourceDir(target) != "" {
   955  		return // On CI all compilers are expected to work, so we don't do the following check.
   956  	}
   957  	args := []string{"-x", "c++", "-", "-o", "/dev/null"}
   958  	args = append(args, target.CFlags...)
   959  	cmd := exec.Command(target.CCompiler, args...)
   960  	cmd.Stdin = strings.NewReader(simpleProg)
   961  	if out, err := cmd.CombinedOutput(); err != nil {
   962  		target.BrokenCompiler = string(out)
   963  		return
   964  	}
   965  }
   966  
   967  func checkFlagSupported(target *Target, targetCFlags []string, flag string) bool {
   968  	args := []string{"-x", "c++", "-", "-o", "/dev/null", "-Werror", flag}
   969  	args = append(args, targetCFlags...)
   970  	cmd := exec.Command(target.CCompiler, args...)
   971  	cmd.Stdin = strings.NewReader(simpleProg)
   972  	return cmd.Run() == nil
   973  }
   974  
   975  // Split an arch into a pair of related 32 and 64 bit arch names.
   976  // If the arch is unknown, we assume that the arch is 64 bit.
   977  func splitArch(arch string) (string, string) {
   978  	type pair struct {
   979  		name32 string
   980  		name64 string
   981  	}
   982  	pairs := []pair{
   983  		{I386, AMD64},
   984  		{ARM, ARM64},
   985  	}
   986  	for _, p := range pairs {
   987  		if p.name32 == arch || p.name64 == arch {
   988  			return p.name32, p.name64
   989  		}
   990  	}
   991  	return "", arch
   992  }
   993  
   994  func processMergedFlags(flags []string) []string {
   995  	mutuallyExclusive := [][]string{
   996  		// For GCC, "-static-pie -static" is not equal to "-static".
   997  		// And since we do it anyway, also clean up those that do get overridden -
   998  		// this will improve the flags list readability.
   999  		{"-static", "-static-pie", "-no-pie", "-pie"},
  1000  	}
  1001  	// For mutually exclusive groups, keep only the last flag.
  1002  	for _, group := range mutuallyExclusive {
  1003  		m := map[string]bool{}
  1004  		for _, s := range group {
  1005  			m[s] = true
  1006  		}
  1007  		keep := ""
  1008  		for i := len(flags) - 1; i >= 0; i-- {
  1009  			if m[flags[i]] {
  1010  				keep = flags[i]
  1011  				break
  1012  			}
  1013  		}
  1014  		if keep != "" {
  1015  			newFlags := []string{}
  1016  			for _, s := range flags {
  1017  				if s == keep || !m[s] {
  1018  					newFlags = append(newFlags, s)
  1019  				}
  1020  			}
  1021  			flags = newFlags
  1022  		}
  1023  	}
  1024  	// Clean up duplicates.
  1025  	dup := map[string]bool{}
  1026  	newFlags := []string{}
  1027  	for _, s := range flags {
  1028  		if dup[s] {
  1029  			continue
  1030  		}
  1031  		newFlags = append(newFlags, s)
  1032  		dup[s] = true
  1033  	}
  1034  	return newFlags
  1035  }
  1036  
  1037  func getSourceDir(target *Target) string {
  1038  	// First try the most granular env option.
  1039  	name := fmt.Sprintf("SOURCEDIR_%s_%s_%s_%s",
  1040  		strings.ToUpper(target.OS), strings.ToUpper(target.Arch),
  1041  		strings.ToUpper(runtime.GOOS), strings.ToUpper(runtime.GOARCH),
  1042  	)
  1043  	if ret := os.Getenv(name); ret != "" {
  1044  		return ret
  1045  	}
  1046  	// .. then the older one.
  1047  	name = fmt.Sprintf("SOURCEDIR_%s", strings.ToUpper(target.OS))
  1048  	if ret := os.Getenv(name); ret != "" {
  1049  		return ret
  1050  	}
  1051  	return os.Getenv("SOURCEDIR")
  1052  }
  1053  
  1054  func needSyscallDefine(nr uint64) bool     { return true }
  1055  func dontNeedSyscallDefine(nr uint64) bool { return false }
  1056  
  1057  var (
  1058  	runningOnCI = os.Getenv("CI") != ""
  1059  	useClang    = os.Getenv("SYZ_CLANG") != ""
  1060  )
  1061  
  1062  const (
  1063  	sourceDirVar = "${SOURCEDIR}"
  1064  	simpleProg   = `
  1065  #include <stdio.h>
  1066  #include <dirent.h> // ensures that system headers are installed
  1067  #include <algorithm> // ensures that C++ headers are installed
  1068  int main() { printf("Hello, World!\n"); }
  1069  `
  1070  )