diff options
| author | Dmitry Vyukov <dvyukov@google.com> | 2015-10-12 10:16:57 +0200 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2015-10-12 10:16:57 +0200 |
| commit | 874c5754bb22dbf77d6b600ff91f0f4f1fc5073a (patch) | |
| tree | 0075fbd088046ad5c86e6e972235701d68b3ce7c /prog/validation.go | |
initial commit
Diffstat (limited to 'prog/validation.go')
| -rw-r--r-- | prog/validation.go | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/prog/validation.go b/prog/validation.go new file mode 100644 index 000000000..097848bed --- /dev/null +++ b/prog/validation.go @@ -0,0 +1,168 @@ +// 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" + + "github.com/google/syzkaller/sys" +) + +type validCtx struct { + args map[*Arg]bool + uses map[*Arg]*Arg +} + +func (p *Prog) validate() error { + ctx := &validCtx{make(map[*Arg]bool), make(map[*Arg]*Arg)} + for _, c := range p.Calls { + if err := c.validate(ctx); err != nil { + + fmt.Printf("PROGRAM:\n") + for _, c := range p.Calls { + fmt.Printf(" %v: %+v %p\n", c.Meta.Name, c.Args, c.Ret) + } + + return 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 (c *Call) validate(ctx *validCtx) error { + if len(c.Args) != len(c.Meta.Args) { + return fmt.Errorf("syscall %v: wrong number of arguments, want %v, got %v", c.Meta.Name, len(c.Meta.Args), len(c.Args)) + } + var checkArg func(arg *Arg, typ sys.Type) error + checkArg = func(arg *Arg, typ sys.Type) error { + if arg == nil { + return fmt.Errorf("syscall %v: nil arg", c.Meta.Name) + } + if arg.Call != c { + return fmt.Errorf("syscall %v: arg has wrong call, call=%p, arg=%+v", c.Meta.Name, c, *arg) + } + if ctx.args[arg] { + return fmt.Errorf("syscall %v: arg is referenced several times in the tree", c.Meta.Name) + } + ctx.args[arg] = true + for u := range arg.Uses { + ctx.uses[u] = arg + } + if arg.Type == nil { + return fmt.Errorf("syscall %v: no type", c.Meta.Name) + } + if arg.Type.Name() != typ.Name() { + return fmt.Errorf("syscall %v: arg '%v' type mismatch", c.Meta.Name, typ.Name()) + } + if arg.Dir == DirOut { + if arg.Val != 0 || arg.AddrPage != 0 || arg.AddrOffset != 0 || len(arg.Data) != 0 { + return fmt.Errorf("syscall %v: output arg '%v' has data", c.Meta.Name, typ.Name()) + } + } + switch arg.Type.(type) { + case sys.ResourceType: + switch arg.Kind { + case ArgResult: + case ArgReturn: + case ArgConst: + if arg.Dir == DirOut && arg.Val != 0 { + return fmt.Errorf("syscall %v: out resource arg '%v' has bad const value %v", c.Meta.Name, typ.Name(), arg.Val) + } + default: + return fmt.Errorf("syscall %v: fd arg '%v' has bad kind %v", c.Meta.Name, typ.Name(), arg.Kind) + } + case sys.FilenameType: + switch arg.Kind { + case ArgData: + default: + return fmt.Errorf("syscall %v: filename arg '%v' has bad kind %v", c.Meta.Name, typ.Name(), arg.Kind) + } + } + switch arg.Kind { + case ArgConst: + case ArgResult: + if arg.Res == nil { + return fmt.Errorf("syscall %v: result arg '%v' has no reference", c.Meta.Name, typ.Name()) + } + if !ctx.args[arg.Res] { + return fmt.Errorf("syscall %v: result arg '%v' references out-of-tree result: %p%+v -> %v %p%+v", + c.Meta.Name, typ.Name(), arg, arg, arg.Res.Call.Meta.Name, arg.Res, arg.Res) + } + if _, ok := arg.Res.Uses[arg]; !ok { + return fmt.Errorf("syscall %v: result arg '%v' has broken link (%+v)", c.Meta.Name, typ.Name(), arg.Res.Uses) + } + case ArgPointer: + if arg.Dir != DirIn { + return fmt.Errorf("syscall %v: pointer arg '%v' has output direction", c.Meta.Name, typ.Name()) + } + switch typ1 := typ.(type) { + case sys.VmaType: + if arg.Res != nil { + return fmt.Errorf("syscall %v: vma arg '%v' has data", c.Meta.Name, typ.Name()) + } + case sys.PtrType: + if arg.Res != nil { + if err := checkArg(arg.Res, typ1.Type); err != nil { + return err + } + } + default: + return fmt.Errorf("syscall %v: pointer arg '%v' has bad meta type %+v", c.Meta.Name, typ.Name(), typ) + } + case ArgPageSize: + case ArgData: + case ArgGroup: + switch typ1 := typ.(type) { + case sys.StructType: + if len(arg.Inner) != len(typ1.Fields) { + return fmt.Errorf("syscall %v: struct arg '%v' has wrong number of fields, want %v, got %v", c.Meta.Name, typ.Name(), len(typ1.Fields), len(arg.Inner)) + } + for i, arg1 := range arg.Inner { + if err := checkArg(arg1, typ1.Fields[i]); err != nil { + return err + } + } + case sys.ArrayType: + for _, arg1 := range arg.Inner { + if err := checkArg(arg1, typ1.Type); err != nil { + return err + } + } + default: + return fmt.Errorf("syscall %v: group arg '%v' has bad underlying type %+v", c.Meta.Name, typ.Name(), typ) + } + case ArgReturn: + default: + return fmt.Errorf("syscall %v: unknown arg '%v' kind", c.Meta.Name, typ.Name()) + } + return nil + } + for i, arg := range c.Args { + if c.Ret.Kind != ArgReturn { + return fmt.Errorf("syscall %v: arg '%v' has wrong return kind", c.Meta.Name, arg.Type.Name()) + } + if err := checkArg(arg, c.Meta.Args[i]); err != nil { + return err + } + } + if c.Ret == nil { + return fmt.Errorf("syscall %v: return value is absent", c.Meta.Name) + } + if c.Ret.Kind != ArgReturn { + return fmt.Errorf("syscall %v: return value has wrong kind %v", c.Meta.Name, c.Ret.Kind) + } + if c.Meta.Ret != nil { + if err := checkArg(c.Ret, c.Meta.Ret); err != nil { + return err + } + } else if c.Ret.Type != nil { + return fmt.Errorf("syscall %v: return value has spurious type: %+v", c.Meta.Name, c.Ret.Type) + } + return nil +} |
