| // Copyright 2015 syzkaller project authors. All rights reserved. |
| // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. |
| |
| package prog |
| |
| import ( |
| "fmt" |
| "path/filepath" |
| ) |
| |
| var debug = false // enabled in tests |
| |
| func (p *Prog) debugValidate() { |
| if debug { |
| if err := p.validate(); err != nil { |
| panic(err) |
| } |
| } |
| } |
| |
| type validCtx struct { |
| target *Target |
| args map[Arg]bool |
| uses map[Arg]Arg |
| } |
| |
| func (p *Prog) validate() error { |
| ctx := &validCtx{ |
| target: p.Target, |
| args: make(map[Arg]bool), |
| uses: make(map[Arg]Arg), |
| } |
| for _, c := range p.Calls { |
| if c.Meta == nil { |
| return fmt.Errorf("call does not have meta information") |
| } |
| if err := ctx.validateCall(c); err != nil { |
| return fmt.Errorf("call %v: %v", c.Meta.Name, err) |
| } |
| } |
| for u, orig := range ctx.uses { |
| if !ctx.args[u] { |
| return fmt.Errorf("use of %+v referes to an out-of-tree arg\narg: %#v", orig, u) |
| } |
| } |
| return nil |
| } |
| |
| func (ctx *validCtx) validateCall(c *Call) error { |
| if len(c.Args) != len(c.Meta.Args) { |
| return fmt.Errorf("wrong number of arguments, want %v, got %v", |
| len(c.Meta.Args), len(c.Args)) |
| } |
| for i, arg := range c.Args { |
| if err := ctx.validateArg(arg, c.Meta.Args[i]); err != nil { |
| return err |
| } |
| } |
| return ctx.validateRet(c) |
| } |
| |
| func (ctx *validCtx) validateRet(c *Call) error { |
| if c.Meta.Ret == nil { |
| if c.Ret != nil { |
| return fmt.Errorf("return value without type") |
| } |
| return nil |
| } |
| if c.Ret == nil { |
| return fmt.Errorf("return value is absent") |
| } |
| if c.Ret.Type().Dir() != DirOut { |
| return fmt.Errorf("return value %v is not output", c.Ret) |
| } |
| if c.Ret.Res != nil || c.Ret.Val != 0 || c.Ret.OpDiv != 0 || c.Ret.OpAdd != 0 { |
| return fmt.Errorf("return value %v is not empty", c.Ret) |
| } |
| return ctx.validateArg(c.Ret, c.Meta.Ret) |
| } |
| |
| func (ctx *validCtx) validateArg(arg Arg, typ Type) error { |
| if arg == nil { |
| return fmt.Errorf("nil arg") |
| } |
| if ctx.args[arg] { |
| return fmt.Errorf("arg %#v is referenced several times in the tree", arg) |
| } |
| if arg.Type() == nil { |
| return fmt.Errorf("no arg type") |
| } |
| if !ctx.target.isAnyPtr(arg.Type()) && arg.Type() != typ { |
| return fmt.Errorf("bad arg type %#v, expect %#v", arg.Type(), typ) |
| } |
| ctx.args[arg] = true |
| return arg.validate(ctx) |
| } |
| |
| func (arg *ConstArg) validate(ctx *validCtx) error { |
| switch typ := arg.Type().(type) { |
| case *IntType: |
| if typ.Dir() == DirOut && !isDefault(arg) { |
| return fmt.Errorf("out int arg '%v' has bad const value %v", typ.Name(), arg.Val) |
| } |
| case *ProcType: |
| if arg.Val >= typ.ValuesPerProc && !isDefault(arg) { |
| return fmt.Errorf("per proc arg '%v' has bad value %v", typ.Name(), arg.Val) |
| } |
| case *CsumType: |
| if arg.Val != 0 { |
| return fmt.Errorf("csum arg '%v' has nonzero value %v", typ.Name(), arg.Val) |
| } |
| case *ConstType, *FlagsType, *LenType: |
| default: |
| return fmt.Errorf("const arg %v has bad type %v", arg, typ.Name()) |
| } |
| if typ := arg.Type(); typ.Dir() == DirOut { |
| // We generate output len arguments, which makes sense since it can be |
| // a length of a variable-length array which is not known otherwise. |
| if _, isLen := typ.(*LenType); !isLen { |
| if !typ.isDefaultArg(arg) { |
| return fmt.Errorf("output arg '%v'/'%v' has non default value '%+v'", |
| typ.FieldName(), typ.Name(), arg) |
| } |
| } |
| } |
| return nil |
| } |
| |
| func (arg *ResultArg) validate(ctx *validCtx) error { |
| typ, ok := arg.Type().(*ResourceType) |
| if !ok { |
| return fmt.Errorf("result arg %v has bad type %v", arg, arg.Type().Name()) |
| } |
| for u := range arg.uses { |
| if u == nil { |
| return fmt.Errorf("nil reference in uses for arg %+v", arg) |
| } |
| if u.Res != arg { |
| return fmt.Errorf("result arg '%v' has broken uses link to (%+v)", arg, u) |
| } |
| ctx.uses[u] = arg |
| } |
| if typ.Dir() == DirOut && arg.Val != 0 && arg.Val != typ.Default() { |
| return fmt.Errorf("out resource arg '%v' has bad const value %v", typ.Name(), arg.Val) |
| } |
| if arg.Res != nil { |
| if !ctx.args[arg.Res] { |
| return fmt.Errorf("result arg %v references out-of-tree result: %#v -> %#v", |
| typ.Name(), arg, arg.Res) |
| } |
| if !arg.Res.uses[arg] { |
| return fmt.Errorf("result arg '%v' has broken link (%+v)", typ.Name(), arg.Res.uses) |
| } |
| } |
| return nil |
| } |
| |
| func (arg *DataArg) validate(ctx *validCtx) error { |
| typ, ok := arg.Type().(*BufferType) |
| if !ok { |
| return fmt.Errorf("data arg %v has bad type %v", arg, arg.Type().Name()) |
| } |
| if typ.Dir() == DirOut && len(arg.data) != 0 { |
| return fmt.Errorf("output arg '%v' has data", typ.Name()) |
| } |
| if !typ.Varlen() && typ.Size() != arg.Size() { |
| return fmt.Errorf("data arg %v has wrong size %v, want %v", |
| typ.Name(), arg.Size(), typ.Size()) |
| } |
| switch typ.Kind { |
| case BufferString: |
| if typ.TypeSize != 0 && arg.Size() != typ.TypeSize { |
| return fmt.Errorf("string arg '%v' has size %v, which should be %v", |
| typ.Name(), arg.Size(), typ.TypeSize) |
| } |
| case BufferFilename: |
| file := string(arg.data) |
| for len(file) != 0 && file[len(file)-1] == 0 { |
| file = file[:len(file)-1] |
| } |
| file = filepath.Clean(file) |
| if len(file) > 0 && file[0] == '/' || |
| len(file) > 1 && file[0] == '.' && file[1] == '.' { |
| return fmt.Errorf("sandbox escaping file name %q", string(arg.data)) |
| } |
| } |
| return nil |
| } |
| |
| func (arg *GroupArg) validate(ctx *validCtx) error { |
| switch typ := arg.Type().(type) { |
| case *StructType: |
| if len(arg.Inner) != len(typ.Fields) { |
| return fmt.Errorf("struct arg '%v' has wrong number of fields: want %v, got %v", |
| typ.Name(), len(typ.Fields), len(arg.Inner)) |
| } |
| for i, field := range arg.Inner { |
| if err := ctx.validateArg(field, typ.Fields[i]); err != nil { |
| return err |
| } |
| } |
| case *ArrayType: |
| if typ.Kind == ArrayRangeLen && typ.RangeBegin == typ.RangeEnd && |
| uint64(len(arg.Inner)) != typ.RangeBegin { |
| return fmt.Errorf("array %v has wrong number of elements %v, want %v", |
| typ.Name(), len(arg.Inner), typ.RangeBegin) |
| } |
| for _, elem := range arg.Inner { |
| if err := ctx.validateArg(elem, typ.Type); err != nil { |
| return err |
| } |
| } |
| default: |
| return fmt.Errorf("group arg %v has bad type %v", arg, typ.Name()) |
| } |
| return nil |
| } |
| |
| func (arg *UnionArg) validate(ctx *validCtx) error { |
| typ, ok := arg.Type().(*UnionType) |
| if !ok { |
| return fmt.Errorf("union arg %v has bad type %v", arg, arg.Type().Name()) |
| } |
| var optType Type |
| for _, typ1 := range typ.Fields { |
| if arg.Option.Type().FieldName() == typ1.FieldName() { |
| optType = typ1 |
| break |
| } |
| } |
| if optType == nil { |
| return fmt.Errorf("union arg '%v' has bad option", typ.Name()) |
| } |
| return ctx.validateArg(arg.Option, optType) |
| } |
| |
| func (arg *PointerArg) validate(ctx *validCtx) error { |
| switch typ := arg.Type().(type) { |
| case *VmaType: |
| if arg.Res != nil { |
| return fmt.Errorf("vma arg '%v' has data", typ.Name()) |
| } |
| case *PtrType: |
| if arg.Res == nil && !arg.Type().Optional() { |
| return fmt.Errorf("non optional pointer arg '%v' is nil", typ.Name()) |
| } |
| if arg.Res != nil { |
| if err := ctx.validateArg(arg.Res, typ.Type); err != nil { |
| return err |
| } |
| } |
| if arg.VmaSize != 0 { |
| return fmt.Errorf("pointer arg '%v' has nonzero size", typ.Name()) |
| } |
| if typ.Dir() == DirOut { |
| return fmt.Errorf("pointer arg '%v' has output direction", typ.Name()) |
| } |
| default: |
| return fmt.Errorf("ptr arg %v has bad type %v", arg, typ.Name()) |
| } |
| maxMem := ctx.target.NumPages * ctx.target.PageSize |
| size := arg.VmaSize |
| if size == 0 && arg.Res != nil { |
| size = arg.Res.Size() |
| } |
| if arg.Address >= maxMem || arg.Address+size > maxMem { |
| return fmt.Errorf("ptr %v has bad address %v/%v/%v", |
| arg.Type().Name(), arg.Address, arg.VmaSize, size) |
| } |
| return nil |
| } |