diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-06-15 15:38:51 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-07-05 11:29:44 +0000 |
| commit | e8b147c6e77e4568d3e7df74121a28e95ee8440a (patch) | |
| tree | 45e8e43e65a1ccb29d9eba5b68cd45965565afde /pkg/vcs | |
| parent | 9a3d2957802872770199d00684132907508a6915 (diff) | |
pkg/bisect: try to drop instrumentation
Refactor Minimize().
As a part of Minimize(), attempt to drop all unnecessary instrumentation
and test whether the kernel is still crashing.
This should reduce the number of irrelevant crashes during bisection and
thus increase bisection result quality.
Diffstat (limited to 'pkg/vcs')
| -rw-r--r-- | pkg/vcs/linux.go | 107 | ||||
| -rw-r--r-- | pkg/vcs/testos.go | 6 | ||||
| -rw-r--r-- | pkg/vcs/vcs.go | 5 |
3 files changed, 101 insertions, 17 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) } diff --git a/pkg/vcs/testos.go b/pkg/vcs/testos.go index fe7c3790f..cfc34cc23 100644 --- a/pkg/vcs/testos.go +++ b/pkg/vcs/testos.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/google/syzkaller/pkg/debugtracer" + "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/sys/targets" ) @@ -32,8 +33,11 @@ func (ctx *testos) EnvForCommit( return &BisectEnv{KernelConfig: kernelConfig}, nil } -func (ctx *testos) Minimize(target *targets.Target, original, baseline []byte, +func (ctx *testos) Minimize(target *targets.Target, original, baseline []byte, types []crash.Type, dt debugtracer.DebugTracer, pred func(test []byte) (BisectResult, error)) ([]byte, error) { + if len(baseline) == 0 { + return original, nil + } if res, err := pred(baseline); err != nil { return nil, err } else if res == BisectBad { diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go index 3e57a1695..9515692ea 100644 --- a/pkg/vcs/vcs.go +++ b/pkg/vcs/vcs.go @@ -16,6 +16,7 @@ import ( "github.com/google/syzkaller/dashboard/dashapi" "github.com/google/syzkaller/pkg/debugtracer" "github.com/google/syzkaller/pkg/osutil" + "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/sys/targets" ) @@ -96,8 +97,8 @@ type Bisecter interface { } type ConfigMinimizer interface { - Minimize(target *targets.Target, original, baseline []byte, dt debugtracer.DebugTracer, - pred func(test []byte) (BisectResult, error)) ([]byte, error) + Minimize(target *targets.Target, original, baseline []byte, types []crash.Type, + dt debugtracer.DebugTracer, pred func(test []byte) (BisectResult, error)) ([]byte, error) } type Commit struct { |
