syzkaller/sys/decl.go
2017-06-12 19:48:23 +02:00

710 lines
13 KiB
Go

// Copyright 2015/2016 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 sys
import (
"fmt"
)
const ptrSize = 8
type Call struct {
ID int
NR int // kernel syscall number
Name string
CallName string
Native bool // real of fake syscall
Args []Type
Ret Type
}
type Dir int
const (
DirIn Dir = iota
DirOut
DirInOut
)
type Type interface {
Name() string
FieldName() string
Dir() Dir
Optional() bool
Default() uintptr
Varlen() bool
Size() uintptr
Align() uintptr
BitfieldOffset() uintptr
BitfieldLength() uintptr
BitfieldLast() bool
}
func IsPad(t Type) bool {
if ct, ok := t.(*ConstType); ok && ct.IsPad {
return true
}
return false
}
type TypeCommon struct {
TypeName string
FldName string // for struct fields and named args
ArgDir Dir
IsOptional bool
}
func (t *TypeCommon) Name() string {
return t.TypeName
}
func (t *TypeCommon) FieldName() string {
return t.FldName
}
func (t *TypeCommon) Optional() bool {
return t.IsOptional
}
func (t *TypeCommon) Default() uintptr {
return 0
}
func (t *TypeCommon) Varlen() bool {
return false
}
func (t *TypeCommon) BitfieldOffset() uintptr {
return 0
}
func (t *TypeCommon) BitfieldLength() uintptr {
return 0
}
func (t *TypeCommon) BitfieldLast() bool {
return false
}
func (t TypeCommon) Dir() Dir {
return t.ArgDir
}
const (
InvalidFD = ^uintptr(0)
)
type ResourceDesc struct {
Name string
Type Type
Kind []string
Values []uintptr
}
type ResourceType struct {
TypeCommon
Desc *ResourceDesc
}
func (t *ResourceType) Default() uintptr {
return t.Desc.Values[0]
}
func (t *ResourceType) SpecialValues() []uintptr {
return t.Desc.Values
}
func (t *ResourceType) Size() uintptr {
return t.Desc.Type.Size()
}
func (t *ResourceType) Align() uintptr {
return t.Desc.Type.Align()
}
type IntTypeCommon struct {
TypeCommon
TypeSize uintptr
BigEndian bool
BitfieldOff uintptr
BitfieldLen uintptr
BitfieldLst bool
}
func (t *IntTypeCommon) Size() uintptr {
return t.TypeSize
}
func (t *IntTypeCommon) Align() uintptr {
return t.Size()
}
func (t *IntTypeCommon) BitfieldOffset() uintptr {
return t.BitfieldOff
}
func (t *IntTypeCommon) BitfieldLength() uintptr {
return t.BitfieldLen
}
func (t *IntTypeCommon) BitfieldLast() bool {
return t.BitfieldLst
}
type ConstType struct {
IntTypeCommon
Val uintptr
IsPad bool
}
type IntKind int
const (
IntPlain IntKind = iota
IntSignalno
IntFileoff // offset within a file
IntRange
)
type IntType struct {
IntTypeCommon
Kind IntKind
RangeBegin int64
RangeEnd int64
}
type FlagsType struct {
IntTypeCommon
Vals []uintptr
}
type LenType struct {
IntTypeCommon
ByteSize uintptr // want size in multiple of bytes instead of array size
Buf string
}
type ProcType struct {
IntTypeCommon
ValuesStart int64
ValuesPerProc uint64
}
type CsumKind int
const (
CsumInet CsumKind = iota
CsumPseudo
)
type CsumType struct {
IntTypeCommon
Kind CsumKind
Buf string
Protocol uint64 // for CsumPseudo
}
type VmaType struct {
TypeCommon
RangeBegin int64 // in pages
RangeEnd int64
}
func (t *VmaType) Size() uintptr {
return ptrSize
}
func (t *VmaType) Align() uintptr {
return t.Size()
}
type BufferKind int
const (
BufferBlobRand BufferKind = iota
BufferBlobRange
BufferString
BufferFilename
BufferText
)
type TextKind int
const (
Text_x86_real TextKind = iota
Text_x86_16
Text_x86_32
Text_x86_64
Text_arm64
)
type BufferType struct {
TypeCommon
Kind BufferKind
RangeBegin uintptr // for BufferBlobRange kind
RangeEnd uintptr // for BufferBlobRange kind
Text TextKind // for BufferText
SubKind string
Values []string // possible values for BufferString kind
Length uintptr // max string length for BufferString kind
}
func (t *BufferType) Varlen() bool {
switch t.Kind {
case BufferBlobRand:
return true
case BufferBlobRange:
return t.RangeBegin != t.RangeEnd
case BufferString:
return t.Length == 0
case BufferFilename:
return true
case BufferText:
return true
default:
panic("bad buffer kind")
}
}
func (t *BufferType) Size() uintptr {
if t.Varlen() {
panic(fmt.Sprintf("buffer size is not statically known: %v", t.Name()))
}
switch t.Kind {
case BufferString:
return t.Length
case BufferBlobRange:
return t.RangeBegin
default:
panic("bad buffer kind")
}
}
func (t *BufferType) Align() uintptr {
return 1
}
type ArrayKind int
const (
ArrayRandLen ArrayKind = iota
ArrayRangeLen
)
type ArrayType struct {
TypeCommon
Type Type
Kind ArrayKind
RangeBegin uintptr
RangeEnd uintptr
}
func (t *ArrayType) Varlen() bool {
switch t.Kind {
case ArrayRandLen:
return true
case ArrayRangeLen:
return t.RangeBegin != t.RangeEnd
default:
panic("bad array kind")
}
}
func (t *ArrayType) Size() uintptr {
if t.Varlen() {
panic(fmt.Sprintf("array size is not statically known: %v", t.Name()))
}
switch t.Kind {
case ArrayRangeLen:
return t.RangeBegin * t.Type.Size()
default:
panic("bad array type")
}
}
func (t *ArrayType) Align() uintptr {
return t.Type.Align()
}
type PtrType struct {
TypeCommon
Type Type
}
func (t *PtrType) Size() uintptr {
return ptrSize
}
func (t *PtrType) Align() uintptr {
return t.Size()
}
type StructType struct {
TypeCommon
Fields []Type
padded bool
packed bool
align uintptr
varlen bool
varlenAssigned bool
}
func (t *StructType) Varlen() bool {
if t.varlenAssigned {
return t.varlen
}
for _, f := range t.Fields {
if f.Varlen() {
t.varlen = true
t.varlenAssigned = true
return t.varlen
}
}
t.varlen = false
t.varlenAssigned = true
return t.varlen
}
func (t *StructType) Size() uintptr {
if t.Varlen() {
panic(fmt.Sprintf("struct size is not statically known: %v", t.Name()))
}
if !t.padded {
panic("struct is not padded yet")
}
var size uintptr
for _, f := range t.Fields {
if f.BitfieldLength() == 0 || f.BitfieldLast() {
size += f.Size()
}
}
return size
}
func (t *StructType) Align() uintptr {
if t.align != 0 {
return t.align // overrided by user attribute
}
if t.packed {
return 1
}
var align uintptr
for _, f := range t.Fields {
if a1 := f.Align(); align < a1 {
align = a1
}
}
return align
}
type UnionType struct {
TypeCommon
Options []Type
varlen bool // provided by user
}
func (t *UnionType) Varlen() bool {
return t.varlen
}
func (t *UnionType) Size() uintptr {
if t.Varlen() {
panic(fmt.Sprintf("union size is not statically known: %v", t.Name()))
}
size := t.Options[0].Size()
for _, opt := range t.Options {
if size < opt.Size() {
size = opt.Size()
}
}
return size
}
func (t *UnionType) Align() uintptr {
var align uintptr
for _, opt := range t.Options {
if a1 := opt.Align(); align < a1 {
align = a1
}
}
return align
}
var (
CallMap = make(map[string]*Call)
structs map[string]Type
keyedStructs map[structKey]Type
Resources map[string]*ResourceDesc
ctors = make(map[string][]*Call)
)
type structKey struct {
name string
field string
dir Dir
}
func getStruct(key structKey) Type {
if structs == nil {
structs = make(map[string]Type)
keyedStructs = make(map[structKey]Type)
for _, str := range structArray {
structs[str.Name()] = str
}
}
str := keyedStructs[key]
if str == nil {
proto := structs[key.name]
if proto == nil {
panic(fmt.Sprintf("missing struct prototype for %v", key.name))
}
switch typed := proto.(type) {
case *StructType:
newStr := new(StructType)
*newStr = *typed
newStr.FldName = key.field
newStr.ArgDir = key.dir
str = newStr
case *UnionType:
newStr := new(UnionType)
*newStr = *typed
newStr.FldName = key.field
newStr.ArgDir = key.dir
str = newStr
default:
panic(fmt.Sprintf("unexpected type of struct prototype for %v: %+v", key.name, proto))
}
keyedStructs[key] = str
}
return str
}
func initStructFields() {
missed := 0
for _, f := range structFields {
untyped := keyedStructs[f.key]
if untyped == nil {
missed++
continue
}
switch str := untyped.(type) {
case *StructType:
str.Fields = f.fields
case *UnionType:
str.Options = f.fields
default:
panic(fmt.Sprintf("unexpected type of struct prototype for %v: %+v", f.key.name, untyped))
}
}
}
func resource(name string) *ResourceDesc {
if Resources == nil {
// This is first called during init of sys package, so does not need to be thread-safe.
// resourceArray is in sys_GOARCH.go (generated by sysgen).
Resources = make(map[string]*ResourceDesc)
for _, res := range resourceArray {
Resources[res.Name] = res
}
}
return Resources[name]
}
// ResourceConstructors returns a list of calls that can create a resource of the given kind.
func ResourceConstructors(name string) []*Call {
return ctors[name]
}
func initResources() {
resource("") // init resources, if it's not done yet
for _, res := range resourceArray {
ctors[res.Name] = resourceCtors(res.Kind, false)
}
}
func resourceCtors(kind []string, precise bool) []*Call {
// Find calls that produce the necessary resources.
var metas []*Call
for _, meta := range Calls {
// Recurse into arguments to see if there is an out/inout arg of necessary type.
ok := false
ForeachType(meta, func(typ Type) {
if ok {
return
}
switch typ1 := typ.(type) {
case *ResourceType:
if typ1.Dir() != DirIn && isCompatibleResource(kind, typ1.Desc.Kind, precise) {
ok = true
}
}
})
if ok {
metas = append(metas, meta)
}
}
return metas
}
// IsCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
func IsCompatibleResource(dst, src string) bool {
dstRes := Resources[dst]
if dstRes == nil {
panic(fmt.Sprintf("unknown resource '%v'", dst))
}
srcRes := Resources[src]
if srcRes == nil {
panic(fmt.Sprintf("unknown resource '%v'", src))
}
return isCompatibleResource(dstRes.Kind, srcRes.Kind, false)
}
// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
// If precise is true, then it does not allow passing a less specialized resource (e.g. fd)
// as a more specialized resource (e.g. socket). Otherwise it does.
func isCompatibleResource(dst, src []string, precise bool) bool {
if len(dst) > len(src) {
// dst is more specialized, e.g dst=socket, src=fd.
if precise {
return false
}
dst = dst[:len(src)]
}
if len(src) > len(dst) {
// src is more specialized, e.g dst=fd, src=socket.
src = src[:len(dst)]
}
for i, k := range dst {
if k != src[i] {
return false
}
}
return true
}
func (c *Call) InputResources() []*ResourceType {
var resources []*ResourceType
ForeachType(c, func(typ Type) {
switch typ1 := typ.(type) {
case *ResourceType:
if typ1.Dir() != DirOut && !typ1.IsOptional {
resources = append(resources, typ1)
}
}
})
return resources
}
func TransitivelyEnabledCalls(enabled map[*Call]bool) map[*Call]bool {
supported := make(map[*Call]bool)
for c := range enabled {
supported[c] = true
}
inputResources := make(map[*Call][]*ResourceType)
ctors := make(map[string][]*Call)
for c := range supported {
inputs := c.InputResources()
inputResources[c] = inputs
for _, res := range inputs {
if _, ok := ctors[res.Desc.Name]; ok {
continue
}
ctors[res.Desc.Name] = resourceCtors(res.Desc.Kind, true)
}
}
for {
n := len(supported)
haveGettime := supported[CallMap["clock_gettime"]]
for c := range supported {
canCreate := true
for _, res := range inputResources[c] {
noctors := true
for _, ctor := range ctors[res.Desc.Name] {
if supported[ctor] {
noctors = false
break
}
}
if noctors {
canCreate = false
break
}
}
// We need to support structs as resources,
// but for now we just special-case timespec/timeval.
if canCreate && !haveGettime {
ForeachType(c, func(typ Type) {
if a, ok := typ.(*StructType); ok && a.Dir() != DirOut && (a.Name() == "timespec" || a.Name() == "timeval") {
canCreate = false
}
})
}
if !canCreate {
delete(supported, c)
}
}
if n == len(supported) {
break
}
}
return supported
}
func ForeachType(meta *Call, f func(Type)) {
seen := make(map[Type]bool)
var rec func(t Type)
rec = func(t Type) {
f(t)
switch a := t.(type) {
case *PtrType:
rec(a.Type)
case *ArrayType:
rec(a.Type)
case *StructType:
if seen[a] {
return // prune recursion via pointers to structs/unions
}
seen[a] = true
for _, f := range a.Fields {
rec(f)
}
case *UnionType:
if seen[a] {
return // prune recursion via pointers to structs/unions
}
seen[a] = true
for _, opt := range a.Options {
rec(opt)
}
case *ResourceType, *BufferType, *VmaType, *LenType,
*FlagsType, *ConstType, *IntType, *ProcType, *CsumType:
default:
panic("unknown type")
}
}
for _, t := range meta.Args {
rec(t)
}
if meta.Ret != nil {
rec(meta.Ret)
}
}
func init() {
initStructFields()
initResources()
initAlign()
keyedStructs = nil
structs = nil
for i, c := range Calls {
c.ID = i
if CallMap[c.Name] != nil {
println(c.Name)
panic("duplicate syscall")
}
CallMap[c.Name] = c
}
}