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