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  }