github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/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  	switch resource {
   174  	case arch.RLIMIT_DATA:
   175  		// OpenBSD performs a strict validation of the RLIMIT_DATA soft
   176  		// limit during memory allocation. Lowering the same limit could
   177  		// cause syz-executor to run out of memory quickly. Therefore
   178  		// make sure to not go lower than the default soft limit for the
   179  		// staff group.
   180  		rlimitMin = 1536 * 1024 * 1024
   181  	case arch.RLIMIT_STACK:
   182  		// Do not allow the stack to grow beyond the initial soft limit
   183  		// chosen by syz-executor. Otherwise, syz-executor will most
   184  		// likely not be able to perform any more heap allocations since
   185  		// they majority of memory is reserved for the stack.
   186  		rlimitMax = 1 * 1024 * 1024
   187  	default:
   188  		return
   189  	}
   190  
   191  	ptr := c.Args[1].(*prog.PointerArg)
   192  	if ptr.Res == nil {
   193  		return
   194  	}
   195  
   196  	args := ptr.Res.(*prog.GroupArg).Inner
   197  	for _, arg := range args {
   198  		switch v := arg.(type) {
   199  		case *prog.ConstArg:
   200  			v.Val = max(v.Val, rlimitMin)
   201  			v.Val = min(v.Val, rlimitMax)
   202  		}
   203  	}
   204  }
   205  
   206  func (arch *arch) neutralizeSysctl(c *prog.Call) {
   207  	ptr := c.Args[0].(*prog.PointerArg)
   208  	if ptr.Res == nil {
   209  		return
   210  	}
   211  
   212  	var mib []*prog.ConstArg
   213  	for _, arg := range ptr.Res.(*prog.GroupArg).Inner {
   214  		switch v := arg.(type) {
   215  		case *prog.ConstArg:
   216  			mib = append(mib, v)
   217  		}
   218  	}
   219  
   220  	if !arch.neutralizeSysctlKern(mib) {
   221  		return
   222  	}
   223  
   224  	for _, m := range mib {
   225  		m.Val = 0
   226  	}
   227  	// Reflect changes in the namelen argument.
   228  	if len(c.Args) >= 1 {
   229  		switch v := c.Args[1].(type) {
   230  		case *prog.ConstArg:
   231  			v.Val = 0
   232  		}
   233  	}
   234  }
   235  
   236  func (arch *arch) neutralizeSysctlKern(mib []*prog.ConstArg) bool {
   237  	// Do not fiddle with root only knob kern.maxclusters, one of the causes
   238  	// of "no output from test machine" reports.
   239  	if len(mib) >= 2 &&
   240  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXCLUSTERS {
   241  		return true
   242  	}
   243  
   244  	// Do not fiddle with root only knob kern.maxproc, can cause the
   245  	// syz-execprog to run out of resources.
   246  	if len(mib) >= 2 &&
   247  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXPROC {
   248  		return true
   249  	}
   250  
   251  	// Do not fiddle with root only knob kern.maxfiles, can cause the
   252  	// syz-execprog to run out of resources.
   253  	if len(mib) >= 2 &&
   254  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXFILES {
   255  		return true
   256  	}
   257  
   258  	// Do not fiddle with root only knob kern.maxthread, can cause the
   259  	// syz-execprog process to panic.
   260  	if len(mib) >= 2 &&
   261  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_MAXTHREAD {
   262  		return true
   263  	}
   264  
   265  	if len(mib) >= 2 &&
   266  		mib[0].Val == arch.CTL_KERN && mib[1].Val == arch.KERN_WITNESS {
   267  		return true
   268  	}
   269  
   270  	return false
   271  }
   272  
   273  func (arch *arch) annotateCall(c prog.ExecCall) string {
   274  	devArg := 2
   275  	switch c.Meta.Name {
   276  	case "mknodat":
   277  		devArg = 3
   278  		fallthrough
   279  	case "mknod":
   280  		dev := c.Args[devArg].(prog.ExecArgConst).Value
   281  		return fmt.Sprintf("major = %v, minor = %v", devmajor(dev), devminor(dev))
   282  	}
   283  	return ""
   284  }