1// Copyright 2018 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 host
5
6import (
7	"github.com/google/syzkaller/prog"
8)
9
10// DetectSupportedSyscalls returns list on supported and unsupported syscalls on the host.
11// For unsupported syscalls it also returns reason as to why it is unsupported.
12func DetectSupportedSyscalls(target *prog.Target, sandbox string) (
13	map[*prog.Syscall]bool, map[*prog.Syscall]string, error) {
14	supported := make(map[*prog.Syscall]bool)
15	unsupported := make(map[*prog.Syscall]string)
16	// Akaros does not have own host and parasitizes on some other OS.
17	if target.OS == "akaros" || target.OS == "test" {
18		for _, c := range target.Syscalls {
19			supported[c] = true
20		}
21		return supported, unsupported, nil
22	}
23	for _, c := range target.Syscalls {
24		ok, reason := isSupported(c, sandbox)
25		if ok {
26			supported[c] = true
27		} else {
28			if reason == "" {
29				reason = "unknown"
30			}
31			unsupported[c] = reason
32		}
33	}
34	return supported, unsupported, nil
35}
36
37var testFallback = false
38
39const (
40	FeatureCoverage = iota
41	FeatureComparisons
42	FeatureSandboxSetuid
43	FeatureSandboxNamespace
44	FeatureFaultInjection
45	FeatureLeakChecking
46	FeatureNetworkInjection
47	FeatureNetworkDevices
48	numFeatures
49)
50
51type Feature struct {
52	Name    string
53	Enabled bool
54	Reason  string
55}
56
57type Features [numFeatures]Feature
58
59var checkFeature [numFeatures]func() string
60var setupFeature [numFeatures]func() error
61var callbFeature [numFeatures]func()
62
63func unconditionallyEnabled() string { return "" }
64
65// Check detects features supported on the host.
66// Empty string for a feature means the feature is supported,
67// otherwise the string contains the reason why the feature is not supported.
68func Check(target *prog.Target) (*Features, error) {
69	const unsupported = "support is not implemented in syzkaller"
70	res := &Features{
71		FeatureCoverage:         {Name: "code coverage", Reason: unsupported},
72		FeatureComparisons:      {Name: "comparison tracing", Reason: unsupported},
73		FeatureSandboxSetuid:    {Name: "setuid sandbox", Reason: unsupported},
74		FeatureSandboxNamespace: {Name: "namespace sandbox", Reason: unsupported},
75		FeatureFaultInjection:   {Name: "fault injection", Reason: unsupported},
76		FeatureLeakChecking:     {Name: "leak checking", Reason: unsupported},
77		FeatureNetworkInjection: {Name: "net packed injection", Reason: unsupported},
78		FeatureNetworkDevices:   {Name: "net device setup", Reason: unsupported},
79	}
80	if target.OS == "akaros" || target.OS == "test" {
81		return res, nil
82	}
83	for n, check := range checkFeature {
84		if check == nil {
85			continue
86		}
87		if reason := check(); reason == "" {
88			res[n].Enabled = true
89			res[n].Reason = "enabled"
90		} else {
91			res[n].Reason = reason
92		}
93	}
94	return res, nil
95}
96
97// Setup enables and does any one-time setup for the requested features on the host.
98// Note: this can be called multiple times and must be idempotent.
99func Setup(target *prog.Target, features *Features) (func(), error) {
100	if target.OS == "akaros" || target.OS == "test" {
101		return nil, nil
102	}
103	var callback func()
104	for n, setup := range setupFeature {
105		if setup == nil || !features[n].Enabled {
106			continue
107		}
108		if err := setup(); err != nil {
109			return nil, err
110		}
111		cb := callbFeature[n]
112		if cb != nil {
113			prev := callback
114			callback = func() {
115				cb()
116				if prev != nil {
117					prev()
118				}
119			}
120
121		}
122	}
123	return callback, nil
124}
125