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