2017-09-05 14:32:32 +00:00
|
|
|
// 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"
|
|
|
|
)
|
|
|
|
|
2018-06-18 17:45:48 +00:00
|
|
|
// We need to support structs as resources,
|
|
|
|
// but for now we just special-case timespec/timeval.
|
|
|
|
var timespecRes = &ResourceDesc{
|
|
|
|
Name: "timespec",
|
|
|
|
Kind: []string{"timespec"},
|
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall {
|
2017-09-05 14:32:32 +00:00
|
|
|
// Find calls that produce the necessary resources.
|
|
|
|
var metas []*Syscall
|
2017-09-14 17:25:01 +00:00
|
|
|
for _, meta := range target.Syscalls {
|
2017-09-05 14:32:32 +00:00
|
|
|
// 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:
|
2017-09-14 17:25:01 +00:00
|
|
|
if typ1.Dir() != DirIn && isCompatibleResourceImpl(kind, typ1.Desc.Kind, precise) {
|
2017-09-05 14:32:32 +00:00
|
|
|
ok = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if ok {
|
|
|
|
metas = append(metas, meta)
|
|
|
|
}
|
|
|
|
}
|
2018-06-18 17:45:48 +00:00
|
|
|
if kind[0] == timespecRes.Name {
|
|
|
|
if c := target.SyscallMap["clock_gettime"]; c != nil {
|
|
|
|
metas = append(metas, c)
|
|
|
|
}
|
|
|
|
}
|
2017-09-05 14:32:32 +00:00
|
|
|
return metas
|
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
// 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 {
|
2018-03-21 11:18:36 +00:00
|
|
|
if dst == target.any.res16.TypeName ||
|
|
|
|
dst == target.any.res32.TypeName ||
|
2018-07-07 18:07:30 +00:00
|
|
|
dst == target.any.res64.TypeName ||
|
|
|
|
dst == target.any.resdec.TypeName ||
|
|
|
|
dst == target.any.reshex.TypeName ||
|
|
|
|
dst == target.any.resoct.TypeName {
|
2018-02-24 13:33:36 +00:00
|
|
|
return true
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
dstRes := target.resourceMap[dst]
|
2017-09-05 14:32:32 +00:00
|
|
|
if dstRes == nil {
|
|
|
|
panic(fmt.Sprintf("unknown resource '%v'", dst))
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
srcRes := target.resourceMap[src]
|
2017-09-05 14:32:32 +00:00
|
|
|
if srcRes == nil {
|
|
|
|
panic(fmt.Sprintf("unknown resource '%v'", src))
|
|
|
|
}
|
2017-09-14 17:25:01 +00:00
|
|
|
return isCompatibleResourceImpl(dstRes.Kind, srcRes.Kind, false)
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|
|
|
|
|
2017-09-14 17:25:01 +00:00
|
|
|
// isCompatibleResourceImpl returns true if resource of kind src can be passed as an argument of kind dst.
|
2017-09-05 14:32:32 +00:00
|
|
|
// 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.
|
2017-09-14 17:25:01 +00:00
|
|
|
func isCompatibleResourceImpl(dst, src []string, precise bool) bool {
|
2017-09-05 14:32:32 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-06-18 17:45:48 +00:00
|
|
|
func (target *Target) inputResources(c *Syscall) []*ResourceDesc {
|
|
|
|
var resources []*ResourceDesc
|
|
|
|
ForeachType(c, func(typ Type) {
|
|
|
|
if typ.Dir() == DirOut {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
switch typ1 := typ.(type) {
|
|
|
|
case *ResourceType:
|
|
|
|
if !typ1.IsOptional {
|
|
|
|
resources = append(resources, typ1.Desc)
|
|
|
|
}
|
|
|
|
case *StructType:
|
|
|
|
if target.OS == "linux" && (typ1.Name() == "timespec" || typ1.Name() == "timeval") {
|
|
|
|
resources = append(resources, timespecRes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
return resources
|
|
|
|
}
|
|
|
|
|
|
|
|
func (target *Target) outputResources(c *Syscall) []*ResourceDesc {
|
|
|
|
var resources []*ResourceDesc
|
2017-09-05 14:32:32 +00:00
|
|
|
ForeachType(c, func(typ Type) {
|
|
|
|
switch typ1 := typ.(type) {
|
|
|
|
case *ResourceType:
|
2018-06-18 17:45:48 +00:00
|
|
|
if typ1.Dir() != DirIn {
|
|
|
|
resources = append(resources, typ1.Desc)
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2018-06-18 17:45:48 +00:00
|
|
|
if c.CallName == "clock_gettime" {
|
|
|
|
resources = append(resources, timespecRes)
|
|
|
|
}
|
2017-09-05 14:32:32 +00:00
|
|
|
return resources
|
|
|
|
}
|
|
|
|
|
2018-04-06 17:43:06 +00:00
|
|
|
func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) {
|
2017-09-05 14:32:32 +00:00
|
|
|
supported := make(map[*Syscall]bool)
|
2018-04-06 17:43:06 +00:00
|
|
|
disabled := make(map[*Syscall]string)
|
2018-06-18 17:45:48 +00:00
|
|
|
canCreate := make(map[string]bool)
|
|
|
|
inputResources := make(map[*Syscall][]*ResourceDesc)
|
2017-09-05 14:32:32 +00:00
|
|
|
for c := range enabled {
|
2018-06-18 17:45:48 +00:00
|
|
|
inputResources[c] = target.inputResources(c)
|
2018-07-07 18:07:30 +00:00
|
|
|
|
|
|
|
if c.Name == "pipe$9p" {
|
|
|
|
fmt.Printf("%v: input resource: %+v\n", c.Name, inputResources[c])
|
|
|
|
}
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|
|
|
|
for {
|
|
|
|
n := len(supported)
|
2018-06-18 17:45:48 +00:00
|
|
|
for c := range enabled {
|
|
|
|
if supported[c] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
ready := true
|
2017-09-05 14:32:32 +00:00
|
|
|
for _, res := range inputResources[c] {
|
2018-06-18 17:45:48 +00:00
|
|
|
if !canCreate[res.Name] {
|
|
|
|
ready = false
|
2017-09-05 14:32:32 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2018-06-18 17:45:48 +00:00
|
|
|
if ready {
|
|
|
|
supported[c] = true
|
|
|
|
for _, res := range target.outputResources(c) {
|
|
|
|
for _, kind := range res.Kind {
|
|
|
|
canCreate[kind] = true
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|
2018-04-06 17:43:06 +00:00
|
|
|
}
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if n == len(supported) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2018-06-18 17:45:48 +00:00
|
|
|
ctors := make(map[string][]string)
|
|
|
|
for c := range enabled {
|
|
|
|
if supported[c] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for _, res := range inputResources[c] {
|
|
|
|
if canCreate[res.Name] {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if ctors[res.Name] == nil {
|
|
|
|
var names []string
|
|
|
|
for _, call := range target.calcResourceCtors(res.Kind, true) {
|
|
|
|
names = append(names, call.Name)
|
|
|
|
}
|
|
|
|
ctors[res.Name] = names
|
|
|
|
}
|
|
|
|
disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+
|
|
|
|
" enable some syscalls that can create it %v",
|
|
|
|
res.Name, ctors[res.Name])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(enabled) != len(supported)+len(disabled) {
|
|
|
|
panic("lost syscalls")
|
|
|
|
}
|
2018-04-06 17:43:06 +00:00
|
|
|
return supported, disabled
|
2017-09-05 14:32:32 +00:00
|
|
|
}
|