// 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 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 } type ( ResourceKind int ResourceSubkind int ) const ( ResFD ResourceKind = iota ResIOCtx ResIPC ResKey ResInotifyDesc ResPid ResUid ResGid ResTimerid ResIocbPtr ResDrmCtx ) const ( ResAny ResourceSubkind = iota FdFile FdSock FdPipe FdSignal FdEvent FdTimer FdEpoll FdDir FdMq FdInotify FdFanotify FdTty FdDRI FdFuse FdKdbus FdBpfMap FdBpfProg FdPerf FdUserFault FdAlg FdAlgConn FdNfcRaw FdNfcLlcp FdBtHci FdBtSco FdBtL2cap FdBtRfcomm FdBtHidp FdBtCmtp FdBtBnep FdUnix FdSctp FdNetlink FdKvm FdKvmVm FdKvmCpu FdSndSeq FdSndTimer FdSndControl FdInputEvent FdTun FdRandom FdKcm FdNetRom IPCMsq IPCSem IPCShm ) func ResourceKinds() []ResourceKind { return []ResourceKind{ ResFD, ResIOCtx, ResIPC, ResKey, ResInotifyDesc, ResPid, ResUid, ResGid, ResTimerid, ResIocbPtr, } } func ResourceSubkinds(kind ResourceKind) []ResourceSubkind { switch kind { case ResFD: return []ResourceSubkind{ResAny, FdFile, FdSock, FdPipe, FdSignal, FdEvent, FdTimer, FdEpoll, FdDir, FdMq, FdInotify, FdFanotify, FdTty, FdDRI, FdFuse, FdKdbus, FdBpfMap, FdBpfProg, FdPerf, FdUserFault, FdAlg, FdAlgConn, FdNfcRaw, FdNfcLlcp, FdBtHci, FdBtSco, FdBtL2cap, FdBtRfcomm, FdBtHidp, FdBtCmtp, FdBtBnep, FdUnix, FdSctp, FdNetlink, FdKvm, FdKvmVm, FdKvmCpu, FdSndSeq, FdSndTimer, FdSndControl, FdInputEvent, FdTun, FdRandom, FdKcm, FdNetRom} case ResIPC: return []ResourceSubkind{IPCMsq, IPCSem, IPCShm} case ResIOCtx, ResKey, ResInotifyDesc, ResPid, ResUid, ResGid, ResTimerid, ResIocbPtr, ResDrmCtx: return []ResourceSubkind{ResAny} default: panic("unknown resource kind") } } func SocketSubkinds() []ResourceSubkind { return []ResourceSubkind{FdAlg, FdAlgConn, FdNfcRaw, FdNfcLlcp, FdBtHci, FdBtSco, FdBtL2cap, FdBtRfcomm, FdBtHidp, FdBtCmtp, FdBtBnep, FdUnix, FdSctp, FdNetlink} } const ( InvalidFD = ^uintptr(0) BogusFD = uintptr(100000 - 1) ) type ResourceType struct { TypeCommon Kind ResourceKind Subkind ResourceSubkind } func (t ResourceType) Default() uintptr { switch t.Kind { case ResFD: return InvalidFD case ResIOCtx: return 0 case ResIPC: return 0 case ResKey: return 0 case ResInotifyDesc: return 0 case ResPid: return 0 case ResUid: return 0 case ResGid: return 0 case ResTimerid: return 0 case ResDrmCtx: return 0 default: panic("unknown resource type") } } func (t ResourceType) SpecialValues() []uintptr { switch t.Kind { case ResFD: return []uintptr{InvalidFD, BogusFD, ^uintptr(0) - 99 /*AT_FDCWD*/} case ResIOCtx: return []uintptr{0} case ResIPC: return []uintptr{0, ^uintptr(0)} case ResKey: // KEY_SPEC_THREAD_KEYRING values return []uintptr{0, ^uintptr(0), ^uintptr(0) - 1, ^uintptr(0) - 2, ^uintptr(0) - 3, ^uintptr(0) - 4, ^uintptr(0) - 5, ^uintptr(0) - 6, ^uintptr(0) - 7} case ResInotifyDesc: return []uintptr{0} case ResPid: return []uintptr{0, ^uintptr(0)} case ResUid: return []uintptr{0, ^uintptr(0)} case ResGid: return []uintptr{0, ^uintptr(0)} case ResTimerid: return []uintptr{0} case ResDrmCtx: return []uintptr{0} default: panic("unknown resource kind") } } func (t ResourceType) Size() uintptr { switch t.Kind { case ResFD: return 4 case ResIOCtx: return 8 case ResIPC: return 4 case ResKey: return 4 case ResInotifyDesc: return 4 case ResPid: return 4 case ResUid: return 4 case ResGid: return 4 case ResTimerid: return 4 case ResDrmCtx: return 4 default: panic("unknown resource kind") } } func (t ResourceType) Align() uintptr { return t.Size() } func (t ResourceType) SubKinds() []ResourceSubkind { return ResourceSubkinds(t.Kind) } 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 ) type IntType struct { TypeCommon TypeSize uintptr Kind IntKind } 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[ResourceKind]map[ResourceSubkind][]*Call) // ResourceConstructors returns a list of calls that can create a resource of the given kind/subkind. func ResourceConstructors(kind ResourceKind, sk ResourceSubkind) []*Call { return ctors[kind][sk] } func initResources() { for _, kind := range ResourceKinds() { ctors[kind] = make(map[ResourceSubkind][]*Call) for _, sk := range ResourceSubkinds(kind) { ctors[kind][sk] = ResourceCtors(kind, sk, false) } } } func ResourceCtors(kind ResourceKind, sk ResourceSubkind, 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 && resarg.Kind == kind && (sk == resarg.Subkind || sk == ResAny || (resarg.Subkind == ResAny && !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 } 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.Kind, res.Subkind, 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 ( CallCount int CallMap = make(map[string]*Call) CallID = make(map[string]int) ) func init() { initCalls() initResources() initAlign() for _, c := range Calls { c.NR = numbers[c.ID] } for _, c := range Calls { 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) }