1// Copyright 2017 syzkaller project authors. All rights reserved.
2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3
4package prog
5
6import (
7	"fmt"
8)
9
10// We need to support structs as resources,
11// but for now we just special-case timespec/timeval.
12var timespecRes = &ResourceDesc{
13	Name: "timespec",
14	Kind: []string{"timespec"},
15}
16
17func (target *Target) calcResourceCtors(kind []string, precise bool) []*Syscall {
18
19	//fmt.Printf("calcResourceCtors: kind=%+v\n", kind)
20
21	// Find calls that produce the necessary resources.
22	var metas []*Syscall
23	for _, meta := range target.Syscalls {
24		// Recurse into arguments to see if there is an out/inout arg of necessary type.
25		ok := false
26		//if meta.Name != "pipe$9p" { continue }
27		//fmt.Printf("found pipe$9p\n")
28
29		ForeachType(meta, func(typ Type) {
30			if ok {
31				return
32			}
33			switch typ1 := typ.(type) {
34			case *ResourceType:
35				//fmt.Printf("   output: %+v\n", typ1.Desc.Kind)
36				if typ1.Dir() != DirIn && isCompatibleResourceImpl(kind, typ1.Desc.Kind, precise) {
37					ok = true
38				}
39			}
40		})
41		if ok {
42			metas = append(metas, meta)
43		}
44	}
45	if kind[0] == timespecRes.Name {
46		if c := target.SyscallMap["clock_gettime"]; c != nil {
47			metas = append(metas, c)
48		}
49	}
50	return metas
51}
52
53// isCompatibleResource returns true if resource of kind src can be passed as an argument of kind dst.
54func (target *Target) isCompatibleResource(dst, src string) bool {
55	if dst == target.any.res16.TypeName ||
56		dst == target.any.res32.TypeName ||
57		dst == target.any.res64.TypeName ||
58		dst == target.any.resdec.TypeName ||
59		dst == target.any.reshex.TypeName ||
60		dst == target.any.resoct.TypeName {
61		return true
62	}
63	dstRes := target.resourceMap[dst]
64	if dstRes == nil {
65		panic(fmt.Sprintf("unknown resource '%v'", dst))
66	}
67	srcRes := target.resourceMap[src]
68	if srcRes == nil {
69		panic(fmt.Sprintf("unknown resource '%v'", src))
70	}
71	return isCompatibleResourceImpl(dstRes.Kind, srcRes.Kind, false)
72}
73
74// isCompatibleResourceImpl returns true if resource of kind src can be passed as an argument of kind dst.
75// If precise is true, then it does not allow passing a less specialized resource (e.g. fd)
76// as a more specialized resource (e.g. socket). Otherwise it does.
77func isCompatibleResourceImpl(dst, src []string, precise bool) bool {
78	//fmt.Printf("isCompatibleResourceImpl: %+v/%v vs %+v/%v\n", dst, len(dst), src, len(src))
79	if len(dst) > len(src) {
80		// dst is more specialized, e.g dst=socket, src=fd.
81		if precise {
82			//fmt.Printf("     = false1\n")
83			return false
84		}
85		dst = dst[:len(src)]
86	}
87	if len(src) > len(dst) {
88		// src is more specialized, e.g dst=fd, src=socket.
89		src = src[:len(dst)]
90	}
91	for i, k := range dst {
92		if k != src[i] {
93			//fmt.Printf("     = false2\n")
94			return false
95		}
96	}
97	//fmt.Printf("     = true\n")
98	return true
99}
100
101func (target *Target) inputResources(c *Syscall) []*ResourceDesc {
102	var resources []*ResourceDesc
103	ForeachType(c, func(typ Type) {
104		if typ.Dir() == DirOut {
105			return
106		}
107		switch typ1 := typ.(type) {
108		case *ResourceType:
109			if !typ1.IsOptional {
110				resources = append(resources, typ1.Desc)
111			}
112		case *StructType:
113			if target.OS == "linux" && (typ1.Name() == "timespec" || typ1.Name() == "timeval") {
114				resources = append(resources, timespecRes)
115			}
116		}
117	})
118	return resources
119}
120
121func (target *Target) outputResources(c *Syscall) []*ResourceDesc {
122	var resources []*ResourceDesc
123	ForeachType(c, func(typ Type) {
124		switch typ1 := typ.(type) {
125		case *ResourceType:
126			if typ1.Dir() != DirIn {
127				resources = append(resources, typ1.Desc)
128			}
129		}
130	})
131	if c.CallName == "clock_gettime" {
132		resources = append(resources, timespecRes)
133	}
134	return resources
135}
136
137func (target *Target) TransitivelyEnabledCalls(enabled map[*Syscall]bool) (map[*Syscall]bool, map[*Syscall]string) {
138	supported := make(map[*Syscall]bool)
139	disabled := make(map[*Syscall]string)
140	canCreate := make(map[string]bool)
141	inputResources := make(map[*Syscall][]*ResourceDesc)
142	for c := range enabled {
143		inputResources[c] = target.inputResources(c)
144
145		if c.Name == "pipe$9p" {
146			fmt.Printf("%v: input resource: %+v\n", c.Name, inputResources[c])
147		}
148	}
149	for {
150		n := len(supported)
151		for c := range enabled {
152			if supported[c] {
153				continue
154			}
155			ready := true
156			for _, res := range inputResources[c] {
157				if !canCreate[res.Name] {
158					ready = false
159					break
160				}
161			}
162			if ready {
163				supported[c] = true
164				for _, res := range target.outputResources(c) {
165					for _, kind := range res.Kind {
166						canCreate[kind] = true
167					}
168				}
169			}
170		}
171		if n == len(supported) {
172			break
173		}
174	}
175	ctors := make(map[string][]string)
176	for c := range enabled {
177		if supported[c] {
178			continue
179		}
180		for _, res := range inputResources[c] {
181			if canCreate[res.Name] {
182				continue
183			}
184			if ctors[res.Name] == nil {
185				var names []string
186				for _, call := range target.calcResourceCtors(res.Kind, true) {
187					names = append(names, call.Name)
188				}
189				ctors[res.Name] = names
190			}
191			disabled[c] = fmt.Sprintf("no syscalls can create resource %v,"+
192				" enable some syscalls that can create it %v",
193				res.Name, ctors[res.Name])
194			break
195		}
196	}
197	if len(enabled) != len(supported)+len(disabled) {
198		panic("lost syscalls")
199	}
200	return supported, disabled
201}
202