github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/sys/openbsd/init.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package openbsd
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  
    10  	"github.com/google/syzkaller/prog"
    11  	"github.com/google/syzkaller/sys/targets"
    12  )
    13  
    14  func InitTarget(target *prog.Target) {
    15  	arch := &arch{
    16  		unix:             targets.MakeUnixNeutralizer(target),
    17  		CLOCK_REALTIME:   target.GetConst("CLOCK_REALTIME"),
    18  		CTL_KERN:         target.GetConst("CTL_KERN"),
    19  		DIOCCLRSTATES:    target.GetConst("DIOCCLRSTATES"),
    20  		DIOCKILLSTATES:   target.GetConst("DIOCKILLSTATES"),
    21  		KERN_MAXCLUSTERS: target.GetConst("KERN_MAXCLUSTERS"),
    22  		KERN_MAXPROC:     target.GetConst("KERN_MAXPROC"),
    23  		KERN_MAXFILES:    target.GetConst("KERN_MAXFILES"),
    24  		KERN_MAXTHREAD:   target.GetConst("KERN_MAXTHREAD"),
    25  		KERN_WITNESS:     target.GetConst("KERN_WITNESS"),
    26  		S_IFCHR:          target.GetConst("S_IFCHR"),
    27  		S_IFMT:           target.GetConst("S_IFMT"),
    28  		MCL_FUTURE:       target.GetConst("MCL_FUTURE"),
    29  		RLIMIT_DATA:      target.GetConst("RLIMIT_DATA"),
    30  		RLIMIT_STACK:     target.GetConst("RLIMIT_STACK"),
    31  	}
    32  
    33  	target.MakeDataMmap = targets.MakePosixMmap(target, false, false)
    34  	target.Neutralize = arch.neutralize
    35  	target.AnnotateCall = arch.annotateCall
    36  }
    37  
    38  type arch struct {
    39  	unix             *targets.UnixNeutralizer
    40  	CLOCK_REALTIME   uint64
    41  	CTL_KERN         uint64
    42  	DIOCCLRSTATES    uint64
    43  	DIOCKILLSTATES   uint64
    44  	KERN_MAXCLUSTERS uint64
    45  	KERN_MAXPROC     uint64
    46  	KERN_MAXFILES    uint64
    47  	KERN_MAXTHREAD   uint64
    48  	KERN_WITNESS     uint64
    49  	S_IFCHR          uint64
    50  	S_IFMT           uint64
    51  	MCL_FUTURE       uint64
    52  	RLIMIT_DATA      uint64
    53  	RLIMIT_STACK     uint64
    54  }
    55  
    56  const (
    57  	mknodMode = 0
    58  	mknodDev  = 1
    59  
    60  	// openbsd:src/etc/etc.amd64/MAKEDEV
    61  	devFdMajor  = 22
    62  	devNullDevT = 0x0202
    63  
    64  	// Mask covering all valid rlimit resources.
    65  	rlimitMask = 0xf
    66  )
    67  
    68  // openbsd:src/sys/sys/types.h
    69  func devmajor(dev uint64) uint64 {
    70  	return (dev >> 8) & 0xff
    71  }
    72  
    73  // openbsd:src/sys/sys/types.h
    74  func devminor(dev uint64) uint64 {
    75  	return (dev & 0xff) | ((dev & 0xffff0000) >> 8)
    76  }
    77  
    78  func isExecutorFd(dev uint64) bool {
    79  	major := devmajor(dev)
    80  	minor := devminor(dev)
    81  
    82  	return major == devFdMajor && minor >= 200
    83  }
    84  
    85  func (arch *arch) neutralize(c *prog.Call, fixStructure bool) error {
    86  	argStart := 1
    87  	switch c.Meta.CallName {
    88  	case "chflagsat":
    89  		argStart = 2
    90  		fallthrough
    91  	case "chflags", "fchflags":
    92  		// Prevent changing mutability flags on files. This is
    93  		// especially problematic for file descriptors referring to
    94  		// tty/pty devices since it can cause the SSH connection to the
    95  		// VM to die.
    96  		flags := c.Args[argStart].(*prog.ConstArg)
    97  		badflags := [...]uint64{
    98  			0x00000002, // UF_IMMUTABLE
    99  			0x00000004, // UF_APPEND
   100  			0x00020000, // SF_IMMUTABLE
   101  			0x00040000, // SF_APPEND
   102  		}
   103  		for _, f := range badflags {
   104  			flags.Val &= ^f
   105  		}
   106  	case "clock_settime":
   107  		arch.neutralizeClockSettime(c)
   108  	case "ioctl":
   109  		// Performing the following ioctl commands on a /dev/pf file
   110  		// descriptor causes the ssh VM connection to die. For now, just
   111  		// rewire them to an invalid command.
   112  		request := c.Args[1].(*prog.ConstArg)
   113  		if request.Val == arch.DIOCCLRSTATES || request.Val == arch.DIOCKILLSTATES {
   114  			request.Val = 0
   115  		}
   116  	case "mknodat":
   117  		argStart = 2
   118  		fallthrough
   119  	case "mknod":
   120  		// Prevent vnodes of type VBAD from being created. Such vnodes will
   121  		// likely trigger assertion errors by the kernel.
   122  		mode := c.Args[argStart+mknodMode].(*prog.ConstArg)
   123  		if mode.Val&arch.S_IFMT == arch.S_IFMT {
   124  			mode.Val &^= arch.S_IFMT
   125  			mode.Val |= arch.S_IFCHR
   126  		}
   127  
   128  		// Prevent certain /dev/fd/X devices from getting created since
   129  		// they belong to the executor. It's especially dangerous to let
   130  		// the executor interact with kcov file descriptors since it can
   131  		// interfere with the coverage collection and cause corpus
   132  		// explosion.
   133  		// https://groups.google.com/d/msg/syzkaller/_IRWeAjVoy4/Akl2XMZTDAAJ
   134  		dev := c.Args[argStart+mknodDev].(*prog.ConstArg)
   135  		if isExecutorFd(dev.Val) {
   136  			dev.Val = devNullDevT
   137  		}
   138  
   139  		// Prevent /dev/sd0b (swap partition) and /dev/sd0c (raw disk)
   140  		// nodes from being created. Writing to such devices can corrupt
   141  		// the file system.
   142  		if devmajor(dev.Val) == 4 && (devminor(dev.Val) == 1 || devminor(dev.Val) == 2) {
   143  			dev.Val = devNullDevT
   144  		}
   145  	case "mlockall":
   146  		flags := c.Args[0].(*prog.ConstArg)
   147  		flags.Val &= ^arch.MCL_FUTURE
   148  	case "setrlimit":
   149  		arch.neutralizeRlimit(c)
   150  	case "sysctl":
   151  		arch.neutralizeSysctl(c)
   152  	default:
   153  		return arch.unix.Neutralize(c, fixStructure)
   154  	}
   155  	return nil
   156  }
   157  
   158  func (arch *arch) neutralizeClockSettime(c *prog.Call) {
   159  	switch v := c.Args[0].(type) {
   160  	case *prog.ConstArg:
   161  		// Do not fiddle with the wall clock, one of the causes of "no
   162  		// output from test machine" reports.
   163  		if v.Val == arch.CLOCK_REALTIME {
   164  			v.Val = ^uint64(0)
   165  		}
   166  	}
   167  }
   168  
   169  func (arch *arch) neutralizeRlimit(c *prog.Call) {
   170  	rlimitMin := uint64(0)
   171  	rlimitMax := uint64(math.MaxUint64)
   172  	resource := c.Args[0].(*prog.ConstArg).Val & rlimitMask
   173  	if resource == arch.RLIMIT_DATA {
   174  		// OpenBSD performs a strict validation of the RLIMIT_DATA soft
   175  		// limit during memory allocation. Lowering the same limit could
   176  		// cause syz-executor to run out of memory quickly. Therefore
   177  		// make sure to not go lower than the default soft limit for the
   178  		// staff group.
   179  		rlimitMin = 1536 * 1024 * 1024
   180  	} else if resource == arch.RLIMIT_STACK {
   181  		// Do not allow the stack to grow beyond the initial soft limit
   182  		// chosen by syz-executor. Otherwise, syz-executor will most
   183  		// likely not be able to perform any more heap allocations since
   184  		// they majority of memory is reserved for the stack.
   185  		rlimitMax = 1 * 1024 * 1024
   186  	} else {
   187  		return
   188  	}
   189  
   190  	ptr := c.Args[1].(*prog.PointerArg)
   191  	if ptr.Res == nil {
   192  		return
   193  	}
   194  
   195  	args := ptr.Res.(*prog.GroupArg).Inner
   196  	for _, arg := range args {
   197  		switch v := arg.(type) {
   198  		case *prog.ConstArg:
   199  			if v.Val < rlimitMin {
   200  				v.Val = rlimitMin
   201  			}
   202  			if v.Val > rlimitMax {
   203  				v.Val = rlimitMax
   204  			}
   205  		}
   206  	}
   207  }
   208  
   209  func (arch *arch) neutralizeSysctl(c *prog.Call) {
   210  	ptr := c.Args[0].(*prog.PointerArg)
   211  	if ptr.Res == nil {
   212  		return
   213  	}
   214  
   215  	var mib []*prog.ConstArg
   216  	for _, arg := range ptr.Res.(*prog.GroupArg).Inner {
   217  		switch v := arg.(type) {
   218  		case *prog.ConstArg:
   219  			mib = append(mib, v)
   220  		}
   221  	}
   222  
   223  	if !arch.neutralizeSysctlKern(mib) {
   224  		return
   225  	}
   226  
   227  	for _, m := range mib {
   228  		m.Val = 0
   229  	}
   230  	// Reflect changes in the namelen argument.
   231  	if len(c.Args) >= 1 {
   232  		switch v := c.Args[1].(type) {
   233  		case *prog.ConstArg:
   234  			v.Val = 0
   235  		}
   236  	}
   237  }
   238  
   239  func (arch *arch) neutralizeSysctlKern(mib []*prog.ConstArg) bool {
   240  	// Do not fiddle with root only knob kern.maxclusters, one of the causes
   241  	// of "no output from test machine" reports.
   242  	if len(mib) >= 2 &&
   243  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXCLUSTERS {
   244  		return true
   245  	}
   246  
   247  	// Do not fiddle with root only knob kern.maxproc, can cause the
   248  	// syz-execprog to run out of resources.
   249  	if len(mib) >= 2 &&
   250  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXPROC {
   251  		return true
   252  	}
   253  
   254  	// Do not fiddle with root only knob kern.maxfiles, can cause the
   255  	// syz-execprog to run out of resources.
   256  	if len(mib) >= 2 &&
   257  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXFILES {
   258  		return true
   259  	}
   260  
   261  	// Do not fiddle with root only knob kern.maxthread, can cause the
   262  	// syz-execprog process to panic.
   263  	if len(mib) >= 2 &&
   264  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXTHREAD {
   265  		return true
   266  	}
   267  
   268  	if len(mib) >= 2 &&
   269  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_WITNESS {
   270  		return true
   271  	}
   272  
   273  	return false
   274  }
   275  
   276  func (arch *arch) annotateCall(c prog.ExecCall) string {
   277  	devArg := 2
   278  	switch c.Meta.Name {
   279  	case "mknodat":
   280  		devArg = 3
   281  		fallthrough
   282  	case "mknod":
   283  		dev := c.Args[devArg].(prog.ExecArgConst).Value
   284  		return fmt.Sprintf("major = %v, minor = %v", devmajor(dev), devminor(dev))
   285  	}
   286  	return ""
   287  }