| // 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 openbsd |
| |
| import ( |
| "fmt" |
| "math" |
| |
| "github.com/google/syzkaller/prog" |
| "github.com/google/syzkaller/sys/targets" |
| ) |
| |
| func InitTarget(target *prog.Target) { |
| arch := &arch{ |
| unix: targets.MakeUnixSanitizer(target), |
| S_IFMT: target.GetConst("S_IFMT"), |
| S_IFCHR: target.GetConst("S_IFCHR"), |
| } |
| |
| target.MakeMmap = targets.MakePosixMmap(target) |
| target.SanitizeCall = arch.SanitizeCall |
| target.AnnotateCall = arch.annotateCall |
| } |
| |
| type arch struct { |
| unix *targets.UnixSanitizer |
| S_IFMT uint64 |
| S_IFCHR uint64 |
| } |
| |
| const ( |
| mknodMode = 0 |
| mknodDev = 1 |
| |
| // openbsd:src/etc/etc.amd64/MAKEDEV |
| devFdMajor = 22 |
| devNullDevT = 0x0202 |
| |
| // kCoverFd in executor/executor.cc |
| kcovFdMinorMin = 232 |
| // kOutPipeFd in executor/executor.cc |
| kcovFdMinorMax = 248 |
| |
| // MCL_FUTURE from openbsd:src/sys/sys/mman.h |
| mclFuture uint64 = 0x2 |
| |
| // RLIMIT_DATA from openbsd:src/sys/sys/resource.h |
| rlimitData = 2 |
| // RLIMIT_STACK from openbsd:src/sys/sys/resource.h |
| rlimitStack = 3 |
| // Mask covering all valid rlimit resources. |
| rlimitMask = 0xf |
| ) |
| |
| // openbsd:src/sys/sys/types.h |
| func devmajor(dev uint64) uint64 { |
| return (dev >> 8) & 0xff |
| } |
| |
| // openbsd:src/sys/sys/types.h |
| func devminor(dev uint64) uint64 { |
| return (dev & 0xff) | ((dev & 0xffff0000) >> 8) |
| } |
| |
| func isKcovFd(dev uint64) bool { |
| major := devmajor(dev) |
| minor := devminor(dev) |
| |
| return major == devFdMajor && minor >= kcovFdMinorMin && minor < kcovFdMinorMax |
| } |
| |
| func (arch *arch) SanitizeCall(c *prog.Call) { |
| argStart := 1 |
| switch c.Meta.CallName { |
| case "chflagsat": |
| argStart = 2 |
| fallthrough |
| case "chflags", "fchflags": |
| // Prevent changing mutability flags on files. This is |
| // especially problematic for file descriptors referring to |
| // tty/pty devices since it can cause the SSH connection to the |
| // VM to die. |
| flags := c.Args[argStart].(*prog.ConstArg) |
| badflags := [...]uint64{ |
| 0x00000002, // UF_IMMUTABLE |
| 0x00000004, // UF_APPEND |
| 0x00020000, // SF_IMMUTABLE |
| 0x00040000, // SF_APPEND |
| } |
| for _, f := range badflags { |
| flags.Val &= ^f |
| } |
| case "mknodat": |
| argStart = 2 |
| fallthrough |
| case "mknod": |
| // Prevent vnodes of type VBAD from being created. Such vnodes will |
| // likely trigger assertion errors by the kernel. |
| mode := c.Args[argStart+mknodMode].(*prog.ConstArg) |
| if mode.Val&arch.S_IFMT == arch.S_IFMT { |
| mode.Val &^= arch.S_IFMT |
| mode.Val |= arch.S_IFCHR |
| } |
| |
| // Prevent /dev/fd/X devices from getting created where X maps |
| // to an open kcov fd. They interfere with kcov data collection |
| // and cause corpus explosion. |
| // https://groups.google.com/d/msg/syzkaller/_IRWeAjVoy4/Akl2XMZTDAAJ |
| dev := c.Args[argStart+mknodDev].(*prog.ConstArg) |
| if isKcovFd(dev.Val) { |
| dev.Val = devNullDevT |
| } |
| |
| // Prevent /dev/sd0b (swap partition) and /dev/sd0c (raw disk) |
| // nodes from being created. Writing to such devices can corrupt |
| // the file system. |
| if devmajor(dev.Val) == 4 && (devminor(dev.Val) == 1 || devminor(dev.Val) == 2) { |
| dev.Val = devNullDevT |
| } |
| case "mlockall": |
| flags := c.Args[0].(*prog.ConstArg) |
| flags.Val &= ^mclFuture |
| case "setrlimit": |
| var rlimitMin uint64 |
| var rlimitMax uint64 = math.MaxUint64 |
| resource := c.Args[0].(*prog.ConstArg).Val & rlimitMask |
| if resource == rlimitData { |
| // OpenBSD performs a strict validation of the |
| // RLIMIT_DATA soft limit during memory allocation. |
| // Lowering the same limit could cause syz-executor to |
| // run out of memory quickly. Therefore make sure to not |
| // go lower than the default soft limit for the staff |
| // group. |
| rlimitMin = 1536 * 1024 * 1024 |
| } else if resource == rlimitStack { |
| // Do not allow the stack to grow beyond the initial |
| // soft limit chosen by syz-executor. Otherwise, |
| // syz-executor will most likely not be able to perform |
| // any more heap allocations since they majority of |
| // memory is reserved for the stack. |
| rlimitMax = 1 * 1024 * 1024 |
| } else { |
| break |
| } |
| ptr := c.Args[1].(*prog.PointerArg) |
| if ptr.Res != nil { |
| args := ptr.Res.(*prog.GroupArg).Inner |
| for _, arg := range args { |
| switch v := arg.(type) { |
| case *prog.ConstArg: |
| if v.Val < rlimitMin { |
| v.Val = rlimitMin |
| } |
| if v.Val > rlimitMax { |
| v.Val = rlimitMax |
| } |
| } |
| } |
| } |
| default: |
| arch.unix.SanitizeCall(c) |
| } |
| } |
| |
| func (arch *arch) annotateCall(c prog.ExecCall) string { |
| devArg := 2 |
| switch c.Meta.Name { |
| case "mknodat": |
| devArg = 3 |
| fallthrough |
| case "mknod": |
| dev := c.Args[devArg].(prog.ExecArgConst).Value |
| return fmt.Sprintf("major = %v, minor = %v", devmajor(dev), devminor(dev)) |
| } |
| return "" |
| } |