blob: e694cce096b01cac8103d9380479134f66abe0ff [file] [log] [blame]
// Copyright 2017 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 linux
import (
"bytes"
"runtime"
"github.com/google/syzkaller/prog"
"github.com/google/syzkaller/sys/targets"
)
func InitTarget(target *prog.Target) {
arch := &arch{
unix: targets.MakeUnixSanitizer(target),
clockGettimeSyscall: target.SyscallMap["clock_gettime"],
MREMAP_MAYMOVE: target.GetConst("MREMAP_MAYMOVE"),
MREMAP_FIXED: target.GetConst("MREMAP_FIXED"),
SYSLOG_ACTION_CONSOLE_OFF: target.GetConst("SYSLOG_ACTION_CONSOLE_OFF"),
SYSLOG_ACTION_CONSOLE_ON: target.GetConst("SYSLOG_ACTION_CONSOLE_ON"),
SYSLOG_ACTION_CONSOLE_LEVEL: target.GetConst("SYSLOG_ACTION_CONSOLE_LEVEL"),
SYSLOG_ACTION_CLEAR: target.GetConst("SYSLOG_ACTION_CLEAR"),
SYSLOG_ACTION_SIZE_UNREAD: target.GetConst("SYSLOG_ACTION_SIZE_UNREAD"),
FIFREEZE: target.GetConst("FIFREEZE"),
FITHAW: target.GetConst("FITHAW"),
SNAPSHOT_FREEZE: target.GetConst("SNAPSHOT_FREEZE"),
SNAPSHOT_UNFREEZE: target.GetConst("SNAPSHOT_UNFREEZE"),
EXT4_IOC_SHUTDOWN: target.GetConst("EXT4_IOC_SHUTDOWN"),
EXT4_IOC_RESIZE_FS: target.GetConst("EXT4_IOC_RESIZE_FS"),
EXT4_IOC_MIGRATE: target.GetConst("EXT4_IOC_MIGRATE"),
FAN_OPEN_PERM: target.GetConst("FAN_OPEN_PERM"),
FAN_ACCESS_PERM: target.GetConst("FAN_ACCESS_PERM"),
FAN_OPEN_EXEC_PERM: target.GetConst("FAN_OPEN_EXEC_PERM"),
PTRACE_TRACEME: target.GetConst("PTRACE_TRACEME"),
CLOCK_REALTIME: target.GetConst("CLOCK_REALTIME"),
AF_NFC: target.GetConst("AF_NFC"),
AF_LLC: target.GetConst("AF_LLC"),
AF_BLUETOOTH: target.GetConst("AF_BLUETOOTH"),
AF_X25: target.GetConst("AF_X25"),
AF_AX25: target.GetConst("AF_AX25"),
AF_NETROM: target.GetConst("AF_NETROM"),
AF_ROSE: target.GetConst("AF_ROSE"),
// These are not present on all arches.
ARCH_SET_FS: target.ConstMap["ARCH_SET_FS"],
ARCH_SET_GS: target.ConstMap["ARCH_SET_GS"],
}
target.MakeMmap = targets.MakePosixMmap(target)
target.SanitizeCall = arch.sanitizeCall
target.SpecialTypes = map[string]func(g *prog.Gen, typ prog.Type, old prog.Arg) (
prog.Arg, []*prog.Call){
"timespec": arch.generateTimespec,
"timeval": arch.generateTimespec,
"sockaddr_alg": arch.generateSockaddrAlg,
"alg_name": arch.generateAlgName,
"alg_aead_name": arch.generateAlgAeadName,
"alg_hash_name": arch.generateAlgHashName,
"alg_blkcipher_name": arch.generateAlgBlkcipherhName,
"ipt_replace": arch.generateIptables,
"ip6t_replace": arch.generateIptables,
"arpt_replace": arch.generateArptables,
"ebt_replace": arch.generateEbtables,
"usb_device_descriptor": arch.generateUsbDeviceDescriptor,
}
// TODO(dvyukov): get rid of this, this must be in descriptions.
target.StringDictionary = []string{
"user", "keyring", "trusted", "system", "security", "selinux",
"posix_acl_access", "mime_type", "md5sum", "nodev", "self",
"bdev", "proc", "cgroup", "cpuset",
"lo", "eth0", "eth1", "em0", "em1", "wlan0", "wlan1", "ppp0", "ppp1",
"vboxnet0", "vboxnet1", "vmnet0", "vmnet1", "GPL",
}
switch target.Arch {
case "amd64":
target.SpecialPointers = []uint64{
0xffffffff81000000, // kernel text
}
case "386":
case "arm64":
case "arm":
case "ppc64le":
default:
panic("unknown arch")
}
if target.Arch == runtime.GOARCH {
KCOV_INIT_TRACE = uintptr(target.GetConst("KCOV_INIT_TRACE"))
KCOV_ENABLE = uintptr(target.GetConst("KCOV_ENABLE"))
KCOV_REMOTE_ENABLE = uintptr(target.GetConst("KCOV_REMOTE_ENABLE"))
KCOV_DISABLE = uintptr(target.GetConst("KCOV_DISABLE"))
KCOV_TRACE_PC = uintptr(target.GetConst("KCOV_TRACE_PC"))
KCOV_TRACE_CMP = uintptr(target.GetConst("KCOV_TRACE_CMP"))
}
}
var (
// This should not be here, but for now we expose this for syz-fuzzer.
KCOV_INIT_TRACE uintptr
KCOV_ENABLE uintptr
KCOV_REMOTE_ENABLE uintptr
KCOV_DISABLE uintptr
KCOV_TRACE_PC uintptr
KCOV_TRACE_CMP uintptr
)
type arch struct {
unix *targets.UnixSanitizer
clockGettimeSyscall *prog.Syscall
MREMAP_MAYMOVE uint64
MREMAP_FIXED uint64
SYSLOG_ACTION_CONSOLE_OFF uint64
SYSLOG_ACTION_CONSOLE_ON uint64
SYSLOG_ACTION_CONSOLE_LEVEL uint64
SYSLOG_ACTION_CLEAR uint64
SYSLOG_ACTION_SIZE_UNREAD uint64
FIFREEZE uint64
FITHAW uint64
SNAPSHOT_FREEZE uint64
SNAPSHOT_UNFREEZE uint64
EXT4_IOC_SHUTDOWN uint64
EXT4_IOC_RESIZE_FS uint64
EXT4_IOC_MIGRATE uint64
FAN_OPEN_PERM uint64
FAN_ACCESS_PERM uint64
FAN_OPEN_EXEC_PERM uint64
PTRACE_TRACEME uint64
CLOCK_REALTIME uint64
ARCH_SET_FS uint64
ARCH_SET_GS uint64
AF_NFC uint64
AF_LLC uint64
AF_BLUETOOTH uint64
AF_X25 uint64
AF_AX25 uint64
AF_NETROM uint64
AF_ROSE uint64
}
func (arch *arch) sanitizeCall(c *prog.Call) {
arch.unix.SanitizeCall(c)
switch c.Meta.CallName {
case "mremap":
// Add MREMAP_FIXED flag, otherwise it produces non-deterministic results.
flags := c.Args[3].(*prog.ConstArg)
if flags.Val&arch.MREMAP_MAYMOVE != 0 {
flags.Val |= arch.MREMAP_FIXED
}
case "syslog":
cmd := c.Args[0].(*prog.ConstArg)
cmd.Val = uint64(uint32(cmd.Val))
// These disable console output, but we need it.
if cmd.Val == arch.SYSLOG_ACTION_CONSOLE_OFF ||
cmd.Val == arch.SYSLOG_ACTION_CONSOLE_ON ||
cmd.Val == arch.SYSLOG_ACTION_CONSOLE_LEVEL ||
cmd.Val == arch.SYSLOG_ACTION_CLEAR {
cmd.Val = arch.SYSLOG_ACTION_SIZE_UNREAD
}
case "ioctl":
arch.sanitizeIoctl(c)
case "fanotify_mark":
// FAN_*_PERM require the program to reply to open requests.
// If that does not happen, the program will hang in an unkillable state forever.
// See the following bug for details:
// https://groups.google.com/d/msg/syzkaller-bugs/pD-vbqJu6U0/kGH30p3lBgAJ
mask := c.Args[2].(*prog.ConstArg)
mask.Val &^= arch.FAN_OPEN_PERM | arch.FAN_ACCESS_PERM | arch.FAN_OPEN_EXEC_PERM
case "ptrace":
req := c.Args[0].(*prog.ConstArg)
// PTRACE_TRACEME leads to unkillable processes, see:
// https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw
if uint64(uint32(req.Val)) == arch.PTRACE_TRACEME {
req.Val = ^uint64(0)
}
case "arch_prctl":
// fs holds address of tls, if a program messes it at least signal
// handling will break. This also allows a program to do writes
// at arbitrary addresses, which usually leads to machine outbreak.
cmd := c.Args[0].(*prog.ConstArg)
if uint64(uint32(cmd.Val)) == arch.ARCH_SET_FS {
cmd.Val = arch.ARCH_SET_GS
}
case "init_module":
// Kernel tries to vmalloc whatever we pass as size and it's not accounted against memcg.
// As the result it can lead to massive OOM kills of everything running on the machine.
// Strictly saying, the same applies to finit_module with a sparse file too,
// but there is no simple way to handle that.
sz := c.Args[1].(*prog.ConstArg)
sz.Val %= 1 << 20
case "syz_init_net_socket":
// Don't let it mess with arbitrary sockets in init namespace.
family := c.Args[0].(*prog.ConstArg)
switch uint64(uint32(family.Val)) {
case arch.AF_NFC, arch.AF_LLC, arch.AF_BLUETOOTH,
arch.AF_X25, arch.AF_AX25, arch.AF_NETROM, arch.AF_ROSE:
default:
family.Val = ^uint64(0)
}
case "syz_open_procfs":
arch.sanitizeSyzOpenProcfs(c)
}
switch c.Meta.Name {
case "setsockopt$EBT_SO_SET_ENTRIES":
arch.sanitizeEbtables(c)
}
}
func (arch *arch) sanitizeIoctl(c *prog.Call) {
cmd := c.Args[1].(*prog.ConstArg)
// Freeze kills machine. Though, it is an interesting functions,
// so we need to test it somehow.
// TODO: not required if executor drops privileges.
// Fortunately, the value does not conflict with any other ioctl commands for now.
if uint64(uint32(cmd.Val)) == arch.FIFREEZE {
cmd.Val = arch.FITHAW
}
// SNAPSHOT_FREEZE freezes all processes and leaves the machine dead.
if uint64(uint32(cmd.Val)) == arch.SNAPSHOT_FREEZE {
cmd.Val = arch.SNAPSHOT_UNFREEZE
}
// EXT4_IOC_SHUTDOWN on root fs effectively brings the machine down in weird ways.
// Fortunately, the value does not conflict with any other ioctl commands for now.
if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_SHUTDOWN {
cmd.Val = arch.EXT4_IOC_MIGRATE
}
// EXT4_IOC_RESIZE_FS on root fs can shrink it to 0 (or whatever is the minimum size)
// and then creation of new temp dirs for tests will fail.
// TODO: not necessary for sandbox=namespace as it tests in a tmpfs
// and/or if we mount tmpfs for sandbox=none (#971).
if uint64(uint32(cmd.Val)) == arch.EXT4_IOC_RESIZE_FS {
cmd.Val = arch.EXT4_IOC_MIGRATE
}
}
func (arch *arch) sanitizeSyzOpenProcfs(c *prog.Call) {
// If fuzzer manages to open /proc/self/exe, it does some nasty things with it:
// - mark as non-executable
// - set some extended acl's that prevent execution
// - mark as immutable, etc
// As the result we fail to start executor again and recreate the VM.
// Don't let it open /proc/self/exe.
ptr := c.Args[1].(*prog.PointerArg)
if ptr.Res != nil {
arg := ptr.Res.(*prog.DataArg)
file := arg.Data()
for len(file) != 0 && (file[0] == '/' || file[0] == '.') {
file = file[1:]
}
if bytes.HasPrefix(file, []byte("exe")) {
arg.SetData([]byte("net\x00"))
}
}
}
func (arch *arch) generateTimespec(g *prog.Gen, typ0 prog.Type, old prog.Arg) (arg prog.Arg, calls []*prog.Call) {
typ := typ0.(*prog.StructType)
// We need to generate timespec/timeval that are either
// (1) definitely in the past, or
// (2) definitely in unreachable fututre, or
// (3) few ms ahead of now.
// Note: timespec/timeval can be absolute or relative to now.
// Note: executor has blocking syscall timeout of 20ms,
// so we generate both 10ms and 30ms.
usec := typ.Name() == "timeval"
switch {
case g.NOutOf(1, 4):
// Now for relative, past for absolute.
arg = prog.MakeGroupArg(typ, []prog.Arg{
prog.MakeResultArg(typ.Fields[0], nil, 0),
prog.MakeResultArg(typ.Fields[1], nil, 0),
})
case g.NOutOf(1, 3):
// Few ms ahead for relative, past for absolute
nsec := uint64(10 * 1e6)
if g.NOutOf(1, 2) {
nsec = 30 * 1e6
}
if usec {
nsec /= 1e3
}
arg = prog.MakeGroupArg(typ, []prog.Arg{
prog.MakeResultArg(typ.Fields[0], nil, 0),
prog.MakeResultArg(typ.Fields[1], nil, nsec),
})
case g.NOutOf(1, 2):
// Unreachable fututre for both relative and absolute
arg = prog.MakeGroupArg(typ, []prog.Arg{
prog.MakeResultArg(typ.Fields[0], nil, 2e9),
prog.MakeResultArg(typ.Fields[1], nil, 0),
})
default:
// Few ms ahead for absolute.
meta := arch.clockGettimeSyscall
ptrArgType := meta.Args[1].(*prog.PtrType)
argType := ptrArgType.Type.(*prog.StructType)
tp := prog.MakeGroupArg(argType, []prog.Arg{
prog.MakeResultArg(argType.Fields[0], nil, 0),
prog.MakeResultArg(argType.Fields[1], nil, 0),
})
var tpaddr prog.Arg
tpaddr, calls = g.Alloc(ptrArgType, tp)
gettime := &prog.Call{
Meta: meta,
Args: []prog.Arg{
prog.MakeConstArg(meta.Args[0], arch.CLOCK_REALTIME),
tpaddr,
},
Ret: prog.MakeReturnArg(meta.Ret),
}
calls = append(calls, gettime)
sec := prog.MakeResultArg(typ.Fields[0], tp.Inner[0].(*prog.ResultArg), 0)
nsec := prog.MakeResultArg(typ.Fields[1], tp.Inner[1].(*prog.ResultArg), 0)
msec := uint64(10)
if g.NOutOf(1, 2) {
msec = 30
}
if usec {
nsec.OpDiv = 1e3
nsec.OpAdd = msec * 1e3
} else {
nsec.OpAdd = msec * 1e6
}
arg = prog.MakeGroupArg(typ, []prog.Arg{sec, nsec})
}
return
}