github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/prog/validation.go (about) 1 // Copyright 2015 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 prog 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 ) 11 12 var debug = false // enabled in tests and fuzzers 13 14 func init() { 15 // Enable debug checking in all tests. 16 if strings.HasSuffix(os.Args[0], ".test") { 17 debug = true 18 } 19 } 20 21 func (p *Prog) debugValidate() { 22 if debug { 23 if err := p.validate(); err != nil { 24 panic(err) 25 } 26 } 27 } 28 29 func (p *Prog) validate() error { 30 return p.validateWithOpts(validationOptions{}) 31 } 32 33 type validCtx struct { 34 target *Target 35 isUnsafe bool 36 opts validationOptions 37 args map[Arg]bool 38 uses map[Arg]Arg 39 currentCall *Call 40 } 41 42 type validationOptions struct { 43 ignoreTransient bool 44 } 45 46 func (p *Prog) validateWithOpts(opts validationOptions) error { 47 ctx := &validCtx{ 48 target: p.Target, 49 isUnsafe: p.isUnsafe, 50 opts: opts, 51 args: make(map[Arg]bool), 52 uses: make(map[Arg]Arg), 53 } 54 for i, c := range p.Calls { 55 if c.Meta == nil { 56 return fmt.Errorf("call does not have meta information") 57 } 58 ctx.currentCall = c 59 if err := ctx.validateCall(c); err != nil { 60 return fmt.Errorf("call #%d %v: %w", i, c.Meta.Name, err) 61 } 62 } 63 ctx.currentCall = nil 64 for u, orig := range ctx.uses { 65 if !ctx.args[u] { 66 return fmt.Errorf("use of %+v referes to an out-of-tree arg\narg: %#v", orig, u) 67 } 68 } 69 return nil 70 } 71 72 func (ctx *validCtx) validateCall(c *Call) error { 73 if !ctx.isUnsafe && c.Meta.Attrs.Disabled { 74 return fmt.Errorf("use of a disabled call") 75 } 76 if c.Props.Rerun > 0 && c.Props.FailNth > 0 { 77 return fmt.Errorf("rerun > 0 && fail_nth > 0") 78 } 79 if len(c.Args) != len(c.Meta.Args) { 80 return fmt.Errorf("wrong number of arguments, want %v, got %v", 81 len(c.Meta.Args), len(c.Args)) 82 } 83 for i, arg := range c.Args { 84 if err := ctx.validateArg(arg, c.Meta.Args[i].Type, DirIn); err != nil { 85 return err 86 } 87 } 88 if err := c.checkConditions(ctx.target, ctx.opts.ignoreTransient); err != nil { 89 return err 90 } 91 return ctx.validateRet(c) 92 } 93 94 func (ctx *validCtx) validateRet(c *Call) error { 95 if c.Meta.Ret == nil { 96 if c.Ret != nil { 97 return fmt.Errorf("return value without type") 98 } 99 return nil 100 } 101 if c.Ret == nil { 102 return fmt.Errorf("return value is absent") 103 } 104 if c.Ret.Res != nil || c.Ret.Val != 0 || c.Ret.OpDiv != 0 || c.Ret.OpAdd != 0 { 105 return fmt.Errorf("return value %v is not empty", c.Ret) 106 } 107 return ctx.validateArg(c.Ret, c.Meta.Ret, DirOut) 108 } 109 110 func (ctx *validCtx) validateArg(arg Arg, typ Type, dir Dir) error { 111 if arg == nil { 112 return fmt.Errorf("nil arg") 113 } 114 if ctx.args[arg] { 115 return fmt.Errorf("arg %#v is referenced several times in the tree", arg) 116 } 117 if arg.Type() == nil { 118 return fmt.Errorf("no arg type") 119 } 120 if _, ok := typ.(*PtrType); ok { 121 dir = DirIn // pointers are always in 122 } 123 // We used to demand that Arg has exactly the same dir as Type, however, 124 // it leads to problems when dealing with ANYRES* types. 125 // If the resource was DirIn before squashing, we should not demand that 126 // it be DirInOut - it would only lead to mutations that make little sense. 127 // Let's only deny truly conflicting directions, e.g. DirIn vs DirOut. 128 if arg.Dir() != dir && dir != DirInOut { 129 return fmt.Errorf("arg %#v type %v has wrong dir %v, expect %v", arg, arg.Type(), arg.Dir(), dir) 130 } 131 if !ctx.target.isAnyPtr(arg.Type()) && arg.Type() != typ { 132 return fmt.Errorf("bad arg type %#v, expect %#v", arg.Type(), typ) 133 } 134 if ctx.currentCall.Meta.Attrs.NoSquash && ctx.target.isAnyPtr(arg.Type()) { 135 return fmt.Errorf("ANY argument for no_squash call %v", ctx.currentCall.Meta.Name) 136 } 137 ctx.args[arg] = true 138 return arg.validate(ctx, dir) 139 } 140 141 func (arg *ConstArg) validate(ctx *validCtx, dir Dir) error { 142 switch typ := arg.Type().(type) { 143 case *IntType: 144 if arg.Dir() == DirOut && !isDefault(arg) { 145 return fmt.Errorf("out int arg '%v' has bad const value %v", typ.Name(), arg.Val) 146 } 147 case *ProcType: 148 if arg.Val >= typ.ValuesPerProc && !isDefault(arg) { 149 return fmt.Errorf("per proc arg '%v' has bad value %v", typ.Name(), arg.Val) 150 } 151 case *CsumType: 152 if arg.Val != 0 { 153 return fmt.Errorf("csum arg '%v' has nonzero value %v", typ.Name(), arg.Val) 154 } 155 case *ConstType, *FlagsType, *LenType: 156 default: 157 return fmt.Errorf("const arg %v has bad type %v", arg, typ.Name()) 158 } 159 if arg.Dir() == DirOut { 160 // We generate output len arguments, which makes sense since it can be 161 // a length of a variable-length array which is not known otherwise. 162 typ := arg.Type() 163 if _, isLen := typ.(*LenType); !isLen { 164 if !typ.isDefaultArg(arg) { 165 return fmt.Errorf("output arg %q has non default value %+v", typ.Name(), arg) 166 } 167 } 168 } 169 return nil 170 } 171 172 func (arg *ResultArg) validate(ctx *validCtx, dir Dir) error { 173 typ, ok := arg.Type().(*ResourceType) 174 if !ok { 175 return fmt.Errorf("result arg %v has bad type %v", arg, arg.Type().Name()) 176 } 177 for u := range arg.uses { 178 if u == nil { 179 return fmt.Errorf("nil reference in uses for arg %+v", arg) 180 } 181 if u.Res != arg { 182 return fmt.Errorf("result arg '%v' has broken uses link to (%+v)", arg, u) 183 } 184 ctx.uses[u] = arg 185 } 186 if arg.Dir() == DirOut && arg.Val != 0 && arg.Val != typ.Default() { 187 return fmt.Errorf("out resource arg '%v' has bad const value %v", typ.Name(), arg.Val) 188 } 189 if arg.Res != nil { 190 if !ctx.args[arg.Res] { 191 return fmt.Errorf("result arg %v references out-of-tree result: %#v -> %#v", 192 typ.Name(), arg, arg.Res) 193 } 194 if !arg.Res.uses[arg] { 195 return fmt.Errorf("result arg '%v' has broken link (%+v)", typ.Name(), arg.Res.uses) 196 } 197 } 198 if arg.Dir() == DirIn && len(arg.uses) > 0 { 199 return fmt.Errorf("result arg '%v' is DirIn, but is used %d times", typ.Name(), len(arg.uses)) 200 } 201 if len(arg.uses) > 0 && arg.Size() > 8 { 202 return fmt.Errorf("result arg '%v' is to be copied out, yet it's bigger than int64 (%d > 8)", typ.Name(), arg.Size()) 203 } 204 return nil 205 } 206 207 func (arg *DataArg) validate(ctx *validCtx, dir Dir) error { 208 typ, ok := arg.Type().(*BufferType) 209 if !ok { 210 return fmt.Errorf("data arg %v has bad type %v", arg, arg.Type().Name()) 211 } 212 if arg.Dir() == DirOut && len(arg.data) != 0 { 213 return fmt.Errorf("output arg '%v' has data", typ.Name()) 214 } 215 if !typ.Varlen() && typ.Size() != arg.Size() { 216 return fmt.Errorf("data arg %v has wrong size %v, want %v", 217 typ.Name(), arg.Size(), typ.Size()) 218 } 219 switch typ.Kind { 220 case BufferString: 221 if typ.TypeSize != 0 && arg.Size() != typ.TypeSize { 222 return fmt.Errorf("string arg '%v' has size %v, which should be %v", 223 typ.Name(), arg.Size(), typ.TypeSize) 224 } 225 case BufferFilename: 226 if !ctx.isUnsafe && escapingFilename(string(arg.data)) { 227 return fmt.Errorf("escaping filename %q", arg.data) 228 } 229 } 230 return nil 231 } 232 233 func (arg *GroupArg) validate(ctx *validCtx, dir Dir) error { 234 switch typ := arg.Type().(type) { 235 case *StructType: 236 if len(arg.Inner) != len(typ.Fields) { 237 return fmt.Errorf("struct arg '%v' has wrong number of fields: want %v, got %v", 238 typ.Name(), len(typ.Fields), len(arg.Inner)) 239 } 240 for i, field := range arg.Inner { 241 if err := ctx.validateArg(field, typ.Fields[i].Type, typ.Fields[i].Dir(dir)); err != nil { 242 return err 243 } 244 } 245 case *ArrayType: 246 if typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd && 247 uint64(len(arg.Inner)) != typ.RangeBegin { 248 return fmt.Errorf("array %v has wrong number of elements %v, want %v", 249 typ.Name(), len(arg.Inner), typ.RangeBegin) 250 } 251 for _, elem := range arg.Inner { 252 if err := ctx.validateArg(elem, typ.Elem, dir); err != nil { 253 return err 254 } 255 } 256 default: 257 return fmt.Errorf("group arg %v has bad type %v", arg, typ.Name()) 258 } 259 return nil 260 } 261 262 func (arg *UnionArg) validate(ctx *validCtx, dir Dir) error { 263 typ, ok := arg.Type().(*UnionType) 264 if !ok { 265 return fmt.Errorf("union arg %v has bad type %v", arg, arg.Type().Name()) 266 } 267 if arg.Index < 0 || arg.Index >= len(typ.Fields) { 268 return fmt.Errorf("union arg %v has bad index %v/%v", arg, arg.Index, len(typ.Fields)) 269 } 270 if arg.transient && !ctx.opts.ignoreTransient { 271 // The union must have been patched via Call.setDefaultConditions. 272 return fmt.Errorf("union arg %v is transient (incomplete)", arg) 273 } 274 opt := typ.Fields[arg.Index] 275 return ctx.validateArg(arg.Option, opt.Type, opt.Dir(dir)) 276 } 277 278 func (arg *PointerArg) validate(ctx *validCtx, dir Dir) error { 279 switch typ := arg.Type().(type) { 280 case *VmaType: 281 if arg.Res != nil { 282 return fmt.Errorf("vma arg '%v' has data", typ.Name()) 283 } 284 case *PtrType: 285 if arg.Res != nil { 286 if err := ctx.validateArg(arg.Res, typ.Elem, typ.ElemDir); err != nil { 287 return err 288 } 289 } 290 if arg.VmaSize != 0 { 291 return fmt.Errorf("pointer arg '%v' has nonzero size", typ.Name()) 292 } 293 if arg.Dir() == DirOut { 294 return fmt.Errorf("pointer arg '%v' has output direction", typ.Name()) 295 } 296 default: 297 return fmt.Errorf("ptr arg %v has bad type %v", arg, typ.Name()) 298 } 299 if arg.IsSpecial() { 300 if -arg.Address >= uint64(len(ctx.target.SpecialPointers)) { 301 return fmt.Errorf("special ptr arg %v has bad value 0x%x", arg.Type().Name(), arg.Address) 302 } 303 } else { 304 maxMem := ctx.target.NumPages * ctx.target.PageSize 305 addr, size := arg.Address, arg.VmaSize 306 if size == 0 && arg.Res != nil { 307 size = arg.Res.Size() 308 } 309 if ctx.isUnsafe { 310 // Allow mapping 2 surrounding pages for DataMmapProg. 311 addr += ctx.target.PageSize 312 maxMem += 2 * ctx.target.PageSize 313 } 314 if addr >= maxMem || addr+size > maxMem { 315 return fmt.Errorf("ptr %v has bad address %v/%v/%v", 316 arg.Type().Name(), arg.Address, arg.VmaSize, size) 317 } 318 } 319 return nil 320 }