aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/host/syscalls_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/host/syscalls_linux.go')
-rw-r--r--pkg/host/syscalls_linux.go568
1 files changed, 0 insertions, 568 deletions
diff --git a/pkg/host/syscalls_linux.go b/pkg/host/syscalls_linux.go
deleted file mode 100644
index b690f1de7..000000000
--- a/pkg/host/syscalls_linux.go
+++ /dev/null
@@ -1,568 +0,0 @@
-// Copyright 2015 syzkaller project authors. All rights reserved.
-// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
-
-package host
-
-import (
- "bytes"
- "fmt"
- "os"
- "regexp"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "syscall"
- "time"
-
- "github.com/google/syzkaller/pkg/osutil"
- "github.com/google/syzkaller/prog"
- "github.com/google/syzkaller/sys/targets"
- "golang.org/x/sys/unix"
-)
-
-func isSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if strings.HasPrefix(c.CallName, "syz_") {
- return isSupportedSyzkall(c, target, sandbox)
- }
- if reason := isSupportedLSM(c); reason != "" {
- return false, reason
- }
- if strings.HasPrefix(c.Name, "socket$") ||
- strings.HasPrefix(c.Name, "socketpair$") {
- return isSupportedSocket(c)
- }
- if strings.HasPrefix(c.Name, "openat$") {
- return isSupportedOpenAt(c)
- }
- if strings.HasPrefix(c.Name, "mount$") {
- return isSupportedMount(c, sandbox)
- }
- if c.CallName == "pkey_alloc" {
- return isSyzPkeySetSupported(c, target, sandbox)
- }
- return isSupportedSyscall(c, target)
-}
-
-func isSupportedSyscall(c *prog.Syscall, target *prog.Target) (bool, string) {
- // There are 3 possible strategies for detecting supported syscalls:
- // 1. Executes all syscalls with presumably invalid arguments and check for ENOprog.
- // But not all syscalls are safe to execute. For example, pause will hang,
- // while setpgrp will push the process into own process group.
- // 2. Check presence of /sys/kernel/debug/tracing/events/syscalls/sys_enter_* files.
- // This requires root and CONFIG_FTRACE_SYSCALLS. Also it lies for some syscalls.
- // For example, on x86_64 it says that sendfile is not present (only sendfile64).
- // 3. Check sys_syscallname in /proc/kallsyms.
- // Requires CONFIG_KALLSYMS.
- // Kallsyms seems to be the most reliable and fast. That's what we use first.
- // If kallsyms is not present, we fallback to execution of syscalls.
- kallsymsOnce.Do(func() {
- kallsyms, _ := os.ReadFile("/proc/kallsyms")
- if len(kallsyms) == 0 {
- return
- }
- kallsymsSyscallSet = parseKallsyms(kallsyms, target.Arch)
- })
- if !testFallback && len(kallsymsSyscallSet) != 0 {
- r, v := isSupportedKallsyms(c)
- return r, v
- }
- return isSupportedTrial(c)
-}
-
-func isSupportedSyscallName(name string, target *prog.Target) (bool, string) {
- syscall := target.SyscallMap[name]
- if syscall == nil {
- return false, fmt.Sprintf("sys_%v is not present in the target", name)
- }
- return isSupportedSyscall(syscall, target)
-}
-
-func parseKallsyms(kallsyms []byte, arch string) map[string]bool {
- set := make(map[string]bool)
- var re *regexp.Regexp
- switch arch {
- case targets.I386, targets.AMD64:
- re = regexp.MustCompile(` T (__ia32_|__x64_)?sys_([^\n]+)\n`)
- case targets.ARM, targets.ARM64:
- re = regexp.MustCompile(` T (__arm64_)?sys_([^\n]+)\n`)
- case targets.PPC64LE:
- re = regexp.MustCompile(` T ()?sys_([^\n]+)\n`)
- case targets.MIPS64LE:
- re = regexp.MustCompile(` T sys_(mips_)?([^\n]+)\n`)
- case targets.S390x:
- re = regexp.MustCompile(` T (__s390_|__s390x_)?sys_([^\n]+)\n`)
- case targets.RiscV64:
- re = regexp.MustCompile(` T sys_(riscv_)?([^\n]+)\n`)
- default:
- panic("unsupported arch for kallsyms parsing")
- }
- matches := re.FindAllSubmatch(kallsyms, -1)
- for _, m := range matches {
- name := string(m[2])
- set[name] = true
- }
- return set
-}
-
-func isSupportedKallsyms(c *prog.Syscall) (bool, string) {
- name := c.CallName
- if newname := kallsymsRenameMap[name]; newname != "" {
- name = newname
- }
- if !kallsymsSyscallSet[name] {
- return false, fmt.Sprintf("sys_%v is not enabled in the kernel (not present in /proc/kallsyms)", name)
- }
- return true, ""
-}
-
-func isSupportedTrial(c *prog.Syscall) (bool, string) {
- switch c.CallName {
- // These known to cause hangs.
- case "exit", "pause":
- return true, ""
- }
- reason := fmt.Sprintf("sys_%v is not enabled in the kernel (returns ENOSYS)", c.CallName)
- trialMu.Lock()
- defer trialMu.Unlock()
- if res, ok := trialSupported[c.NR]; ok {
- return res, reason
- }
- cmd := osutil.Command(os.Args[0])
- cmd.Env = []string{fmt.Sprintf("SYZ_TRIAL_TEST=%v", c.NR)}
- _, err := osutil.Run(10*time.Second, cmd)
- res := err != nil
- trialSupported[c.NR] = res
- return res, reason
-}
-
-func init() {
- str := os.Getenv("SYZ_TRIAL_TEST")
- if str == "" {
- return
- }
- nr, err := strconv.Atoi(str)
- if err != nil {
- panic(err)
- }
- arg := ^uintptr(0) - 1e4 // something as invalid as possible
- _, _, err = syscall.Syscall6(uintptr(nr), arg, arg, arg, arg, arg, arg)
- if err == syscall.ENOSYS {
- os.Exit(0)
- }
- os.Exit(1)
-}
-
-// Some syscall names diverge in __NR_* consts and kallsyms.
-// umount2 is renamed to umount in arch/x86/entry/syscalls/syscall_64.tbl.
-// Where umount is renamed to oldumount is unclear.
-var (
- kallsymsOnce sync.Once
- kallsymsSyscallSet map[string]bool
- kallsymsRenameMap = map[string]string{
- "umount": "oldumount",
- "umount2": "umount",
- "stat": "newstat",
- }
- trialMu sync.Mutex
- trialSupported = make(map[uint64]bool)
- filesystems []byte
- filesystemsOnce sync.Once
- lsmOnce sync.Once
- lsmError error
- lsmDisabled map[string]bool
-)
-
-func isSyzUsbSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkUSBEmulation()
- return reason == "", reason
-}
-
-func alwaysSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return true, ""
-}
-
-func isNetInjectionSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkNetInjection()
- return reason == "", reason
-}
-
-func isVhciInjectionSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkVhciInjection()
- return reason == "", reason
-}
-
-func isWifiEmulationSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- reason := checkWifiEmulation()
- return reason == "", reason
-}
-
-func isSyzKvmSetupCPUSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- switch c.Name {
- case "syz_kvm_setup_cpu$x86":
- if runtime.GOARCH == targets.AMD64 || runtime.GOARCH == targets.I386 {
- return true, ""
- }
- case "syz_kvm_setup_cpu$arm64":
- if runtime.GOARCH == targets.ARM64 {
- return true, ""
- }
- case "syz_kvm_setup_cpu$ppc64":
- if runtime.GOARCH == targets.PPC64LE {
- return true, ""
- }
- }
- return false, "unsupported arch"
-}
-
-func isSyzOpenDevSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedSyzOpenDev(sandbox, c)
-}
-
-func isSyzInitNetSocketSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := onlySandboxNone(sandbox); !ok {
- return false, reason
- }
- return isSupportedSocket(c)
-}
-
-func isSyzSocketConnectNvmeTCPSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return onlySandboxNone(sandbox)
-}
-
-func isSyzGenetlinkGetFamilyIDSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
- if fd == -1 {
- return false, fmt.Sprintf("socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC) failed: %v", err)
- }
- // TODO: try to obtain actual family ID here. It will disable whole sets of sendmsg syscalls.
- syscall.Close(fd)
- return true, ""
-}
-
-func isSyzMountImageSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := onlySandboxNone(sandbox); !ok {
- return ok, reason
- }
- fstype, ok := extractStringConst(c.Args[0].Type)
- if !ok {
- panic("syz_mount_image arg is not string")
- }
- return isSupportedFilesystem(fstype)
-}
-
-func isSyzReadPartTableSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return onlySandboxNone(sandbox)
-}
-
-func isSyzIoUringSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedSyscallName("io_uring_setup", target)
-}
-
-func isSyzMemcpySupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- ret, msg := isSyzIoUringSupported(c, target, sandbox)
- if ret {
- return ret, msg
- }
- return isSyzKvmSetupCPUSupported(c, target, sandbox)
-}
-
-func isBtfVmlinuxSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if err := osutil.IsAccessible("/sys/kernel/btf/vmlinux"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNone(sandbox)
-}
-
-func isSyzFuseSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if ok, reason := isSupportedFilesystem("fuse"); !ok {
- return ok, reason
- }
- if ok, reason := onlySandboxNoneOrNamespace(sandbox); !ok {
- return false, reason
- }
- return true, ""
-}
-
-func isSyzUsbIPSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if err := osutil.IsWritable("/sys/devices/platform/vhci_hcd.0/attach"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNoneOrNamespace(sandbox)
-}
-
-var syzkallSupport = map[string]func(*prog.Syscall, *prog.Target, string) (bool, string){
- "syz_open_dev": isSyzOpenDevSupported,
- "syz_open_procfs": isSyzOpenProcfsSupported,
- "syz_open_pts": alwaysSupported,
- "syz_execute_func": alwaysSupported,
- "syz_emit_ethernet": isNetInjectionSupported,
- "syz_extract_tcp_res": isNetInjectionSupported,
- "syz_usb_connect": isSyzUsbSupported,
- "syz_usb_connect_ath9k": isSyzUsbSupported,
- "syz_usb_disconnect": isSyzUsbSupported,
- "syz_usb_control_io": isSyzUsbSupported,
- "syz_usb_ep_write": isSyzUsbSupported,
- "syz_usb_ep_read": isSyzUsbSupported,
- "syz_kvm_setup_cpu": isSyzKvmSetupCPUSupported,
- "syz_emit_vhci": isVhciInjectionSupported,
- "syz_init_net_socket": isSyzInitNetSocketSupported,
- "syz_genetlink_get_family_id": isSyzGenetlinkGetFamilyIDSupported,
- "syz_mount_image": isSyzMountImageSupported,
- "syz_read_part_table": isSyzReadPartTableSupported,
- "syz_io_uring_submit": isSyzIoUringSupported,
- "syz_io_uring_complete": isSyzIoUringSupported,
- "syz_io_uring_setup": isSyzIoUringSupported,
- "syz_memcpy_off": isSyzMemcpySupported,
- "syz_btf_id_by_name": isBtfVmlinuxSupported,
- "syz_fuse_handle_req": isSyzFuseSupported,
- "syz_80211_inject_frame": isWifiEmulationSupported,
- "syz_80211_join_ibss": isWifiEmulationSupported,
- "syz_usbip_server_init": isSyzUsbIPSupported,
- "syz_clone": alwaysSupported,
- "syz_clone3": alwaysSupported,
- "syz_pkey_set": isSyzPkeySetSupported,
- "syz_socket_connect_nvme_tcp": isSyzSocketConnectNvmeTCPSupported,
- "syz_pidfd_open": alwaysSupported,
-}
-
-func isSupportedSyzkall(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- sysTarget := targets.Get(target.OS, target.Arch)
- for _, depCall := range sysTarget.PseudoSyscallDeps[c.CallName] {
- if ok, reason := isSupportedSyscallName(depCall, target); !ok {
- return ok, reason
- }
- }
- if strings.HasPrefix(c.CallName, "syz_ext_") {
- // Non-mainline pseudo-syscalls in executor/common_ext.h can't have the checking function
- // and are assumed to be unconditionally supported.
- if syzkallSupport[c.CallName] != nil {
- panic("syz_ext_ prefix is reserved for non-mainline pseudo-syscalls")
- }
- return true, ""
- }
- if isSupported, ok := syzkallSupport[c.CallName]; ok {
- return isSupported(c, target, sandbox)
- }
- panic("unknown syzkall: " + c.Name)
-}
-
-func isSupportedSyzOpenDev(sandbox string, c *prog.Syscall) (bool, string) {
- if _, ok := c.Args[0].Type.(*prog.ConstType); ok {
- // This is for syz_open_dev$char/block.
- return true, ""
- }
- fname, ok := extractStringConst(c.Args[0].Type)
- if !ok {
- panic("first open arg is not a pointer to string const")
- }
- if strings.Contains(fname, "/dev/raw/raw#") {
- // For syz_open_dev$char_raw, these files don't exist initially.
- return true, ""
- }
- if !strings.Contains(fname, "#") {
- panic(fmt.Sprintf("%v does not contain # in the file name (should be openat)", c.Name))
- }
- if checkUSBEmulation() == "" {
- // These entries might not be available at boot time,
- // but will be created by connected USB devices.
- USBDevicePrefixes := []string{
- "/dev/hidraw", "/dev/usb/hiddev", "/dev/input/",
- }
- for _, prefix := range USBDevicePrefixes {
- if strings.HasPrefix(fname, prefix) {
- return true, ""
- }
- }
- }
- var check func(dev string) bool
- check = func(dev string) bool {
- if !strings.Contains(dev, "#") {
- // Note: don't try to open them all, some can hang (e.g. /dev/snd/pcmC#D#p).
- return osutil.IsExist(dev)
- }
- for i := 0; i < 10; i++ {
- if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) {
- return true
- }
- }
- return false
- }
- if !check(fname) {
- return false, fmt.Sprintf("file %v does not exist", fname)
- }
- return onlySandboxNoneOrNamespace(sandbox)
-}
-
-func isSupportedLSM(c *prog.Syscall) string {
- lsmOnce.Do(func() {
- data, err := os.ReadFile("/sys/kernel/security/lsm")
- if err != nil {
- // securityfs may not be mounted, but it does not mean
- // that no LSMs are enabled.
- if !os.IsNotExist(err) {
- lsmError = err
- }
- return
- }
- lsmDisabled = make(map[string]bool)
- for _, lsm := range []string{"selinux", "apparmor", "smack"} {
- if !strings.Contains(string(data), lsm) {
- lsmDisabled[lsm] = true
- }
- }
- })
- if lsmError != nil {
- return lsmError.Error()
- }
- for lsm := range lsmDisabled {
- if strings.Contains(strings.ToLower(c.Name), lsm) {
- return fmt.Sprintf("LSM %v is not enabled", lsm)
- }
- }
- return ""
-}
-
-func onlySandboxNone(sandbox string) (bool, string) {
- if syscall.Getuid() != 0 || sandbox != "none" {
- return false, "only supported under root with sandbox=none"
- }
- return true, ""
-}
-
-func onlySandboxNoneOrNamespace(sandbox string) (bool, string) {
- if syscall.Getuid() != 0 || sandbox == "setuid" {
- return false, "only supported under root with sandbox=none/namespace"
- }
- return true, ""
-}
-
-func isSupportedSocket(c *prog.Syscall) (bool, string) {
- af, ok := c.Args[0].Type.(*prog.ConstType)
- if !ok {
- panic("socket family is not const")
- }
- fd, err := syscall.Socket(int(af.Val), 0, 0)
- if fd != -1 {
- syscall.Close(fd)
- }
- if err == syscall.ENOSYS {
- return false, "socket syscall returns ENOSYS"
- }
- if err == syscall.EAFNOSUPPORT {
- return false, "socket family is not supported (EAFNOSUPPORT)"
- }
- proto, ok := c.Args[2].Type.(*prog.ConstType)
- if !ok {
- return true, ""
- }
- var typ uint64
- if arg, ok := c.Args[1].Type.(*prog.ConstType); ok {
- typ = arg.Val
- } else if arg, ok := c.Args[1].Type.(*prog.FlagsType); ok {
- typ = arg.Vals[0]
- } else {
- return true, ""
- }
- fd, err = syscall.Socket(int(af.Val), int(typ), int(proto.Val))
- if fd != -1 {
- syscall.Close(fd)
- return true, ""
- }
- return false, err.Error()
-}
-
-func isSyzOpenProcfsSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- return isSupportedOpenFile(c, 1, nil)
-}
-
-func isSupportedOpenAt(c *prog.Syscall) (bool, string) {
- // Attempt to extract flags from the syscall description.
- var modes []int
- if mode, ok := c.Args[2].Type.(*prog.ConstType); ok {
- modes = []int{int(mode.Val)}
- }
- return isSupportedOpenFile(c, 1, modes)
-}
-
-func isSupportedOpenFile(c *prog.Syscall, filenameArg int, modes []int) (bool, string) {
- fname, ok := extractStringConst(c.Args[filenameArg].Type)
- if !ok || fname == "" || fname[0] != '/' {
- return true, ""
- }
- if len(modes) == 0 {
- modes = []int{syscall.O_RDONLY, syscall.O_WRONLY, syscall.O_RDWR, syscall.O_RDONLY | syscall.O_NONBLOCK}
- }
- var err error
- for _, mode := range modes {
- var fd int
- fd, err = syscall.Open(fname, mode, 0)
- if fd != -1 {
- syscall.Close(fd)
- }
- if err == nil {
- return true, ""
- }
- }
- return false, fmt.Sprintf("open(%v) failed: %v", fname, err)
-}
-
-func isSupportedMount(c *prog.Syscall, sandbox string) (bool, string) {
- fstype, ok := extractStringConst(c.Args[2].Type)
- if !ok {
- panic(fmt.Sprintf("%v: filesystem is not string const", c.Name))
- }
- if ok, reason := isSupportedFilesystem(fstype); !ok {
- return ok, reason
- }
- switch fstype {
- case "fuse", "fuseblk":
- if err := osutil.IsAccessible("/dev/fuse"); err != nil {
- return false, err.Error()
- }
- return onlySandboxNoneOrNamespace(sandbox)
- default:
- return onlySandboxNone(sandbox)
- }
-}
-
-func isSupportedFilesystem(fstype string) (bool, string) {
- filesystemsOnce.Do(func() {
- filesystems, _ = os.ReadFile("/proc/filesystems")
- })
- if !bytes.Contains(filesystems, []byte("\t"+fstype+"\n")) {
- return false, fmt.Sprintf("/proc/filesystems does not contain %v", fstype)
- }
- return true, ""
-}
-
-func extractStringConst(typ prog.Type) (string, bool) {
- ptr, ok := typ.(*prog.PtrType)
- if !ok {
- panic("first open arg is not a pointer to string const")
- }
- str, ok := ptr.Elem.(*prog.BufferType)
- if !ok || str.Kind != prog.BufferString || len(str.Values) == 0 {
- return "", false
- }
- v := str.Values[0]
- for v != "" && v[len(v)-1] == 0 {
- v = v[:len(v)-1] // string terminating \x00
- }
- return v, true
-}
-
-func isSyzPkeySetSupported(c *prog.Syscall, target *prog.Target, sandbox string) (bool, string) {
- if target.Arch != targets.AMD64 && target.Arch != targets.I386 {
- return false, "unsupported arch"
- }
- key, _, err := syscall.Syscall6(unix.SYS_PKEY_ALLOC, 0, 0, 0, 0, 0, 0)
- if err != 0 {
- return false, fmt.Sprintf("pkey_alloc failed: %v", err)
- }
- _, _, err = syscall.Syscall6(unix.SYS_PKEY_FREE, key, 0, 0, 0, 0, 0)
- if err != 0 {
- return false, fmt.Sprintf("pkey_free failed: %v", err)
- }
- return true, ""
-}