aboutsummaryrefslogtreecommitdiffstats
path: root/sys/linux/init.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2017-09-13 15:34:21 +0200
committerDmitry Vyukov <dvyukov@google.com>2017-09-15 16:02:37 +0200
commit18e96021edb8f4de80626d7f50240964e37d570e (patch)
tree920bc3f7d064868bc4bfb21c8e6663bff1797c0d /sys/linux/init.go
parent4a7f7fab1ecf47d82fc2b403b57d3cf9b5836398 (diff)
sys: move linux descriptions to sys/linux
Diffstat (limited to 'sys/linux/init.go')
-rw-r--r--sys/linux/init.go272
1 files changed, 272 insertions, 0 deletions
diff --git a/sys/linux/init.go b/sys/linux/init.go
new file mode 100644
index 000000000..f4974f51b
--- /dev/null
+++ b/sys/linux/init.go
@@ -0,0 +1,272 @@
+// 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 sys
+
+import (
+ "runtime"
+
+ "github.com/google/syzkaller/prog"
+)
+
+func init() {
+ lazyInit()
+ target := &prog.Target{
+ OS: runtime.GOOS,
+ Arch: runtime.GOARCH,
+ PtrSize: ptrSize,
+ PageSize: pageSize,
+ DataOffset: dataOffset,
+ Syscalls: syscalls,
+ Resources: resources,
+ MakeMmap: makeMmap,
+ AnalyzeMmap: analyzeMmap,
+ SanitizeCall: sanitizeCall,
+ SpecialStructs: map[string]func(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (prog.Arg, []*prog.Call){
+ "timespec": generateTimespec,
+ "timeval": generateTimespec,
+ },
+ StringDictionary: stringDictionary,
+ }
+ prog.RegisterTarget(target)
+ prog.SetDefaultTarget(runtime.GOOS, runtime.GOARCH)
+}
+
+const (
+ pageSize = 4 << 10
+ dataOffset = 512 << 20
+ invalidFD = ^uint64(0)
+)
+
+var (
+ mmapSyscall *prog.Syscall
+ clockGettimeSyscall *prog.Syscall
+
+ 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"}
+)
+
+// createMmapCall creates a "normal" mmap call that maps [start, start+npages) page range.
+func makeMmap(start, npages uint64) *prog.Call {
+ return &prog.Call{
+ Meta: mmapSyscall,
+ Args: []prog.Arg{
+ prog.MakePointerArg(mmapSyscall.Args[0], start, 0, npages, nil),
+ prog.MakeConstArg(mmapSyscall.Args[1], npages*pageSize),
+ prog.MakeConstArg(mmapSyscall.Args[2], PROT_READ|PROT_WRITE),
+ prog.MakeConstArg(mmapSyscall.Args[3], MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED),
+ prog.MakeResultArg(mmapSyscall.Args[4], nil, invalidFD),
+ prog.MakeConstArg(mmapSyscall.Args[5], 0),
+ },
+ Ret: prog.MakeReturnArg(mmapSyscall.Ret),
+ }
+}
+
+func analyzeMmap(c *prog.Call) (start, npages uint64, mapped bool) {
+ switch c.Meta.Name {
+ case "mmap":
+ // Filter out only very wrong arguments.
+ npages = c.Args[1].(*prog.ConstArg).Val / pageSize
+ if npages == 0 {
+ return
+ }
+ flags := c.Args[3].(*prog.ConstArg).Val
+ fd := c.Args[4].(*prog.ResultArg).Val
+ if flags&MAP_ANONYMOUS == 0 && fd == invalidFD {
+ return
+ }
+ start = c.Args[0].(*prog.PointerArg).PageIndex
+ mapped = true
+ return
+ case "munmap":
+ start = c.Args[0].(*prog.PointerArg).PageIndex
+ npages = c.Args[1].(*prog.ConstArg).Val / pageSize
+ mapped = false
+ return
+ case "mremap":
+ start = c.Args[4].(*prog.PointerArg).PageIndex
+ npages = c.Args[2].(*prog.ConstArg).Val / pageSize
+ mapped = true
+ return
+ default:
+ return
+ }
+}
+
+func sanitizeCall(c *prog.Call) {
+ switch c.Meta.CallName {
+ case "mmap":
+ // Add MAP_FIXED flag, otherwise it produces non-deterministic results.
+ c.Args[3].(*prog.ConstArg).Val |= MAP_FIXED
+ case "mremap":
+ // Add MREMAP_FIXED flag, otherwise it produces non-deterministic results.
+ flags := c.Args[3].(*prog.ConstArg)
+ if flags.Val&MREMAP_MAYMOVE != 0 {
+ flags.Val |= MREMAP_FIXED
+ }
+ case "mknod", "mknodat":
+ pos := 1
+ if c.Meta.CallName == "mknodat" {
+ pos = 2
+ }
+ mode := c.Args[pos].(*prog.ConstArg)
+ dev := c.Args[pos+1].(*prog.ConstArg)
+ // Char and block devices read/write io ports, kernel memory and do other nasty things.
+ // TODO: not required if executor drops privileges.
+ switch mode.Val & (S_IFREG | S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK) {
+ case S_IFREG, S_IFIFO, S_IFSOCK:
+ case S_IFBLK:
+ if dev.Val>>8 == 7 {
+ break // loop
+ }
+ mode.Val &^= S_IFBLK
+ mode.Val |= S_IFREG
+ case S_IFCHR:
+ mode.Val &^= S_IFCHR
+ mode.Val |= S_IFREG
+ }
+ case "syslog":
+ cmd := c.Args[0].(*prog.ConstArg)
+ // These disable console output, but we need it.
+ if cmd.Val == SYSLOG_ACTION_CONSOLE_OFF || cmd.Val == SYSLOG_ACTION_CONSOLE_ON {
+ cmd.Val = SYSLOG_ACTION_SIZE_UNREAD
+ }
+ case "ioctl":
+ 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.
+ if uint32(cmd.Val) == FIFREEZE {
+ cmd.Val = FITHAW
+ }
+ case "ptrace":
+ req := c.Args[0].(*prog.ConstArg)
+ // PTRACE_TRACEME leads to unkillable processes, see:
+ // https://groups.google.com/forum/#!topic/syzkaller/uGzwvhlCXAw
+ if req.Val == PTRACE_TRACEME {
+ req.Val = ^uint64(0)
+ }
+ case "exit", "exit_group":
+ code := c.Args[0].(*prog.ConstArg)
+ // These codes are reserved by executor.
+ if code.Val%128 == 67 || code.Val%128 == 68 {
+ code.Val = 1
+ }
+ }
+}
+
+func generateTimespec(g *prog.Gen, typ *prog.StructType, old *prog.GroupArg) (arg prog.Arg, calls []*prog.Call) {
+ // 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 := 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], CLOCK_REALTIME),
+ tpaddr,
+ },
+ Ret: prog.MakeReturnArg(meta.Ret),
+ }
+ calls = append(calls, gettime)
+ sec := prog.MakeResultArg(typ.Fields[0], tp.(*prog.GroupArg).Inner[0], 0)
+ nsec := prog.MakeResultArg(typ.Fields[1], tp.(*prog.GroupArg).Inner[1], 0)
+ msec := uint64(10)
+ if g.NOutOf(1, 2) {
+ msec = 30
+ }
+ if usec {
+ nsec.(*prog.ResultArg).OpDiv = 1e3
+ nsec.(*prog.ResultArg).OpAdd = msec * 1e3
+ } else {
+ nsec.(*prog.ResultArg).OpAdd = msec * 1e6
+ }
+ arg = prog.MakeGroupArg(typ, []prog.Arg{sec, nsec})
+ }
+ return
+}
+
+func lazyInit() {
+ resourceMap := make(map[string]*prog.ResourceDesc)
+ for _, res := range resources {
+ resourceMap[res.Name] = res
+ }
+
+ keyedStructs := make(map[prog.StructKey]*prog.StructDesc)
+ for _, desc := range structDescs {
+ keyedStructs[desc.Key] = desc.Desc
+ }
+ structDescs = nil
+
+ for _, c := range syscalls {
+ prog.ForeachType(c, func(t0 prog.Type) {
+ switch t := t0.(type) {
+ case *prog.ResourceType:
+ t.Desc = resourceMap[t.TypeName]
+ if t.Desc == nil {
+ panic("no resource desc")
+ }
+ case *prog.StructType:
+ t.StructDesc = keyedStructs[t.Key]
+ if t.StructDesc == nil {
+ panic("no struct desc")
+ }
+ case *prog.UnionType:
+ t.StructDesc = keyedStructs[t.Key]
+ if t.StructDesc == nil {
+ panic("no union desc")
+ }
+ }
+ })
+ switch c.Name {
+ case "mmap":
+ mmapSyscall = c
+ case "clock_gettime":
+ clockGettimeSyscall = c
+ }
+ }
+}