mirror of
https://github.com/reactos/syzkaller.git
synced 2024-11-23 11:29:46 +00:00
872ebc286c
Yet another root only knob that can cause the syz-execprog process to panic[1]. Greg is currently working on sanitizing sysctl integer knobs in the OpenBSD kernel. This will improve the situation but preventing fiddling with this knob is a good call anyway. While here, restructure the code a bit making it easier to add sysctl neutralizers. [1] https://syzkaller.appspot.com/bug?id=40bcbbeb244998dfc4eeec90b8f8bce372882211
265 lines
7.0 KiB
Go
265 lines
7.0 KiB
Go
// 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.MakeUnixNeutralizer(target),
|
|
CLOCK_REALTIME: target.GetConst("CLOCK_REALTIME"),
|
|
CTL_KERN: target.GetConst("CTL_KERN"),
|
|
DIOCCLRSTATES: target.GetConst("DIOCCLRSTATES"),
|
|
DIOCKILLSTATES: target.GetConst("DIOCKILLSTATES"),
|
|
KERN_MAXCLUSTERS: target.GetConst("KERN_MAXCLUSTERS"),
|
|
KERN_MAXTHREAD: target.GetConst("KERN_MAXTHREAD"),
|
|
S_IFCHR: target.GetConst("S_IFCHR"),
|
|
S_IFMT: target.GetConst("S_IFMT"),
|
|
MCL_FUTURE: target.GetConst("MCL_FUTURE"),
|
|
RLIMIT_DATA: target.GetConst("RLIMIT_DATA"),
|
|
RLIMIT_STACK: target.GetConst("RLIMIT_STACK"),
|
|
}
|
|
|
|
target.MakeDataMmap = targets.MakePosixMmap(target, false, false)
|
|
target.Neutralize = arch.neutralize
|
|
target.AnnotateCall = arch.annotateCall
|
|
}
|
|
|
|
type arch struct {
|
|
unix *targets.UnixNeutralizer
|
|
CLOCK_REALTIME uint64
|
|
CTL_KERN uint64
|
|
DIOCCLRSTATES uint64
|
|
DIOCKILLSTATES uint64
|
|
KERN_MAXCLUSTERS uint64
|
|
KERN_MAXTHREAD uint64
|
|
S_IFCHR uint64
|
|
S_IFMT uint64
|
|
MCL_FUTURE uint64
|
|
RLIMIT_DATA uint64
|
|
RLIMIT_STACK 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
|
|
|
|
// 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) neutralize(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 "clock_settime":
|
|
arch.neutralizeClockSettime(c)
|
|
case "ioctl":
|
|
// Performing the following ioctl commands on a /dev/pf file
|
|
// descriptor causes the ssh VM connection to die. For now, just
|
|
// rewire them to an invalid command.
|
|
request := c.Args[1].(*prog.ConstArg)
|
|
if request.Val == arch.DIOCCLRSTATES || request.Val == arch.DIOCKILLSTATES {
|
|
request.Val = 0
|
|
}
|
|
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 &= ^arch.MCL_FUTURE
|
|
case "setrlimit":
|
|
arch.neutralizeRlimit(c)
|
|
case "sysctl":
|
|
arch.neutralizeSysctl(c)
|
|
default:
|
|
arch.unix.Neutralize(c)
|
|
}
|
|
}
|
|
|
|
func (arch *arch) neutralizeClockSettime(c *prog.Call) {
|
|
switch v := c.Args[0].(type) {
|
|
case *prog.ConstArg:
|
|
// Do not fiddle with the wall clock, one of the causes of "no
|
|
// output from test machine" reports.
|
|
if v.Val == arch.CLOCK_REALTIME {
|
|
v.Val = ^uint64(0)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (arch *arch) neutralizeRlimit(c *prog.Call) {
|
|
rlimitMin := uint64(0)
|
|
rlimitMax := uint64(math.MaxUint64)
|
|
resource := c.Args[0].(*prog.ConstArg).Val & rlimitMask
|
|
if resource == arch.RLIMIT_DATA {
|
|
// 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 == arch.RLIMIT_STACK {
|
|
// 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 {
|
|
return
|
|
}
|
|
|
|
ptr := c.Args[1].(*prog.PointerArg)
|
|
if ptr.Res == nil {
|
|
return
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (arch *arch) neutralizeSysctl(c *prog.Call) {
|
|
ptr := c.Args[0].(*prog.PointerArg)
|
|
if ptr.Res == nil {
|
|
return
|
|
}
|
|
|
|
var mib []*prog.ConstArg
|
|
for _, arg := range ptr.Res.(*prog.GroupArg).Inner {
|
|
switch v := arg.(type) {
|
|
case *prog.ConstArg:
|
|
mib = append(mib, v)
|
|
}
|
|
}
|
|
|
|
if !arch.neutralizeSysctlKern(mib) {
|
|
return
|
|
}
|
|
|
|
for _, m := range mib {
|
|
m.Val = 0
|
|
}
|
|
// Reflect changes in the namelen argument.
|
|
if len(c.Args) >= 1 {
|
|
switch v := c.Args[1].(type) {
|
|
case *prog.ConstArg:
|
|
v.Val = 0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (arch *arch) neutralizeSysctlKern(mib []*prog.ConstArg) bool {
|
|
// Do not fiddle with root only knob kern.maxclusters, one of the causes
|
|
// of "no output from test machine" reports.
|
|
if len(mib) >= 2 &&
|
|
mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXCLUSTERS {
|
|
return true
|
|
}
|
|
|
|
// Do not fiddle with root only knob kern.maxthread, can cause the
|
|
// syz-execprog process to panic.
|
|
if len(mib) >= 2 &&
|
|
mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXTHREAD {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
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 ""
|
|
}
|