mirror of
https://github.com/reactos/syzkaller.git
synced 2024-12-01 07:10:37 +00:00
bbd4840872
Add new pseudo syscall syz_kvm_setup_cpu that setups VCPU into interesting states for execution. KVM is too difficult to setup otherwise. Lots of improvements possible, but this is a starting point.
556 lines
9.7 KiB
Go
556 lines
9.7 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
|
|
CallID int
|
|
Name string
|
|
CallName string
|
|
Args []Type
|
|
Ret Type
|
|
}
|
|
|
|
type Dir int
|
|
|
|
const (
|
|
DirIn Dir = iota
|
|
DirOut
|
|
DirInOut
|
|
)
|
|
|
|
type Type interface {
|
|
Name() string
|
|
Dir() Dir
|
|
Optional() bool
|
|
Default() uintptr
|
|
Size() uintptr
|
|
Align() uintptr
|
|
}
|
|
|
|
func IsPad(t Type) bool {
|
|
if ct, ok := t.(*ConstType); ok && ct.IsPad {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
type TypeCommon struct {
|
|
TypeName string
|
|
ArgDir Dir
|
|
IsOptional bool
|
|
}
|
|
|
|
func (t *TypeCommon) Name() string {
|
|
return t.TypeName
|
|
}
|
|
|
|
func (t *TypeCommon) Optional() bool {
|
|
return t.IsOptional
|
|
}
|
|
|
|
func (t *TypeCommon) Default() uintptr {
|
|
return 0
|
|
}
|
|
|
|
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 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
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
func (t *BufferType) Size() uintptr {
|
|
switch t.Kind {
|
|
case BufferString:
|
|
size := 0
|
|
for _, s := range t.Values {
|
|
if size != 0 && size != len(s) {
|
|
size = 0
|
|
break
|
|
}
|
|
size = len(s)
|
|
}
|
|
if size != 0 {
|
|
return uintptr(size)
|
|
}
|
|
case BufferBlobRange:
|
|
if t.RangeBegin == t.RangeEnd {
|
|
return t.RangeBegin
|
|
}
|
|
}
|
|
panic(fmt.Sprintf("buffer size is not statically known: %v", t.Name()))
|
|
}
|
|
|
|
func (t *BufferType) Align() uintptr {
|
|
return 1
|
|
}
|
|
|
|
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 LenType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
BigEndian bool
|
|
ByteSize uintptr // want size in multiple of bytes instead of array size
|
|
Buf string
|
|
}
|
|
|
|
func (t *LenType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t *LenType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type FlagsType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
BigEndian bool
|
|
Vals []uintptr
|
|
}
|
|
|
|
func (t *FlagsType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t *FlagsType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type ConstType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
BigEndian bool
|
|
Val uintptr
|
|
IsPad bool
|
|
}
|
|
|
|
func (t *ConstType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t *ConstType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type IntKind int
|
|
|
|
const (
|
|
IntPlain IntKind = iota
|
|
IntSignalno
|
|
IntFileoff // offset within a file
|
|
IntRange
|
|
)
|
|
|
|
type IntType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
BigEndian bool
|
|
Kind IntKind
|
|
RangeBegin int64
|
|
RangeEnd int64
|
|
}
|
|
|
|
func (t *IntType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t *IntType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type ProcType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
BigEndian bool
|
|
ValuesStart int64
|
|
ValuesPerProc uint64
|
|
}
|
|
|
|
func (t *ProcType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t *ProcType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type ArrayKind int
|
|
|
|
const (
|
|
ArrayRandLen ArrayKind = iota
|
|
ArrayRangeLen
|
|
)
|
|
|
|
type ArrayType struct {
|
|
TypeCommon
|
|
Type Type
|
|
Kind ArrayKind
|
|
RangeBegin uintptr
|
|
RangeEnd uintptr
|
|
}
|
|
|
|
func (t *ArrayType) Size() uintptr {
|
|
if t.RangeBegin == t.RangeEnd {
|
|
return t.RangeBegin * t.Type.Size()
|
|
}
|
|
return 0 // for trailing embed arrays
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (t *StructType) Size() uintptr {
|
|
if !t.padded {
|
|
panic("struct is not padded yet")
|
|
}
|
|
var size uintptr
|
|
for _, f := range t.Fields {
|
|
size += f.Size()
|
|
}
|
|
return size
|
|
}
|
|
|
|
func (t *StructType) Align() uintptr {
|
|
if t.align != 0 {
|
|
return t.align // overrided by user attribute
|
|
}
|
|
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
|
|
}
|
|
|
|
func (t *UnionType) Size() uintptr {
|
|
if t.varlen {
|
|
panic("union size is not statically known")
|
|
}
|
|
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 ctors = make(map[string][]*Call)
|
|
|
|
// 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() {
|
|
for name, res := range Resources {
|
|
ctors[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
|
|
}
|
|
for {
|
|
n := len(supported)
|
|
haveGettime := supported[CallMap["clock_gettime"]]
|
|
for c := range supported {
|
|
canCreate := true
|
|
for _, res := range c.InputResources() {
|
|
noctors := true
|
|
for _, ctor := range resourceCtors(res.Desc.Kind, true) {
|
|
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:
|
|
default:
|
|
panic("unknown type")
|
|
}
|
|
}
|
|
for _, t := range meta.Args {
|
|
rec(t)
|
|
}
|
|
if meta.Ret != nil {
|
|
rec(meta.Ret)
|
|
}
|
|
}
|
|
|
|
var (
|
|
Calls []*Call
|
|
CallCount int
|
|
CallMap = make(map[string]*Call)
|
|
CallID = make(map[string]int)
|
|
)
|
|
|
|
func init() {
|
|
initCalls()
|
|
initStructFields()
|
|
initResources()
|
|
initAlign()
|
|
|
|
for i, c := range Calls {
|
|
c.ID = i
|
|
if CallMap[c.Name] != nil {
|
|
println(c.Name)
|
|
panic("duplicate syscall")
|
|
}
|
|
id, ok := CallID[c.CallName]
|
|
if !ok {
|
|
id = len(CallID)
|
|
CallID[c.CallName] = id
|
|
}
|
|
c.CallID = id
|
|
CallMap[c.Name] = c
|
|
}
|
|
CallCount = len(CallID)
|
|
}
|