github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/vminfo/features.go (about) 1 // Copyright 2024 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 4 package vminfo 5 6 import ( 7 "fmt" 8 "strings" 9 10 "github.com/google/syzkaller/pkg/flatrpc" 11 "github.com/google/syzkaller/pkg/fuzzer/queue" 12 "github.com/google/syzkaller/pkg/ipc" 13 "github.com/google/syzkaller/prog" 14 ) 15 16 type Feature struct { 17 Enabled bool 18 NeedSetup bool 19 Reason string 20 } 21 22 type Features map[flatrpc.Feature]Feature 23 24 func (features Features) Enabled() flatrpc.Feature { 25 var mask flatrpc.Feature 26 for feat, info := range features { 27 if info.Enabled { 28 mask |= feat 29 } 30 } 31 return mask 32 } 33 34 func (features Features) NeedSetup() flatrpc.Feature { 35 var mask flatrpc.Feature 36 for feat, info := range features { 37 if info.Enabled && info.NeedSetup { 38 mask |= feat 39 } 40 } 41 return mask 42 } 43 44 type featureResult struct { 45 id flatrpc.Feature 46 reason string 47 } 48 49 func (ctx *checkContext) startFeaturesCheck() { 50 testProg := ctx.target.DataMmapProg() 51 for feat := range flatrpc.EnumNamesFeature { 52 feat := feat 53 go func() { 54 envFlags, execFlags := ctx.featureToFlags(feat) 55 req := &queue.Request{ 56 Prog: testProg, 57 ReturnOutput: true, 58 ReturnError: true, 59 ExecOpts: ipc.ExecOpts{ 60 EnvFlags: envFlags, 61 ExecFlags: execFlags, 62 SandboxArg: ctx.cfg.SandboxArg, 63 }, 64 } 65 ctx.executor.Submit(req) 66 res := req.Wait(ctx.ctx) 67 reason := ctx.featureSucceeded(feat, testProg, res) 68 ctx.features <- featureResult{feat, reason} 69 }() 70 } 71 } 72 73 func (ctx *checkContext) finishFeatures(featureInfos []*flatrpc.FeatureInfo) (Features, error) { 74 // Feature checking consists of 2 parts: 75 // - we ask executor to try to setup each feature (results are returned in featureInfos) 76 // - we also try to run a simple program with feature-specific flags 77 // Here we combine both results. 78 features := make(Features) 79 for _, info := range featureInfos { 80 features[info.Id] = Feature{ 81 Reason: info.Reason, 82 NeedSetup: info.NeedSetup, 83 } 84 } 85 outputReplacer := strings.NewReplacer( 86 "SYZFAIL:", "", 87 "\n", ". ", 88 ) 89 for range flatrpc.EnumNamesFeature { 90 res := <-ctx.features 91 feat := features[res.id] 92 if feat.Reason == "" { 93 feat.Reason = res.reason 94 } 95 if feat.Reason == "" { 96 feat.Reason = "enabled" 97 feat.Enabled = true 98 } 99 if pos := strings.Index(feat.Reason, "loop exited with status"); pos != -1 { 100 feat.Reason = feat.Reason[:pos] 101 } 102 // If executor exited the output is prefixed with "executor 4: EOF". 103 const executorPrefix = ": EOF\n" 104 if pos := strings.Index(feat.Reason, executorPrefix); pos != -1 { 105 feat.Reason = feat.Reason[pos+len(executorPrefix):] 106 } 107 feat.Reason = strings.TrimSpace(outputReplacer.Replace(feat.Reason)) 108 features[res.id] = feat 109 } 110 if feat := features[flatrpc.FeatureSandboxSetuid]; !feat.Enabled { 111 return features, fmt.Errorf("execution of simple program fails: %v", feat.Reason) 112 } 113 if feat := features[flatrpc.FeatureCoverage]; ctx.cfg.Cover && !feat.Enabled { 114 return features, fmt.Errorf("coverage is not supported: %v", feat.Reason) 115 } 116 return features, nil 117 } 118 119 // featureToFlags creates ipc flags required to test the feature on a simple program. 120 // For features that has setup procedure in the executor, we just execute with the default flags. 121 func (ctx *checkContext) featureToFlags(feat flatrpc.Feature) (flatrpc.ExecEnv, flatrpc.ExecFlag) { 122 envFlags := ctx.sandbox 123 // These don't have a corresponding feature and are always enabled. 124 envFlags |= flatrpc.ExecEnvEnableCloseFds | flatrpc.ExecEnvEnableCgroups | flatrpc.ExecEnvEnableNetReset 125 execFlags := flatrpc.ExecFlagThreaded 126 switch feat { 127 case flatrpc.FeatureCoverage: 128 envFlags |= flatrpc.ExecEnvSignal 129 execFlags |= flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover 130 case flatrpc.FeatureComparisons: 131 envFlags |= flatrpc.ExecEnvSignal 132 execFlags |= flatrpc.ExecFlagCollectComps 133 case flatrpc.FeatureExtraCoverage: 134 envFlags |= flatrpc.ExecEnvSignal | flatrpc.ExecEnvExtraCover 135 execFlags |= flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover 136 case flatrpc.FeatureDelayKcovMmap: 137 envFlags |= flatrpc.ExecEnvSignal | flatrpc.ExecEnvDelayKcovMmap 138 execFlags |= flatrpc.ExecFlagCollectSignal | flatrpc.ExecFlagCollectCover 139 case flatrpc.FeatureSandboxSetuid: 140 // We use setuid sandbox feature to test that the simple program 141 // succeeds under the actual sandbox (not necessary setuid). 142 // We do this because we don't have a feature for sandbox 'none'. 143 case flatrpc.FeatureSandboxNamespace: 144 case flatrpc.FeatureSandboxAndroid: 145 case flatrpc.FeatureFault: 146 case flatrpc.FeatureLeak: 147 case flatrpc.FeatureNetInjection: 148 envFlags |= flatrpc.ExecEnvEnableTun 149 case flatrpc.FeatureNetDevices: 150 envFlags |= flatrpc.ExecEnvEnableNetDev 151 case flatrpc.FeatureKCSAN: 152 case flatrpc.FeatureDevlinkPCI: 153 envFlags |= flatrpc.ExecEnvEnableDevlinkPCI 154 case flatrpc.FeatureNicVF: 155 envFlags |= flatrpc.ExecEnvEnableNicVF 156 case flatrpc.FeatureUSBEmulation: 157 case flatrpc.FeatureVhciInjection: 158 envFlags |= flatrpc.ExecEnvEnableVhciInjection 159 case flatrpc.FeatureWifiEmulation: 160 envFlags |= flatrpc.ExecEnvEnableWifi 161 case flatrpc.FeatureLRWPANEmulation: 162 case flatrpc.FeatureBinFmtMisc: 163 case flatrpc.FeatureSwap: 164 default: 165 panic(fmt.Sprintf("unknown feature %v", flatrpc.EnumNamesFeature[feat])) 166 } 167 return envFlags, execFlags 168 } 169 170 // featureSucceeded checks if execution of a simple program with feature-specific flags succeed. 171 // This generally checks that just all syscalls were executed and succeed, 172 // for coverage features we also check that we got actual coverage. 173 func (ctx *checkContext) featureSucceeded(feat flatrpc.Feature, testProg *prog.Prog, 174 res *queue.Result) string { 175 if res.Status != queue.Success { 176 if len(res.Output) != 0 { 177 return string(res.Output) 178 } 179 if res.Err != nil { 180 return res.Err.Error() 181 } 182 return "test program execution failed" 183 } 184 if len(res.Info.Calls) != len(testProg.Calls) { 185 return fmt.Sprintf("only %v calls are executed out of %v", 186 len(res.Info.Calls), len(testProg.Calls)) 187 } 188 for i, call := range res.Info.Calls { 189 if call.Errno != 0 { 190 return fmt.Sprintf("call %v failed with errno %v", i, call.Errno) 191 } 192 } 193 call := res.Info.Calls[0] 194 switch feat { 195 case flatrpc.FeatureCoverage: 196 if len(call.Cover) == 0 || len(call.Signal) == 0 { 197 return "got no coverage" 198 } 199 case flatrpc.FeatureComparisons: 200 if len(call.Comps) == 0 { 201 return "got no coverage" 202 } 203 } 204 return "" 205 }