diff options
Diffstat (limited to 'pkg/vcs/linux.go')
| -rw-r--r-- | pkg/vcs/linux.go | 107 |
1 files changed, 93 insertions, 14 deletions
diff --git a/pkg/vcs/linux.go b/pkg/vcs/linux.go index 07278e83f..c6effd58a 100644 --- a/pkg/vcs/linux.go +++ b/pkg/vcs/linux.go @@ -17,6 +17,7 @@ import ( "github.com/google/syzkaller/pkg/debugtracer" "github.com/google/syzkaller/pkg/kconfig" "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/prog" "github.com/google/syzkaller/sys/targets" ) @@ -304,7 +305,12 @@ func ParseMaintainersLinux(text []byte) Recipients { const configBisectTag = "# Minimized by syzkaller" -func (ctx *linux) Minimize(target *targets.Target, original, baseline []byte, +// Minimize() attempts to drop Linux kernel configs that are unnecessary(*) for bug reproduction. +// 1. Remove sanitizers that are not needed to trigger the target class of bugs. +// 2. Disable unrelated kernel subsystems. This is done by bisecting config changes between +// `original` and `baseline`. +// (*) After an unnecessary config is deleted, we still have pred() == BisectBad. +func (ctx *linux) Minimize(target *targets.Target, original, baseline []byte, types []crash.Type, dt debugtracer.DebugTracer, pred func(test []byte) (BisectResult, error)) ([]byte, error) { if bytes.HasPrefix(original, []byte(configBisectTag)) { dt.Log("# configuration already minimized\n") @@ -314,27 +320,100 @@ func (ctx *linux) Minimize(target *targets.Target, original, baseline []byte, if err != nil { return nil, fmt.Errorf("failed to parse Kconfig: %v", err) } - originalConfig, err := kconfig.ParseConfigData(original, "original") + config, err := kconfig.ParseConfigData(original, "original") if err != nil { return nil, err } - baselineConfig, err := kconfig.ParseConfigData(baseline, "baseline") + minimizeCtx := &minimizeLinuxCtx{ + kconf: kconf, + config: config, + pred: func(cfg *kconfig.ConfigFile) (bool, error) { + res, err := pred(serialize(cfg)) + return res == BisectBad, err + }, + transform: func(cfg *kconfig.ConfigFile) { + setLinuxTagConfigs(cfg, nil) + }, + DebugTracer: dt, + } + if len(types) > 0 { + // Technically, as almost all sanitizers are Yes/No config options, we could have + // achieved this minimization simply by disabling them all in the baseline config. + // However, we are now trying to make the most out of the few config minimization + // iterations we're ready to make do during the bisection process. + // Since it's possible to quite reliably determine the needed and unneeded sanitizers + // just by looking at crash reports, let's prefer a more complicated logic over worse + // bisection results. + // Once we start doing proper config minimizations for every reproducer, we can delete + // most of the related code. + err := minimizeCtx.dropInstrumentation(types) + if err != nil { + return nil, err + } + } + if len(baseline) > 0 { + baselineConfig, err := kconfig.ParseConfigData(baseline, "baseline") + if err != nil { + return nil, err + } + err = minimizeCtx.minimizeAgainst(baselineConfig) + if err != nil { + return nil, err + } + } + return minimizeCtx.getConfig(), nil +} + +func serialize(cf *kconfig.ConfigFile) []byte { + return []byte(fmt.Sprintf("%v, rev: %v\n%s", configBisectTag, prog.GitRevision, cf.Serialize())) +} + +type minimizeLinuxCtx struct { + kconf *kconfig.KConfig + config *kconfig.ConfigFile + pred func(*kconfig.ConfigFile) (bool, error) + transform func(*kconfig.ConfigFile) + debugtracer.DebugTracer +} + +func (ctx *minimizeLinuxCtx) minimizeAgainst(base *kconfig.ConfigFile) error { + base = base.Clone() + ctx.transform(base) + minConfig, err := ctx.kconf.Minimize(base, ctx.config, ctx.pred, ctx) if err != nil { - return nil, err + return err } - setLinuxTagConfigs(originalConfig, nil) - setLinuxTagConfigs(baselineConfig, nil) - kconfPred := func(candidate *kconfig.ConfigFile) (bool, error) { - res, err := pred(serialize(candidate)) - return res == BisectBad, err + ctx.config = minConfig + return nil +} + +func (ctx *minimizeLinuxCtx) dropInstrumentation(types []crash.Type) error { + ctx.Log("check whether we can drop unnecessary instrumentation") + oldTransform := ctx.transform + transform := func(c *kconfig.ConfigFile) { + oldTransform(c) + setLinuxSanitizerConfigs(c, types, ctx) + } + newConfig := ctx.config.Clone() + transform(newConfig) + if bytes.Equal(ctx.config.Serialize(), newConfig.Serialize()) { + ctx.Log("there was nothing we could disable; skip") + return nil } - minConfig, err := kconf.Minimize(baselineConfig, originalConfig, kconfPred, dt) + ctx.SaveFile("no-instrumentation.config", newConfig.Serialize()) + ok, err := ctx.pred(newConfig) if err != nil { - return nil, err + return err + } + if ok { + ctx.Log("the bug reproduces without the instrumentation") + ctx.transform = transform + ctx.config = newConfig } - return serialize(minConfig), nil + return nil } -func serialize(cf *kconfig.ConfigFile) []byte { - return []byte(fmt.Sprintf("%v, rev: %v\n%s", configBisectTag, prog.GitRevision, cf.Serialize())) +func (ctx *minimizeLinuxCtx) getConfig() []byte { + ctx.transform(ctx.config) + return serialize(ctx.config) } |
