2015-10-12 08:16:57 +00: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 14:24:08 +00:00
"fmt"
2015-10-12 08:16:57 +00:00
"github.com/google/syzkaller/sys"
)
type Prog struct {
Calls [ ] * Call
}
type Call struct {
Meta * sys . Call
Args [ ] * Arg
Ret * Arg
}
type Arg struct {
2016-10-11 12:24:25 +00:00
Type sys . Type
Kind ArgKind
Val uintptr // value of ArgConst
AddrPage uintptr // page index for ArgPointer address, page count for ArgPageSize
AddrOffset int // page offset for ArgPointer address
AddrPagesNum uintptr // number of available pages for ArgPointer
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 14:00:57 +00:00
// ArgUnion/UnionType
Option * Arg
OptionType sys . Type
2015-10-12 08:16:57 +00: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 14:00:57 +00:00
ArgGroup // logical group of args (struct or array)
ArgUnion
2015-10-12 08:16:57 +00:00
ArgReturn // fake value denoting syscall return value
)
2016-10-11 12:24:25 +00:00
// Returns inner arg for PtrType args
2016-10-29 21:42:36 +00:00
func ( a * Arg ) InnerArg ( ) * Arg {
switch typ := a . Type . ( type ) {
2016-10-19 14:20:37 +00:00
case * sys . PtrType :
2016-10-11 12:24:25 +00:00
if a . Res == nil {
2016-10-29 21:42:36 +00:00
if ! typ . Optional ( ) {
panic ( fmt . Sprintf ( "non-optional pointer is nil\narg: %+v\ntype: %+v" , a , typ ) )
2016-10-11 12:24:25 +00:00
}
2016-10-29 21:42:36 +00:00
return nil
2016-10-11 12:24:25 +00:00
} else {
2016-10-29 21:42:36 +00:00
return a . Res . InnerArg ( )
2016-10-11 12:24:25 +00:00
}
default :
return a
}
}
2016-10-11 19:01:06 +00:00
func encodeValue ( value , size uintptr , bigEndian bool ) uintptr {
if ! bigEndian {
return value
}
switch size {
case 2 :
return uintptr ( swap16 ( uint16 ( value ) ) )
case 4 :
return uintptr ( swap32 ( uint32 ( value ) ) )
case 8 :
return uintptr ( swap64 ( uint64 ( value ) ) )
default :
panic ( fmt . Sprintf ( "bad size %v for value %v" , size , value ) )
}
}
// Returns value taking endianness into consideration.
2016-10-29 21:42:36 +00:00
func ( a * Arg ) Value ( ) uintptr {
switch typ := a . Type . ( type ) {
2016-10-19 14:20:37 +00:00
case * sys . IntType :
2016-10-29 21:42:36 +00:00
return encodeValue ( a . Val , typ . Size ( ) , typ . BigEndian )
2016-10-19 14:20:37 +00:00
case * sys . ConstType :
2016-10-29 21:42:36 +00:00
return encodeValue ( a . Val , typ . Size ( ) , typ . BigEndian )
2016-10-19 14:20:37 +00:00
case * sys . FlagsType :
2016-10-29 21:42:36 +00:00
return encodeValue ( a . Val , typ . Size ( ) , typ . BigEndian )
2016-10-19 14:20:37 +00:00
case * sys . LenType :
2016-10-29 21:42:36 +00:00
return encodeValue ( a . Val , typ . Size ( ) , typ . BigEndian )
2016-10-11 19:01:06 +00:00
}
return a . Val
}
2016-10-29 21:42:36 +00:00
func ( a * Arg ) Size ( ) uintptr {
switch typ := a . Type . ( type ) {
2016-10-31 21:15:13 +00:00
case * sys . IntType , * sys . LenType , * sys . FlagsType , * sys . ConstType ,
2016-10-29 22:06:40 +00:00
* sys . ResourceType , * sys . VmaType , * sys . PtrType :
2015-12-11 14:42:14 +00:00
return typ . Size ( )
2016-10-19 14:20:37 +00:00
case * sys . BufferType :
2015-12-11 14:42:14 +00:00
return uintptr ( len ( a . Data ) )
2016-09-03 10:36:49 +00:00
case * sys . StructType :
2015-10-12 08:16:57 +00:00
var size uintptr
2016-10-29 21:42:36 +00:00
for _ , fld := range a . Inner {
size += fld . Size ( )
2015-10-12 08:16:57 +00:00
}
return size
2016-09-03 10:36:49 +00:00
case * sys . UnionType :
2016-10-29 21:42:36 +00:00
return a . Option . Size ( )
2016-10-19 14:20:37 +00:00
case * sys . ArrayType :
2015-12-11 14:42:14 +00:00
var size uintptr
for _ , in := range a . Inner {
2016-10-29 21:42:36 +00:00
size += in . Size ( )
2015-10-12 08:16:57 +00:00
}
2015-12-11 14:42:14 +00:00
return size
2015-10-12 08:16:57 +00:00
default :
panic ( "unknown arg type" )
}
}
2016-10-29 08:24:21 +00:00
func constArg ( t sys . Type , v uintptr ) * Arg {
return & Arg { Type : t , Kind : ArgConst , Val : v }
2015-10-12 08:16:57 +00:00
}
2016-10-29 08:24:21 +00:00
func resultArg ( t sys . Type , r * Arg ) * Arg {
arg := & Arg { Type : t , Kind : ArgResult , Res : r }
2015-10-12 08:16:57 +00:00
if r . Uses == nil {
r . Uses = make ( map [ * Arg ] bool )
}
if r . Uses [ arg ] {
panic ( "already used" )
}
r . Uses [ arg ] = true
return arg
}
2016-10-29 08:24:21 +00:00
func dataArg ( t sys . Type , data [ ] byte ) * Arg {
return & Arg { Type : t , Kind : ArgData , Data : append ( [ ] byte { } , data ... ) }
2015-10-12 08:16:57 +00:00
}
2016-10-29 08:24:21 +00:00
func pointerArg ( t sys . Type , page uintptr , off int , npages uintptr , obj * Arg ) * Arg {
return & Arg { Type : t , Kind : ArgPointer , AddrPage : page , AddrOffset : off , AddrPagesNum : npages , Res : obj }
2015-10-12 08:16:57 +00:00
}
2016-10-29 08:24:21 +00:00
func pageSizeArg ( t sys . Type , npages uintptr , off int ) * Arg {
return & Arg { Type : t , Kind : ArgPageSize , AddrPage : npages , AddrOffset : off }
2015-10-12 08:16:57 +00:00
}
2016-10-29 08:24:21 +00:00
func groupArg ( t sys . Type , inner [ ] * Arg ) * Arg {
return & Arg { Type : t , Kind : ArgGroup , Inner : inner }
2015-10-12 08:16:57 +00:00
}
2016-10-29 08:24:21 +00:00
func unionArg ( t sys . Type , opt * Arg , typ sys . Type ) * Arg {
return & Arg { Type : t , Kind : ArgUnion , Option : opt , OptionType : typ }
2015-12-29 14:00:57 +00:00
}
2016-10-29 08:24:21 +00:00
func returnArg ( t sys . Type ) * Arg {
return & Arg { Type : t , Kind : ArgReturn }
2015-10-12 08:16:57 +00:00
}
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 14:24:08 +00:00
2016-10-29 08:24:21 +00:00
// replaceArg replaces arg with arg1 in call c in program p, and inserts calls before arg call.
func ( p * Prog ) replaceArg ( c * Call , arg , arg1 * Arg , calls [ ] * Call ) {
2015-12-31 14:24:08 +00:00
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 {
sanitizeCall ( c )
}
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
}
sanitizeCall ( c )
}
2016-10-29 08:24:21 +00:00
// removeArg removes all references to/from arg0 of call c from p.
func ( p * Prog ) removeArg ( c * Call , arg0 * Arg ) {
2015-12-31 14:24:08 +00:00
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" )
}
2016-10-29 08:24:21 +00:00
arg2 := constArg ( arg1 . Type , arg1 . Type . Default ( ) )
p . replaceArg ( c , arg1 , arg2 , nil )
2015-12-31 14:24:08 +00:00
}
} )
}
// 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 {
2016-10-29 08:24:21 +00:00
p . removeArg ( c , arg )
2015-12-31 14:24:08 +00:00
}
2016-10-29 08:24:21 +00:00
p . removeArg ( c , c . Ret )
2015-12-31 14:24:08 +00:00
}