Skip to content

Commit 3ced5db

Browse files
committed
tools/syz-kconfig: suggest reasons for wrongly selected configs
The most frustrating part of updating syzbot configs is figuring out what config options (possibly transivitely) selected the configs we wanted to stay disabled. For each "X is present in the final config" message, auto-generate a small list of enabled config options that may have transitively "select"ed X.
1 parent 98683f8 commit 3ced5db

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

pkg/kconfig/kconfig.go

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,15 @@ type Menu struct {
3030
Elems []*Menu // sub-elements for menus
3131
Parent *Menu // parent menu, non-nil for everythign except for mainmenu
3232

33-
kconf *KConfig // back-link to the owning KConfig
34-
prompts []prompt
35-
defaults []defaultVal
36-
dependsOn expr
37-
visibleIf expr
38-
deps map[string]bool
39-
depsOnce sync.Once
33+
kconf *KConfig // back-link to the owning KConfig
34+
prompts []prompt
35+
defaults []defaultVal
36+
dependsOn expr
37+
visibleIf expr
38+
deps map[string]bool
39+
depsOnce sync.Once
40+
selects []string
41+
selectedBy []string // filled in in setSelectedBy()
4042
}
4143

4244
type prompt struct {
@@ -144,6 +146,7 @@ func ParseData(target *targets.Target, data []byte, file string) (*KConfig, erro
144146
Configs: make(map[string]*Menu),
145147
}
146148
kconf.walk(root, nil, nil)
149+
kconf.setSelectedBy()
147150
return kconf, nil
148151
}
149152

@@ -159,6 +162,37 @@ func (kconf *KConfig) walk(m *Menu, dependsOn, visibleIf expr) {
159162
}
160163
}
161164

165+
// NOTE: the function is ignoring the "if" part of select/imply.
166+
func (kconf *KConfig) setSelectedBy() {
167+
for name, cfg := range kconf.Configs {
168+
for _, selectedName := range cfg.selects {
169+
selected := kconf.Configs[selectedName]
170+
if selected == nil {
171+
continue
172+
}
173+
selected.selectedBy = append(selected.selectedBy, name)
174+
}
175+
}
176+
}
177+
178+
// NOTE: the function is ignoring the "if" part of select/imply.
179+
func (kconf *KConfig) SelectedBy(name string) map[string]bool {
180+
ret := map[string]bool{}
181+
toVisit := []string{name}
182+
for len(toVisit) > 0 {
183+
next := kconf.Configs[toVisit[len(toVisit)-1]]
184+
toVisit = toVisit[:len(toVisit)-1]
185+
if next == nil {
186+
continue
187+
}
188+
for _, selectedBy := range next.selectedBy {
189+
ret[selectedBy] = true
190+
toVisit = append(toVisit, selectedBy)
191+
}
192+
}
193+
return ret
194+
}
195+
162196
func (kp *kconfigParser) parseFile() {
163197
for kp.nextLine() {
164198
kp.parseLine()
@@ -293,7 +327,8 @@ func (kp *kconfigParser) parseProperty(prop string) {
293327
kp.MustConsume("if")
294328
cur.visibleIf = exprAnd(cur.visibleIf, kp.parseExpr())
295329
case "select", "imply":
296-
_ = kp.Ident()
330+
name := kp.Ident()
331+
cur.selects = append(cur.selects, name)
297332
if kp.TryConsume("if") {
298333
_ = kp.parseExpr()
299334
}

pkg/kconfig/kconfig_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
"github.com/google/syzkaller/sys/targets"
11+
"github.com/stretchr/testify/assert"
1112
)
1213

1314
func TestParseKConfig(t *testing.T) {
@@ -35,6 +36,37 @@ config FOO
3536
}
3637
}
3738

39+
func TestSelectedby(t *testing.T) {
40+
configData := `
41+
mainmenu "test"
42+
43+
config FEATURE_A
44+
bool "Feature A"
45+
select FEATURE_B
46+
47+
config FEATURE_B
48+
bool "Feature B"
49+
select FEATURE_C
50+
51+
config FEATURE_C
52+
bool "Feature C"
53+
54+
`
55+
target := targets.Get("linux", "amd64")
56+
kconf, err := ParseData(target, []byte(configData), "Kconfig")
57+
if err != nil {
58+
t.Fatal(err)
59+
}
60+
assert.Empty(t, kconf.SelectedBy("FEATURE_A"))
61+
assert.Equal(t, map[string]bool{
62+
"FEATURE_A": true,
63+
}, kconf.SelectedBy("FEATURE_B"))
64+
assert.Equal(t, map[string]bool{
65+
"FEATURE_A": true,
66+
"FEATURE_B": true,
67+
}, kconf.SelectedBy("FEATURE_C"))
68+
}
69+
3870
func TestFuzzParseKConfig(t *testing.T) {
3971
for _, data := range []string{
4072
``,

tools/syz-kconf/kconf.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,17 @@ func (ctx *Context) verifyConfigs(cf *kconfig.ConfigFile) error {
315315
if act == kconfig.No {
316316
errs.push("%v:%v: %v is not present in the final config", cfg.File, cfg.Line, cfg.Name)
317317
} else if cfg.Value == kconfig.No {
318-
errs.push("%v:%v: %v is present in the final config", cfg.File, cfg.Line, cfg.Name)
318+
var selectedBy []string
319+
for name := range ctx.Kconf.SelectedBy(cfg.Name) {
320+
if cf.Value(name) == kconfig.Yes {
321+
selectedBy = append(selectedBy, name)
322+
}
323+
}
324+
selectedByStr := ""
325+
if len(selectedBy) > 0 {
326+
selectedByStr = fmt.Sprintf(", possibly selected by %q", selectedBy)
327+
}
328+
errs.push("%v:%v: %v is present in the final config%s", cfg.File, cfg.Line, cfg.Name, selectedByStr)
319329
} else {
320330
errs.push("%v:%v: %v does not match final config %v vs %v",
321331
cfg.File, cfg.Line, cfg.Name, cfg.Value, act)

0 commit comments

Comments
 (0)