From e6cd3005e289a9d82272bb20a7173a4dbe75e55c Mon Sep 17 00:00:00 2001 From: Aleksandr Nogikh Date: Thu, 15 Jun 2023 14:14:53 +0200 Subject: pkg/vcs: disable Linux kernel configs based on crash.Type More sanitizers means longer compilation times, more flakiness and risks of failures due to bugs in sanitizers themselves. If the crash type is known, determine the sanitizer that detected the problem and disable everything else. --- pkg/vcs/linux_configs.go | 54 ++++++++++++++++++++++++- pkg/vcs/linux_configs_test.go | 93 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 pkg/vcs/linux_configs_test.go (limited to 'pkg') diff --git a/pkg/vcs/linux_configs.go b/pkg/vcs/linux_configs.go index b54fed8fa..607a06285 100644 --- a/pkg/vcs/linux_configs.go +++ b/pkg/vcs/linux_configs.go @@ -3,7 +3,13 @@ package vcs -import "github.com/google/syzkaller/pkg/kconfig" +import ( + "strings" + + "github.com/google/syzkaller/pkg/debugtracer" + "github.com/google/syzkaller/pkg/kconfig" + "github.com/google/syzkaller/pkg/report/crash" +) // setLinuxTagConfigs() disables Linux kernel configurations depending on the Linux kernel version, // which is determined by the git tags reachable from HEAD. @@ -86,3 +92,49 @@ func setLinuxTagConfigs(cf *kconfig.ConfigFile, tags map[string]bool) { } } } + +// setLinuxSanitizerConfigs() removes Linux kernel sanitizers that are not necessary +// to trigger the specified crash types. +func setLinuxSanitizerConfigs(cf *kconfig.ConfigFile, types []crash.Type, dt debugtracer.DebugTracer) { + keep := map[crash.Type]func(){ + crash.Hang: func() { + cf.Unset("RCU_STALL_COMMON") + cf.Unset("LOCKUP_DETECTOR") + cf.Unset("SOFTLOCKUP_DETECTOR") + cf.Unset("HARDLOCKUP_DETECTOR") + cf.Unset("DETECT_HUNG_TASK") + // It looks like it's the only reliable way to completely disable hung errors. + val := cf.Value("CMDLINE") + pos := strings.LastIndexByte(val, '"') + if pos >= 0 { + cf.Set("CMDLINE", + val[:pos]+" rcupdate.rcu_cpu_stall_suppress=1"+val[pos:]) + } + }, + crash.MemoryLeak: func() { cf.Unset("DEBUG_KMEMLEAK") }, + crash.UBSAN: func() { cf.Unset("UBSAN") }, + crash.Bug: func() { cf.Unset("BUG") }, + crash.KASAN: func() { cf.Unset("KASAN") }, + crash.LockdepBug: func() { cf.Unset("LOCKDEP") }, + crash.AtomicSleep: func() { cf.Unset("DEBUG_ATOMIC_SLEEP") }, + } + need := map[crash.Type]bool{} + for _, typ := range types { + if typ == crash.Warning { + // These are disabled together. + typ = crash.Bug + } + need[typ] = true + } + var disabled []string + for typ, f := range keep { + if need[typ] { + continue + } + f() + disabled = append(disabled, string(typ)) + } + if len(disabled) > 0 { + dt.Log("disabling configs for %v, they are not needed", disabled) + } +} diff --git a/pkg/vcs/linux_configs_test.go b/pkg/vcs/linux_configs_test.go new file mode 100644 index 000000000..6ea95645f --- /dev/null +++ b/pkg/vcs/linux_configs_test.go @@ -0,0 +1,93 @@ +// Copyright 2023 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +package vcs + +import ( + "testing" + + "github.com/google/syzkaller/pkg/debugtracer" + "github.com/google/syzkaller/pkg/kconfig" + "github.com/google/syzkaller/pkg/report/crash" + "github.com/stretchr/testify/assert" +) + +func TestDropLinuxSanitizerConfigs(t *testing.T) { + tests := []struct { + name string + types []crash.Type + test func(*testing.T, *kconfig.ConfigFile) + }{ + { + name: "warning", + types: []crash.Type{crash.Warning}, + test: func(t *testing.T, cf *kconfig.ConfigFile) { + assertConfigs(t, cf, "BUG") + assert.Equal(t, + `"param1=a param2=b rcupdate.rcu_cpu_stall_suppress=1"`, + cf.Value("CMDLINE"), + ) + }, + }, + { + name: "kasan bug", + types: []crash.Type{crash.KASAN}, + test: func(t *testing.T, cf *kconfig.ConfigFile) { + assertConfigs(t, cf, "KASAN") + }, + }, + { + name: "warning & kasan bug", + types: []crash.Type{crash.Warning, crash.KASAN}, + test: func(t *testing.T, cf *kconfig.ConfigFile) { + assertConfigs(t, cf, "KASAN", "BUG") + }, + }, + { + name: "lockdep", + types: []crash.Type{crash.LockdepBug}, + test: func(t *testing.T, cf *kconfig.ConfigFile) { + assertConfigs(t, cf, "LOCKDEP") + }, + }, + { + name: "rcu stall", + types: []crash.Type{crash.Hang}, + test: func(t *testing.T, cf *kconfig.ConfigFile) { + assertConfigs(t, cf, "RCU_STALL_COMMON") + assert.Equal(t, `"param1=a param2=b"`, cf.Value("CMDLINE")) + }, + }, + } + + const base = ` +CONFIG_CMDLINE="param1=a param2=b" +CONFIG_BUG=y +CONFIG_KASAN=y +CONFIG_LOCKDEP=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_UBSAN=y +CONFIG_DEBUG_ATOMIC_SLEEP=y +` + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + conf, err := kconfig.ParseConfigData([]byte(base), "base") + if err != nil { + t.Fatal(err) + } + setLinuxSanitizerConfigs(conf, test.types, &debugtracer.NullTracer{}) + test.test(t, conf) + }) + } +} + +func assertConfigs(t *testing.T, cf *kconfig.ConfigFile, names ...string) { + var setConfigs []string + for _, name := range names { + if cf.Value(name) == kconfig.Yes { + setConfigs = append(setConfigs, name) + } + } + assert.ElementsMatch(t, setConfigs, names) +} -- cgit mrf-deployment