mirror of
https://github.com/reactos/syzkaller.git
synced 2024-12-03 00:46:35 +00:00
7690667267
Currently to add a new resource one needs to modify multiple source files, which complicates descirption of new system calls. Move resource descriptions from source code to text desciptions.
532 lines
9.0 KiB
Go
532 lines
9.0 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 Type interface {
|
|
Name() string
|
|
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
|
|
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
|
|
}
|
|
|
|
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 FileoffType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
File string
|
|
}
|
|
|
|
func (t FileoffType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t FileoffType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type BufferKind int
|
|
|
|
const (
|
|
BufferBlob BufferKind = iota
|
|
BufferString
|
|
BufferSockaddr
|
|
BufferFilesystem
|
|
BufferAlgType
|
|
BufferAlgName
|
|
)
|
|
|
|
type BufferType struct {
|
|
TypeCommon
|
|
Kind BufferKind
|
|
}
|
|
|
|
func (t BufferType) Size() uintptr {
|
|
switch t.Kind {
|
|
case BufferAlgType:
|
|
return 14
|
|
case BufferAlgName:
|
|
return 64
|
|
default:
|
|
panic(fmt.Sprintf("buffer size is not statically known: %v", t.Name()))
|
|
}
|
|
}
|
|
|
|
func (t BufferType) Align() uintptr {
|
|
return 1
|
|
}
|
|
|
|
type VmaType struct {
|
|
TypeCommon
|
|
}
|
|
|
|
func (t VmaType) Size() uintptr {
|
|
return ptrSize
|
|
}
|
|
|
|
func (t VmaType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type LenType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
ByteSize bool // want size in 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
|
|
Vals []uintptr
|
|
}
|
|
|
|
func (t FlagsType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t FlagsType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type ConstType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
Val uintptr
|
|
IsPad bool
|
|
}
|
|
|
|
func (t ConstType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t ConstType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type StrConstType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
Val string
|
|
}
|
|
|
|
func (t StrConstType) Size() uintptr {
|
|
return ptrSize
|
|
}
|
|
|
|
func (t StrConstType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type IntKind int
|
|
|
|
const (
|
|
IntPlain IntKind = iota
|
|
IntSignalno
|
|
IntInaddr
|
|
IntInport
|
|
IntRange
|
|
)
|
|
|
|
type IntType struct {
|
|
TypeCommon
|
|
TypeSize uintptr
|
|
Kind IntKind
|
|
RangeBegin int64
|
|
RangeEnd int64
|
|
}
|
|
|
|
func (t IntType) Size() uintptr {
|
|
return t.TypeSize
|
|
}
|
|
|
|
func (t IntType) Align() uintptr {
|
|
return t.Size()
|
|
}
|
|
|
|
type FilenameType struct {
|
|
TypeCommon
|
|
}
|
|
|
|
func (t FilenameType) Size() uintptr {
|
|
panic("filename size is not statically known")
|
|
}
|
|
|
|
func (t FilenameType) Align() uintptr {
|
|
return 1
|
|
}
|
|
|
|
type ArrayType struct {
|
|
TypeCommon
|
|
Type Type
|
|
Len uintptr // 0 if variable-length, unused for now
|
|
}
|
|
|
|
func (t ArrayType) Size() uintptr {
|
|
if t.Len == 0 {
|
|
return 0 // for trailing embed arrays
|
|
}
|
|
return t.Len * t.Type.Size()
|
|
}
|
|
|
|
func (t ArrayType) Align() uintptr {
|
|
return t.Type.Align()
|
|
}
|
|
|
|
type PtrType struct {
|
|
TypeCommon
|
|
Type Type
|
|
Dir Dir
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
type Dir int
|
|
|
|
const (
|
|
DirIn Dir = iota
|
|
DirOut
|
|
DirInOut
|
|
)
|
|
|
|
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
|
|
// Recurse into arguments to see if there is an out/inout arg of necessary type.
|
|
var checkArg func(typ Type, dir Dir) bool
|
|
checkArg = func(typ Type, dir Dir) bool {
|
|
if resarg, ok := typ.(ResourceType); ok && dir != DirIn && isCompatibleResource(kind, resarg.Desc.Kind, precise) {
|
|
return true
|
|
}
|
|
switch typ1 := typ.(type) {
|
|
case ArrayType:
|
|
if checkArg(typ1.Type, dir) {
|
|
return true
|
|
}
|
|
case StructType:
|
|
for _, fld := range typ1.Fields {
|
|
if checkArg(fld, dir) {
|
|
return true
|
|
}
|
|
}
|
|
case UnionType:
|
|
for _, opt := range typ1.Options {
|
|
if checkArg(opt, dir) {
|
|
return true
|
|
}
|
|
}
|
|
case PtrType:
|
|
if checkArg(typ1.Type, typ1.Dir) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
for _, meta := range Calls {
|
|
ok := false
|
|
for _, arg := range meta.Args {
|
|
if checkArg(arg, DirIn) {
|
|
ok = true
|
|
break
|
|
}
|
|
}
|
|
if !ok && meta.Ret != nil && checkArg(meta.Ret, DirOut) {
|
|
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
|
|
var checkArg func(typ Type, dir Dir)
|
|
checkArg = func(typ Type, dir Dir) {
|
|
switch typ1 := typ.(type) {
|
|
case ResourceType:
|
|
if dir != DirOut && !typ1.IsOptional {
|
|
resources = append(resources, typ1)
|
|
}
|
|
case ArrayType:
|
|
checkArg(typ1.Type, dir)
|
|
case PtrType:
|
|
checkArg(typ1.Type, typ1.Dir)
|
|
case StructType:
|
|
for _, fld := range typ1.Fields {
|
|
checkArg(fld, dir)
|
|
}
|
|
case UnionType:
|
|
for _, opt := range typ1.Options {
|
|
checkArg(opt, dir)
|
|
}
|
|
}
|
|
}
|
|
for _, arg := range c.Args {
|
|
checkArg(arg, DirIn)
|
|
}
|
|
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)
|
|
for c := range enabled {
|
|
if !supported[c] {
|
|
continue
|
|
}
|
|
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
|
|
}
|
|
}
|
|
if !canCreate {
|
|
delete(supported, c)
|
|
}
|
|
}
|
|
if n == len(supported) {
|
|
break
|
|
}
|
|
}
|
|
return supported
|
|
}
|
|
|
|
var (
|
|
Calls []*Call
|
|
CallCount int
|
|
CallMap = make(map[string]*Call)
|
|
CallID = make(map[string]int)
|
|
)
|
|
|
|
func init() {
|
|
initCalls()
|
|
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)
|
|
}
|