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 )