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"
|
2017-10-16 10:18:50 +00:00
|
|
|
"fmt"
|
2015-12-27 11:20:00 +00:00
|
|
|
"io/ioutil"
|
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"
|
|
|
|
|
2017-06-17 10:23:52 +00:00
|
|
|
"github.com/google/syzkaller/pkg/osutil"
|
2017-09-05 11:31:14 +00:00
|
|
|
"github.com/google/syzkaller/prog"
|
2015-12-27 11:20:00 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// DetectSupportedSyscalls returns list on supported syscalls on host.
|
2018-03-05 11:07:59 +00:00
|
|
|
func DetectSupportedSyscalls(target *prog.Target, sandbox string) (map[*prog.Syscall]bool, error) {
|
2015-12-27 11:20:00 +00:00
|
|
|
// There are 3 possible strategies:
|
2017-09-05 11:31:14 +00:00
|
|
|
// 1. Executes all syscalls with presumably invalid arguments and check for ENOprog.
|
2015-12-27 11:20:00 +00:00
|
|
|
// 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")
|
2017-09-05 11:31:14 +00:00
|
|
|
supported := make(map[*prog.Syscall]bool)
|
2017-09-14 17:25:01 +00:00
|
|
|
for _, c := range target.Syscalls {
|
2018-03-05 11:07:59 +00:00
|
|
|
if isSupported(sandbox, kallsyms, c) {
|
2015-12-27 11:20:00 +00:00
|
|
|
supported[c] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return supported, nil
|
|
|
|
}
|
|
|
|
|
2018-03-05 11:07:59 +00:00
|
|
|
func isSupported(sandbox string, kallsyms []byte, c *prog.Syscall) bool {
|
2015-12-28 09:45:30 +00:00
|
|
|
if strings.HasPrefix(c.CallName, "syz_") {
|
2018-03-05 11:07:59 +00:00
|
|
|
return isSupportedSyzkall(sandbox, 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
|
|
|
}
|
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
|
|
|
}
|
2017-11-20 16:23:24 +00:00
|
|
|
name := c.CallName
|
|
|
|
if newname := kallsymsMap[name]; newname != "" {
|
|
|
|
name = newname
|
|
|
|
}
|
2018-03-08 17:48:26 +00:00
|
|
|
return bytes.Contains(kallsyms, []byte(" T sys_"+name+"\n"))
|
2017-11-20 16:23:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 kallsymsMap = map[string]string{
|
|
|
|
"umount": "oldumount",
|
|
|
|
"umount2": "umount",
|
2015-12-28 09:45:30 +00:00
|
|
|
}
|
|
|
|
|
2018-03-05 11:07:59 +00:00
|
|
|
func isSupportedSyzkall(sandbox string, c *prog.Syscall) bool {
|
2015-12-27 11:20:00 +00:00
|
|
|
switch c.CallName {
|
2016-01-13 17:57:12 +00:00
|
|
|
case "syz_open_dev":
|
2017-09-05 11:31:14 +00:00
|
|
|
if _, ok := c.Args[0].(*prog.ConstType); ok {
|
2017-02-10 12:54:59 +00:00
|
|
|
// This is for syz_open_dev$char/block.
|
|
|
|
// They are currently commented out, but in case one enables them.
|
|
|
|
return true
|
|
|
|
}
|
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")
|
|
|
|
}
|
2018-03-05 11:07:59 +00:00
|
|
|
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
2016-01-13 17:57:12 +00:00
|
|
|
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, "#") {
|
2017-06-17 10:23:52 +00:00
|
|
|
return osutil.IsExist(dev)
|
2016-01-25 18:08:17 +00:00
|
|
|
}
|
|
|
|
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)
|
2017-11-27 08:08:59 +00:00
|
|
|
case "syz_open_procfs":
|
|
|
|
return true
|
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":
|
2018-03-05 11:07:59 +00:00
|
|
|
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
|
|
|
return false
|
|
|
|
}
|
2017-06-17 10:23:52 +00:00
|
|
|
return osutil.IsExist("/dev/fuse")
|
2015-12-27 11:20:00 +00:00
|
|
|
case "syz_fuseblk_mount":
|
2018-03-05 11:07:59 +00:00
|
|
|
if syscall.Getuid() != 0 || sandbox == "setuid" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return osutil.IsExist("/dev/fuse")
|
sys, executor: extract tcp sequence numbers from /dev/net/tun
This commit adds a new pseudo syscall syz_extract_tcp_res, that reads
a packet from /dev/net/tun and extracts tcp sequence numbers to be used
in subsequent packets.
As a result this syzkaller program:
mmap(&(0x7f0000000000/0x10000)=nil, (0x10000), 0x3, 0x32, 0xffffffffffffffff, 0x0)
r0 = socket$inet_tcp(0x2, 0x1, 0x0)
bind$inet(r0, &(0x7f0000001000)={0x2, 0x0, @empty=0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x10)
listen(r0, 0x5)
syz_emit_ethernet(0x36, &(0x7f0000002000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="4c6112cc15d8", [], {{0x800, @ipv4={{0x5, 0x4, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x6, 0x0, @remote={0xac, 0x14, 0x0, 0xbb}, @local={0xac, 0x14, 0x0, 0xaa}, {[]}}, @tcp={{0x1, 0x0, 0x42424242, 0x42424242, 0x0, 0x0, 0x5, 0x2, 0x0, 0x0, 0x0, {[]}}, {""}}}}}})
syz_extract_tcp_res(&(0x7f0000003000)={<r1=>0x42424242, <r2=>0x42424242}, 0x1, 0x0)
syz_emit_ethernet(0x38, &(0x7f0000004000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @remote={[0xbb, 0xbb, 0xbb, 0xbb, 0xbb], 0x0}, [], {{0x800, @ipv4={{0x5, 0x4, 0x0, 0x0, 0x2a, 0x0, 0x0, 0x0, 0x6, 0x0, @remote={0xac, 0x14, 0x0, 0xbb}, @local={0xac, 0x14, 0x0, 0xaa}, {[]}}, @tcp={{0x1, 0x0, r2, r1, 0x0, 0x0, 0x5, 0x10, 0x0, 0x0, 0x0, {[]}}, {"0c10"}}}}}})
r3 = accept$inet(r0, &(0x7f0000005000)={0x0, 0x0, @multicast1=0x0, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, &(0x7f0000006000)=0x10)
established a TCP connection:
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:20000 0.0.0.0:* LISTEN 5477/a.out
tcp 2 0 172.20.0.170:20000 172.20.0.187:20001 ESTABLISHED 5477/a.out
Similar program for IPv6:
mmap(&(0x7f0000000000/0x10000)=nil, (0x10000), 0x3, 0x32, 0xffffffffffffffff, 0x0)
r0 = socket$inet6_tcp(0xa, 0x1, 0x0)
bind$inet6(r0, &(0x7f0000000000)={0xa, 0x1, 0x0, @empty={[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x0}, 0x1c)
listen(r0, 0x5)
syz_emit_ethernet(0x4a, &(0x7f0000001000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="de895db1468d", [], {{0x86dd, @ipv6={0x0, 0x6, "a228af", 0x14, 0x6, 0x0, @remote={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xbb}, @local={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xaa}, {[], @tcp={{0x0, 0x1, 0x42424242, 0x42424242, 0x0, 0x0, 0x5, 0x2, 0x0, 0x0, 0x0, {[]}}, {""}}}}}}})
syz_extract_tcp_res(&(0x7f0000002000)={<r1=>0x42424242, <r2=>0x42424242}, 0x1, 0x0)
syz_emit_ethernet(0x4a, &(0x7f0000003000)={@local={[0xaa, 0xaa, 0xaa, 0xaa, 0xaa], 0x0}, @random="de895db1468d", [], {{0x86dd, @ipv6={0x0, 0x6, "a228af", 0x14, 0x6, 0x0, @remote={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xbb}, @local={0xfe, 0x80, [0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0], 0x0, 0xaa}, {[], @tcp={{0x0, 0x1, r2, r1, 0x0, 0x0, 0x5, 0x10, 0x0, 0x0, 0x0, {[]}}, {""}}}}}}})
r3 = accept$inet6(r0, &(0x7f0000004000)={0x0, 0x0, 0x0, @empty={[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]}, 0x0}, &(0x7f0000005000)=0x1c)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp6 0 0 :::20001 :::* LISTEN 5527/a.out
tcp6 0 0 fe80::aa:20001 fe80::bb:20000 ESTABLISHED 5527/a.out
2017-05-16 14:14:58 +00:00
|
|
|
case "syz_emit_ethernet", "syz_extract_tcp_res":
|
2017-02-17 15:07:48 +00:00
|
|
|
fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 0)
|
|
|
|
if err == nil {
|
|
|
|
syscall.Close(fd)
|
|
|
|
}
|
2018-03-05 11:07:59 +00:00
|
|
|
return err == nil
|
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
|
|
|
}
|
2018-03-05 11:07:59 +00:00
|
|
|
case "syz_init_net_socket":
|
|
|
|
// Unfortunately this only works with sandbox none at the moment.
|
|
|
|
// The problem is that setns of a network namespace requires CAP_SYS_ADMIN
|
|
|
|
// in the target namespace, and we've lost all privs in the init namespace
|
|
|
|
// during creation of a user namespace.
|
|
|
|
if syscall.Getuid() != 0 || sandbox != "none" {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return isSupportedSocket(c)
|
2018-03-21 11:18:36 +00:00
|
|
|
case "syz_genetlink_get_family_id":
|
|
|
|
fd, _ := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_GENERIC)
|
|
|
|
if fd == -1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
syscall.Close(fd)
|
|
|
|
return true
|
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
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
func isSupportedSocket(c *prog.Syscall) bool {
|
|
|
|
af, ok := c.Args[0].(*prog.ConstType)
|
2015-12-28 09:45:30 +00:00
|
|
|
if !ok {
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
func isSupportedOpenAt(c *prog.Syscall) bool {
|
2017-01-05 14:13:12 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-09-05 11:31:14 +00:00
|
|
|
func extractStringConst(typ prog.Type) (string, bool) {
|
|
|
|
ptr, ok := typ.(*prog.PtrType)
|
2016-10-31 21:15:13 +00:00
|
|
|
if !ok {
|
|
|
|
panic("first open arg is not a pointer to string const")
|
|
|
|
}
|
2017-09-05 11:31:14 +00:00
|
|
|
str, ok := ptr.Type.(*prog.BufferType)
|
|
|
|
if !ok || str.Kind != prog.BufferString || len(str.Values) != 1 {
|
2016-10-31 21:15:13 +00:00
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
v := str.Values[0]
|
|
|
|
v = v[:len(v)-1] // string terminating \x00
|
|
|
|
return v, true
|
|
|
|
}
|
2017-10-16 10:18:50 +00:00
|
|
|
|
|
|
|
func EnableFaultInjection() error {
|
|
|
|
if err := osutil.WriteFile("/sys/kernel/debug/failslab/ignore-gfp-wait", []byte("N")); err != nil {
|
|
|
|
return fmt.Errorf("failed to write /sys/kernel/debug/failslab/ignore-gfp-wait: %v", err)
|
|
|
|
}
|
|
|
|
if err := osutil.WriteFile("/sys/kernel/debug/fail_futex/ignore-private", []byte("N")); err != nil {
|
|
|
|
return fmt.Errorf("failed to write /sys/kernel/debug/fail_futex/ignore-private: %v", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|