Golang程序  |  585行  |  10.2 KB

// 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 prog

import (
	"fmt"
)

type Syscall struct {
	ID       int
	NR       uint64 // kernel syscall number
	Name     string
	CallName string
	Args     []Type
	Ret      Type
}

type Dir int

const (
	DirIn Dir = iota
	DirOut
	DirInOut
)

func (dir Dir) String() string {
	switch dir {
	case DirIn:
		return "in"
	case DirOut:
		return "out"
	case DirInOut:
		return "inout"
	default:
		panic("unknown dir")
	}
}

type BinaryFormat int

const (
	FormatNative BinaryFormat = iota
	FormatBigEndian
	FormatStrDec
	FormatStrHex
	FormatStrOct
)

type Type interface {
	String() string
	Name() string
	FieldName() string
	Dir() Dir
	Optional() bool
	Varlen() bool
	Size() uint64
	Format() BinaryFormat
	BitfieldOffset() uint64
	BitfieldLength() uint64
	BitfieldMiddle() bool // returns true for all but last bitfield in a group

	makeDefaultArg() Arg
	isDefaultArg(arg Arg) bool
	generate(r *randGen, s *state) (arg Arg, calls []*Call)
	mutate(r *randGen, s *state, arg Arg, ctx ArgCtx) (calls []*Call, retry, preserve bool)
	minimize(ctx *minimizeArgsCtx, arg Arg, path string) 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
	TypeSize   uint64 // static size of the type, or 0 for variable size types
	ArgDir     Dir
	IsOptional bool
	IsVarlen   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) Size() uint64 {
	if t.IsVarlen {
		panic(fmt.Sprintf("static type size is not known: %#v", t))
	}
	return t.TypeSize
}

func (t *TypeCommon) Varlen() bool {
	return t.IsVarlen
}

func (t *TypeCommon) Format() BinaryFormat {
	return FormatNative
}

func (t *TypeCommon) BitfieldOffset() uint64 {
	return 0
}

func (t *TypeCommon) BitfieldLength() uint64 {
	return 0
}

func (t *TypeCommon) BitfieldMiddle() bool {
	return false
}

func (t TypeCommon) Dir() Dir {
	return t.ArgDir
}

type ResourceDesc struct {
	Name   string
	Type   Type
	Kind   []string
	Values []uint64
}

type ResourceType struct {
	TypeCommon
	ArgFormat BinaryFormat
	Desc      *ResourceDesc
}

func (t *ResourceType) String() string {
	return t.Name()
}

func (t *ResourceType) makeDefaultArg() Arg {
	return MakeResultArg(t, nil, t.Default())
}

func (t *ResourceType) isDefaultArg(arg Arg) bool {
	a := arg.(*ResultArg)
	return a.Res == nil && a.OpDiv == 0 && a.OpAdd == 0 &&
		len(a.uses) == 0 && a.Val == t.Default()
}

func (t *ResourceType) Default() uint64 {
	return t.Desc.Values[0]
}

func (t *ResourceType) SpecialValues() []uint64 {
	return t.Desc.Values
}

func (t *ResourceType) Format() BinaryFormat {
	return t.ArgFormat
}

type IntTypeCommon struct {
	TypeCommon
	ArgFormat   BinaryFormat
	BitfieldOff uint64
	BitfieldLen uint64
	BitfieldMdl bool
}

func (t *IntTypeCommon) String() string {
	return t.Name()
}

func (t *IntTypeCommon) Format() BinaryFormat {
	return t.ArgFormat
}

func (t *IntTypeCommon) BitfieldOffset() uint64 {
	return t.BitfieldOff
}

func (t *IntTypeCommon) BitfieldLength() uint64 {
	return t.BitfieldLen
}

func (t *IntTypeCommon) BitfieldMiddle() bool {
	return t.BitfieldMdl
}

type ConstType struct {
	IntTypeCommon
	Val   uint64
	IsPad bool
}

func (t *ConstType) makeDefaultArg() Arg {
	return MakeConstArg(t, t.Val)
}

func (t *ConstType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == t.Val
}

func (t *ConstType) String() string {
	if t.IsPad {
		return fmt.Sprintf("pad[%v]", t.Size())
	}
	return fmt.Sprintf("const[%v, %v]", t.Val, t.IntTypeCommon.String())
}

type IntKind int

const (
	IntPlain   IntKind = iota
	IntFileoff         // offset within a file
	IntRange
)

type IntType struct {
	IntTypeCommon
	Kind       IntKind
	RangeBegin uint64
	RangeEnd   uint64
}

func (t *IntType) makeDefaultArg() Arg {
	return MakeConstArg(t, 0)
}

func (t *IntType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == 0
}

type FlagsType struct {
	IntTypeCommon
	Vals    []uint64
	BitMask bool
}

func (t *FlagsType) makeDefaultArg() Arg {
	return MakeConstArg(t, 0)
}

func (t *FlagsType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == 0
}

type LenType struct {
	IntTypeCommon
	BitSize uint64 // want size in multiple of bits instead of array size
	Buf     string
}

func (t *LenType) makeDefaultArg() Arg {
	return MakeConstArg(t, 0)
}

func (t *LenType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == 0
}

type ProcType struct {
	IntTypeCommon
	ValuesStart   uint64
	ValuesPerProc uint64
}

const (
	MaxPids          = 32
	procDefaultValue = 0xffffffffffffffff // special value denoting 0 for all procs
)

func (t *ProcType) makeDefaultArg() Arg {
	return MakeConstArg(t, procDefaultValue)
}

func (t *ProcType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == procDefaultValue
}

type CsumKind int

const (
	CsumInet CsumKind = iota
	CsumPseudo
)

type CsumType struct {
	IntTypeCommon
	Kind     CsumKind
	Buf      string
	Protocol uint64 // for CsumPseudo
}

func (t *CsumType) String() string {
	return "csum"
}

func (t *CsumType) makeDefaultArg() Arg {
	return MakeConstArg(t, 0)
}

func (t *CsumType) isDefaultArg(arg Arg) bool {
	return arg.(*ConstArg).Val == 0
}

type VmaType struct {
	TypeCommon
	RangeBegin uint64 // in pages
	RangeEnd   uint64
}

func (t *VmaType) String() string {
	return "vma"
}

func (t *VmaType) makeDefaultArg() Arg {
	return MakeNullPointerArg(t)
}

func (t *VmaType) isDefaultArg(arg Arg) bool {
	return arg.(*PointerArg).IsNull()
}

type BufferKind int

const (
	BufferBlobRand BufferKind = iota
	BufferBlobRange
	BufferString
	BufferFilename
	BufferText
)

type TextKind int

const (
	TextX86Real TextKind = iota
	TextX86bit16
	TextX86bit32
	TextX86bit64
	TextArm64
)

type BufferType struct {
	TypeCommon
	Kind       BufferKind
	RangeBegin uint64   // for BufferBlobRange kind
	RangeEnd   uint64   // for BufferBlobRange kind
	Text       TextKind // for BufferText
	SubKind    string
	Values     []string // possible values for BufferString kind
	NoZ        bool     // non-zero terminated BufferString/BufferFilename
}

func (t *BufferType) String() string {
	return "buffer"
}

func (t *BufferType) makeDefaultArg() Arg {
	if t.Dir() == DirOut {
		var sz uint64
		if !t.Varlen() {
			sz = t.Size()
		}
		return MakeOutDataArg(t, sz)
	}
	var data []byte
	if !t.Varlen() {
		data = make([]byte, t.Size())
	}
	return MakeDataArg(t, data)
}

func (t *BufferType) isDefaultArg(arg Arg) bool {
	a := arg.(*DataArg)
	if a.Size() == 0 {
		return true
	}
	if a.Type().Varlen() {
		return false
	}
	if a.Type().Dir() == DirOut {
		return true
	}
	for _, v := range a.Data() {
		if v != 0 {
			return false
		}
	}
	return true
}

type ArrayKind int

const (
	ArrayRandLen ArrayKind = iota
	ArrayRangeLen
)

type ArrayType struct {
	TypeCommon
	Type       Type
	Kind       ArrayKind
	RangeBegin uint64
	RangeEnd   uint64
}

func (t *ArrayType) String() string {
	return fmt.Sprintf("array[%v]", t.Type.String())
}

func (t *ArrayType) makeDefaultArg() Arg {
	var elems []Arg
	if t.Kind == ArrayRangeLen && t.RangeBegin == t.RangeEnd {
		for i := uint64(0); i < t.RangeBegin; i++ {
			elems = append(elems, t.Type.makeDefaultArg())
		}
	}
	return MakeGroupArg(t, elems)
}

func (t *ArrayType) isDefaultArg(arg Arg) bool {
	a := arg.(*GroupArg)
	if !a.fixedInnerSize() && len(a.Inner) != 0 {
		return false
	}
	for _, elem := range a.Inner {
		if !isDefault(elem) {
			return false
		}
	}
	return true
}

type PtrType struct {
	TypeCommon
	Type Type
}

func (t *PtrType) String() string {
	return fmt.Sprintf("ptr[%v, %v]", t.Dir(), t.Type.String())
}

func (t *PtrType) makeDefaultArg() Arg {
	if t.Optional() {
		return MakeNullPointerArg(t)
	}
	return MakePointerArg(t, 0, t.Type.makeDefaultArg())
}

func (t *PtrType) isDefaultArg(arg Arg) bool {
	a := arg.(*PointerArg)
	if t.Optional() {
		return a.IsNull()
	}
	return a.Address == 0 && isDefault(a.Res)
}

type StructType struct {
	Key     StructKey
	FldName string
	*StructDesc
}

func (t *StructType) String() string {
	return t.Name()
}

func (t *StructType) FieldName() string {
	return t.FldName
}

func (t *StructType) makeDefaultArg() Arg {
	inner := make([]Arg, len(t.Fields))
	for i, field := range t.Fields {
		inner[i] = field.makeDefaultArg()
	}
	return MakeGroupArg(t, inner)
}

func (t *StructType) isDefaultArg(arg Arg) bool {
	a := arg.(*GroupArg)
	for _, elem := range a.Inner {
		if !isDefault(elem) {
			return false
		}
	}
	return true
}

type UnionType struct {
	Key     StructKey
	FldName string
	*StructDesc
}

func (t *UnionType) String() string {
	return t.Name()
}

func (t *UnionType) FieldName() string {
	return t.FldName
}

func (t *UnionType) makeDefaultArg() Arg {
	return MakeUnionArg(t, t.Fields[0].makeDefaultArg())
}

func (t *UnionType) isDefaultArg(arg Arg) bool {
	a := arg.(*UnionArg)
	return a.Option.Type().FieldName() == t.Fields[0].FieldName() && isDefault(a.Option)
}

type StructDesc struct {
	TypeCommon
	Fields    []Type
	AlignAttr uint64
}

func (t *StructDesc) FieldName() string {
	panic("must not be called")
}

type StructKey struct {
	Name string
	Dir  Dir
}

type KeyedStruct struct {
	Key  StructKey
	Desc *StructDesc
}

type ConstValue struct {
	Name  string
	Value uint64
}

func ForeachType(meta *Syscall, f func(Type)) {
	seen := make(map[*StructDesc]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.StructDesc] {
				return // prune recursion via pointers to structs/unions
			}
			seen[a.StructDesc] = true
			for _, f := range a.Fields {
				rec(f)
			}
		case *UnionType:
			if seen[a.StructDesc] {
				return // prune recursion via pointers to structs/unions
			}
			seen[a.StructDesc] = true
			for _, opt := range a.Fields {
				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)
	}
}