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 )