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 }