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