syzkaller/prog/prog.go

207 lines
5.2 KiB
Go
Raw Normal View History

2015-10-12 10:16:57 +02:00
// 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 (
2015-12-31 15:24:08 +01:00
"fmt"
2015-10-12 10:16:57 +02:00
"github.com/google/syzkaller/sys"
)
type Prog struct {
Calls []*Call
}
type Call struct {
Meta *sys.Call
Args []*Arg
Ret *Arg
}
type Arg struct {
Call *Call
Type sys.Type
Kind ArgKind
Dir ArgDir
Val uintptr // value of ArgConst
ByteSize uintptr // size of an array in bytes for ArgConst
2015-10-12 10:16:57 +02:00
AddrPage uintptr // page index for ArgPointer address, page count for ArgPageSize
AddrOffset int // page offset for ArgPointer address
Data []byte // data of ArgData
Inner []*Arg // subargs of ArgGroup
Res *Arg // target of ArgResult, pointee for ArgPointer
Uses map[*Arg]bool // this arg is used by those ArgResult args
OpDiv uintptr // divide result for ArgResult (executed before OpAdd)
OpAdd uintptr // add to result for ArgResult
2015-12-29 15:00:57 +01:00
// ArgUnion/UnionType
Option *Arg
OptionType sys.Type
2015-10-12 10:16:57 +02:00
}
type ArgKind int
const (
ArgConst ArgKind = iota
ArgResult
ArgPointer // even if these are always constant (for reproducibility), we use a separate type because they are represented in an abstract (base+page+offset) form
ArgPageSize // same as ArgPointer but base is not added, so it represents "lengths" in pages
ArgData
2015-12-29 15:00:57 +01:00
ArgGroup // logical group of args (struct or array)
ArgUnion
2015-10-12 10:16:57 +02:00
ArgReturn // fake value denoting syscall return value
)
type ArgDir sys.Dir
const (
DirIn = ArgDir(sys.DirIn)
DirOut = ArgDir(sys.DirOut)
DirInOut = ArgDir(sys.DirInOut)
)
func (a *Arg) Size(typ sys.Type) uintptr {
switch typ1 := typ.(type) {
case sys.IntType, sys.LenType, sys.FlagsType, sys.ConstType, sys.StrConstType,
sys.FileoffType, sys.ResourceType, sys.VmaType, sys.PtrType:
return typ.Size()
2015-10-12 10:16:57 +02:00
case sys.FilenameType:
return uintptr(len(a.Data))
case sys.BufferType:
return uintptr(len(a.Data))
2015-10-12 10:16:57 +02:00
case sys.StructType:
var size uintptr
for i, f := range typ1.Fields {
size += a.Inner[i].Size(f)
}
return size
2015-12-29 15:00:57 +01:00
case sys.UnionType:
return a.Option.Size(a.OptionType)
2015-10-12 10:16:57 +02:00
case sys.ArrayType:
var size uintptr
for _, in := range a.Inner {
size += in.Size(typ1.Type)
2015-10-12 10:16:57 +02:00
}
return size
2015-10-12 10:16:57 +02:00
default:
panic("unknown arg type")
}
}
func constArg(v uintptr) *Arg {
return &Arg{Kind: ArgConst, Val: v}
}
func resultArg(r *Arg) *Arg {
arg := &Arg{Kind: ArgResult, Res: r}
if r.Uses == nil {
r.Uses = make(map[*Arg]bool)
}
if r.Uses[arg] {
panic("already used")
}
r.Uses[arg] = true
return arg
}
func dataArg(data []byte) *Arg {
return &Arg{Kind: ArgData, Data: append([]byte{}, data...)}
}
func pointerArg(page uintptr, off int, obj *Arg) *Arg {
return &Arg{Kind: ArgPointer, AddrPage: page, AddrOffset: off, Res: obj}
}
func pageSizeArg(npages uintptr, off int) *Arg {
return &Arg{Kind: ArgPageSize, AddrPage: npages, AddrOffset: off}
}
func groupArg(inner []*Arg) *Arg {
return &Arg{Kind: ArgGroup, Inner: inner}
}
2015-12-29 15:00:57 +01:00
func unionArg(opt *Arg, typ sys.Type) *Arg {
return &Arg{Kind: ArgUnion, Option: opt, OptionType: typ}
}
2015-10-12 10:16:57 +02:00
func returnArg() *Arg {
return &Arg{Kind: ArgReturn, Dir: DirOut}
}
func (p *Prog) insertBefore(c *Call, calls []*Call) {
idx := 0
for ; idx < len(p.Calls); idx++ {
if p.Calls[idx] == c {
break
}
}
var newCalls []*Call
newCalls = append(newCalls, p.Calls[:idx]...)
newCalls = append(newCalls, calls...)
if idx < len(p.Calls) {
newCalls = append(newCalls, p.Calls[idx])
newCalls = append(newCalls, p.Calls[idx+1:]...)
}
p.Calls = newCalls
}
2015-12-31 15:24:08 +01:00
// replaceArg replaces arg with arg1 in p, and inserts calls before arg call.
func (p *Prog) replaceArg(arg, arg1 *Arg, calls []*Call) {
if arg.Kind != ArgConst && arg.Kind != ArgResult && arg.Kind != ArgPointer && arg.Kind != ArgUnion {
panic(fmt.Sprintf("replaceArg: bad arg kind %v", arg.Kind))
}
if arg1.Kind != ArgConst && arg1.Kind != ArgResult && arg1.Kind != ArgPointer && arg.Kind != ArgUnion {
panic(fmt.Sprintf("replaceArg: bad arg1 kind %v", arg1.Kind))
}
if arg.Kind == ArgResult {
delete(arg.Res.Uses, arg)
}
for _, c := range calls {
assignTypeAndDir(c)
sanitizeCall(c)
}
c := arg.Call
p.insertBefore(c, calls)
// Somewhat hacky, but safe and preserves references to arg.
uses := arg.Uses
*arg = *arg1
arg.Uses = uses
if arg.Kind == ArgResult {
delete(arg.Res.Uses, arg1)
arg.Res.Uses[arg] = true
}
assignTypeAndDir(c)
sanitizeCall(c)
}
// removeArg removes all references to/from arg0 from p.
func (p *Prog) removeArg(arg0 *Arg) {
foreachSubarg(arg0, func(arg, _ *Arg, _ *[]*Arg) {
if arg.Kind == ArgResult {
if _, ok := arg.Res.Uses[arg]; !ok {
panic("broken tree")
}
delete(arg.Res.Uses, arg)
}
for arg1 := range arg.Uses {
if arg1.Kind != ArgResult {
panic("use references not ArgResult")
}
arg2 := constArg(arg1.Type.Default())
p.replaceArg(arg1, arg2, nil)
}
})
}
// removeCall removes call idx from p.
func (p *Prog) removeCall(idx int) {
c := p.Calls[idx]
copy(p.Calls[idx:], p.Calls[idx+1:])
p.Calls = p.Calls[:len(p.Calls)-1]
for _, arg := range c.Args {
p.removeArg(arg)
}
p.removeArg(c.Ret)
}