2015-12-27 11:20:00 +00:00
|
|
|
// 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"
|
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
2017-01-08 16:20:32 +00:00
|
|
|
"runtime"
|
2016-01-25 18:08:17 +00:00
|
|
|
"strconv"
|
2015-12-27 11:20:00 +00:00
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"github.com/google/syzkaller/sys"
|
|
|
|
)
|
|
|
|
|
|
|
|
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
|
|
|
func DetectSupportedSyscalls() (map[*sys.Call]bool, error) {
|
|
|
|
// There are 3 possible strategies:
|
|
|
|
// 1. Executes all syscalls with presumably invalid arguments and check for ENOSYS.
|
|
|
|
// 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. Seems to be the most reliable. That's what we use here.
|
|
|
|
|
2016-08-14 01:15:38 +00:00
|
|
|
kallsyms, _ := ioutil.ReadFile("/proc/kallsyms")
|
2015-12-27 11:20:00 +00:00
|
|
|
supported := make(map[*sys.Call]bool)
|
|
|
|
for _, c := range sys.Calls {
|
2015-12-28 09:45:30 +00:00
|
|
|
if isSupported(kallsyms, c) {
|
2015-12-27 11:20:00 +00:00
|
|
|
supported[c] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return supported, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func isSupported(kallsyms []byte, c *sys.Call) bool {
|
|
|
|
if c.NR == -1 {
|
|
|
|
return false // don't even have a syscall number
|
|
|
|
}
|
2015-12-28 09:45:30 +00:00
|
|
|
if strings.HasPrefix(c.CallName, "syz_") {
|
2016-08-14 01:15:38 +00:00
|
|
|
return isSupportedSyzkall(c)
|
2015-12-28 09:45:30 +00:00
|
|
|
}
|
|
|
|
if strings.HasPrefix(c.Name, "socket$") {
|
2016-08-14 01:15:38 +00:00
|
|
|
return isSupportedSocket(c)
|
2015-12-27 11:20:00 +00:00
|
|
|
}
|
2015-12-28 09:45:30 +00:00
|
|
|
if strings.HasPrefix(c.Name, "open$") {
|
2016-08-14 01:15:38 +00:00
|
|
|
return isSupportedOpen(c)
|
|
|
|
}
|
2017-01-05 14:13:12 +00:00
|
|
|
if strings.HasPrefix(c.Name, "openat$") {
|
|
|
|
return isSupportedOpenAt(c)
|
|
|
|
}
|
2016-08-14 01:15:38 +00:00
|
|
|
if len(kallsyms) == 0 {
|
|
|
|
return true
|
2015-12-28 09:45:30 +00:00
|
|
|
}
|
|
|
|
return bytes.Index(kallsyms, []byte(" T sys_"+c.CallName+"\n")) != -1
|
|
|
|
}
|
|
|
|
|
2016-08-14 01:15:38 +00:00
|
|
|
func isSupportedSyzkall(c *sys.Call) bool {
|
2015-12-27 11:20:00 +00:00
|
|
|
switch c.CallName {
|
2016-09-28 18:06:42 +00:00
|
|
|
case "syz_test":
|
|
|
|
return false
|
2016-01-13 17:57:12 +00:00
|
|
|
case "syz_open_dev":
|
2016-10-31 21:15:13 +00:00
|
|
|
fname, ok := extractStringConst(c.Args[0])
|
2016-01-13 17:57:12 +00:00
|
|
|
if !ok {
|
|
|
|
panic("first open arg is not a pointer to string const")
|
|
|
|
}
|
|
|
|
if syscall.Getuid() != 0 {
|
|
|
|
return false
|
|
|
|
}
|
2016-01-25 18:08:17 +00:00
|
|
|
var check func(dev string) bool
|
|
|
|
check = func(dev string) bool {
|
|
|
|
if !strings.Contains(dev, "#") {
|
|
|
|
_, err := os.Stat(dev)
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
for i := 0; i < 10; i++ {
|
|
|
|
if check(strings.Replace(dev, "#", strconv.Itoa(i), 1)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2016-10-31 21:15:13 +00:00
|
|
|
return check(fname)
|
2016-01-13 17:57:12 +00:00
|
|
|
case "syz_open_pts":
|
|
|
|
return true
|
2015-12-27 11:20:00 +00:00
|
|
|
case "syz_fuse_mount":
|
|
|
|
_, err := os.Stat("/dev/fuse")
|
|
|
|
return err == nil
|
|
|
|
case "syz_fuseblk_mount":
|
|
|
|
_, err := os.Stat("/dev/fuse")
|
|
|
|
return err == nil && syscall.Getuid() == 0
|
2016-10-04 14:12:12 +00:00
|
|
|
case "syz_emit_ethernet":
|
|
|
|
_, err := os.Stat("/dev/net/tun")
|
|
|
|
return err == nil && syscall.Getuid() == 0
|
2017-01-08 16:20:32 +00:00
|
|
|
case "syz_kvm_setup_cpu":
|
|
|
|
switch c.Name {
|
|
|
|
case "syz_kvm_setup_cpu$x86":
|
|
|
|
return runtime.GOARCH == "amd64" || runtime.GOARCH == "386"
|
2017-01-12 10:57:17 +00:00
|
|
|
case "syz_kvm_setup_cpu$arm64":
|
|
|
|
return runtime.GOARCH == "arm64"
|
2017-01-08 16:20:32 +00:00
|
|
|
}
|
2015-12-27 11:20:00 +00:00
|
|
|
}
|
2017-01-08 16:20:32 +00:00
|
|
|
panic("unknown syzkall: " + c.Name)
|
2015-12-27 11:20:00 +00:00
|
|
|
}
|
2015-12-28 09:45:30 +00:00
|
|
|
|
2016-08-14 01:15:38 +00:00
|
|
|
func isSupportedSocket(c *sys.Call) bool {
|
2016-10-19 14:20:37 +00:00
|
|
|
af, ok := c.Args[0].(*sys.ConstType)
|
2015-12-28 09:45:30 +00:00
|
|
|
if !ok {
|
|
|
|
println(c.Name)
|
|
|
|
panic("socket family is not const")
|
|
|
|
}
|
|
|
|
fd, err := syscall.Socket(int(af.Val), 0, 0)
|
|
|
|
if fd != -1 {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
|
|
|
return err != syscall.ENOSYS && err != syscall.EAFNOSUPPORT
|
|
|
|
}
|
|
|
|
|
2016-08-14 01:15:38 +00:00
|
|
|
func isSupportedOpen(c *sys.Call) bool {
|
2016-10-31 21:15:13 +00:00
|
|
|
fname, ok := extractStringConst(c.Args[0])
|
2015-12-28 09:45:30 +00:00
|
|
|
if !ok {
|
|
|
|
return true
|
|
|
|
}
|
2016-10-31 21:15:13 +00:00
|
|
|
fd, err := syscall.Open(fname, syscall.O_RDONLY, 0)
|
2015-12-28 09:45:30 +00:00
|
|
|
if fd != -1 {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
|
|
|
return err == nil
|
|
|
|
}
|
2016-10-31 21:15:13 +00:00
|
|
|
|
2017-01-05 14:13:12 +00:00
|
|
|
func isSupportedOpenAt(c *sys.Call) bool {
|
|
|
|
fname, ok := extractStringConst(c.Args[1])
|
|
|
|
if !ok {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
fd, err := syscall.Open(fname, syscall.O_RDONLY, 0)
|
|
|
|
if fd != -1 {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
|
|
|
return err == nil
|
|
|
|
}
|
|
|
|
|
2016-10-31 21:15:13 +00:00
|
|
|
func extractStringConst(typ sys.Type) (string, bool) {
|
|
|
|
ptr, ok := typ.(*sys.PtrType)
|
|
|
|
if !ok {
|
|
|
|
panic("first open arg is not a pointer to string const")
|
|
|
|
}
|
|
|
|
str, ok := ptr.Type.(*sys.BufferType)
|
|
|
|
if !ok || str.Kind != sys.BufferString || len(str.Values) != 1 {
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
v := str.Values[0]
|
|
|
|
v = v[:len(v)-1] // string terminating \x00
|
|
|
|
return v, true
|
|
|
|
}
|