blob: 68fbe1599e9998698ef1a7798b036adc49c31366 [file] [log] [blame]
// Copyright 2017 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"
)
func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall {
// Find calls that produce the necessary resources.
var metas []*Syscall
for _, meta := range target.Syscalls {
// 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 && isCompatibleResourceImpl(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 (target *Target) isCompatibleResource(dst, src string) bool {
if dst == target.any.res16.TypeName ||
dst == target.any.res32.TypeName ||
dst == target.any.res64.TypeName {
return true
}
dstRes := target.resourceMap[dst]
if dstRes == nil {
panic(fmt.Sprintf("unknown resource '%v'", dst))
}
srcRes := target.resourceMap[src]
if srcRes == nil {
panic(fmt.Sprintf("unknown resource '%v'", src))
}
return isCompatibleResourceImpl(dstRes.Kind, srcRes.Kind, false)
}
// isCompatibleResourceImpl 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 isCompatibleResourceImpl(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 *Syscall) 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 (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) {
supported := make(map[*Syscall]bool)
disabled := make(map[*Syscall]string)
for c := range enabled {
supported[c] = true
}
inputResources := make(map[*Syscall][]*ResourceType)
ctors := make(map[string][]*Syscall)
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] = target.calcResourceCtors(res.Desc.Kind, true)
}
}
for {
n := len(supported)
haveGettime := supported[target.SyscallMap["clock_gettime"]]
for c := range supported {
cantCreate := ""
var resourceCtors []*Syscall
for _, res := range inputResources[c] {
noctors := true
for _, ctor := range ctors[res.Desc.Name] {
if supported[ctor] {
noctors = false
break
}
}
if noctors {
cantCreate = res.Desc.Name
resourceCtors = ctors[res.Desc.Name]
break
}
}
// We need to support structs as resources,
// but for now we just special-case timespec/timeval.
if cantCreate == "" && !haveGettime && target.SyscallMap["clock_gettime"] != nil {
ForeachType(c, func(typ Type) {
if a, ok := typ.(*StructType); ok && a.Dir() != DirOut &&
(a.Name() == "timespec" || a.Name() == "timeval") {
cantCreate = a.Name()
resourceCtors = []*Syscall{target.SyscallMap["clock_gettime"]}
}
})
}
if cantCreate != "" {
delete(supported, c)
var ctorNames []string
for _, ctor := range resourceCtors {
ctorNames = append(ctorNames, ctor.Name)
}
disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+
" enable some syscalls that can create it %v",
cantCreate, ctorNames)
}
}
if n == len(supported) {
break
}
}
return supported, disabled
}