diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2025-07-30 15:38:19 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2025-07-31 12:54:40 +0000 |
| commit | a9814992554d3a157c61716e82aace97ad7860af (patch) | |
| tree | 2c6c57cef0f9e43a38353da6b3b651e8b494a3e3 /syz-cluster | |
| parent | 3526e777e9f189bbd898e10cf3042abe4c75f179 (diff) | |
syz-cluster: split tree and fuzz config selection
Not always are fuzzing targets well represented by their own kernel
trees, so let's select a kernel tree and a fuzzing config separately.
Drop explicit priorities and instead just sort the lists of trees and
configs.
Diffstat (limited to 'syz-cluster')
| -rw-r--r-- | syz-cluster/pkg/api/api.go | 133 | ||||
| -rw-r--r-- | syz-cluster/pkg/api/client.go | 3 | ||||
| -rw-r--r-- | syz-cluster/pkg/controller/api.go | 3 | ||||
| -rw-r--r-- | syz-cluster/pkg/triage/fuzz_config.go | 28 | ||||
| -rw-r--r-- | syz-cluster/pkg/triage/fuzz_config_test.go | 51 | ||||
| -rw-r--r-- | syz-cluster/pkg/triage/tree.go | 7 | ||||
| -rw-r--r-- | syz-cluster/pkg/triage/tree_test.go | 21 | ||||
| -rw-r--r-- | syz-cluster/workflow/triage-step/main.go | 16 |
8 files changed, 166 insertions, 96 deletions
diff --git a/syz-cluster/pkg/api/api.go b/syz-cluster/pkg/api/api.go index 481554a50..363ee45fb 100644 --- a/syz-cluster/pkg/api/api.go +++ b/syz-cluster/pkg/api/api.go @@ -10,11 +10,11 @@ type TriageResult struct { // If set, ignore the patch series completely. SkipReason string `json:"skip_reason"` // Fuzzing configuration to try (NULL if nothing). - Fuzz *FuzzConfig `json:"fuzz"` + Fuzz *FuzzTask `json:"fuzz"` } // The data layout faclitates the simplicity of the workflow definition. -type FuzzConfig struct { +type FuzzTask struct { Base BuildRequest `json:"base"` Patched BuildRequest `json:"patched"` Config string `json:"config"` // Refers to workflow/configs/{}. @@ -23,18 +23,19 @@ type FuzzConfig struct { // The triage step of the workflow will request these from controller. type Tree struct { + Name string `json:"name"` // Primary key. + URL string `json:"URL"` + Branch string `json:"branch"` + EmailLists []string `json:"email_lists"` +} + +type FuzzConfig struct { Name string `json:"name"` // Primary key. - URL string `json:"URL"` - Branch string `json:"branch"` EmailLists []string `json:"email_lists"` - Priority int64 `json:"priority"` // Higher numbers mean higher priority. KernelConfig string `json:"kernel_config"` - FuzzConfig string `json:"fuzz_config"` + CorpusURL string `json:"corpus_url"` } -// Select only if directly specified in the series subject. -const TreePriorityNever = -1 - type BuildRequest struct { Arch string `json:"arch"` TreeName string `json:"tree_name"` @@ -154,86 +155,76 @@ type BuildInfo struct { } // Let them stay here until we find a better place. +// The list is ordered by decreasing importance. var DefaultTrees = []*Tree{ { - Name: `torvalds`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux`, - Branch: `master`, - Priority: 0, - EmailLists: []string{}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `all`, + Name: `bpf-next`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next.git`, + Branch: `master`, + EmailLists: []string{`bpf@vger.kernel.org`}, }, { - Name: `net`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net.git`, - Branch: `main`, - Priority: TreePriorityNever, - EmailLists: []string{`netdev@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `net`, + Name: `bpf`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf.git`, + Branch: `master`, + EmailLists: []string{`bpf@vger.kernel.org`}, }, { - Name: `net-next`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net-next.git`, - Branch: `main`, - Priority: 1, - EmailLists: []string{`netdev@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `net`, + Name: `nf-next`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netfilter/nf-next.git`, + Branch: `main`, + EmailLists: []string{`netfilter-devel@vger.kernel.org`}, }, { - Name: `nf`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netfilter/nf.git`, - Branch: `main`, - Priority: TreePriorityNever, - EmailLists: []string{`netfilter-devel@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `net`, + Name: `nf`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netfilter/nf.git`, + Branch: `main`, + EmailLists: []string{`netfilter-devel@vger.kernel.org`}, }, { - Name: `nf-next`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netfilter/nf-next.git`, - Branch: `main`, - Priority: 2, - EmailLists: []string{`netfilter-devel@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `net`, + Name: `net-next`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net-next.git`, + Branch: `main`, + EmailLists: []string{`netdev@vger.kernel.org`}, }, { - Name: `bpf`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf.git`, - Branch: `master`, - Priority: TreePriorityNever, - EmailLists: []string{`bpf@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `bpf`, + Name: `net`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/netdev/net.git`, + Branch: `main`, + EmailLists: []string{`netdev@vger.kernel.org`}, }, { - Name: `bpf-next`, - URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/bpf/bpf-next.git`, - Branch: `master`, - Priority: 2, - EmailLists: []string{`bpf@vger.kernel.org`}, - KernelConfig: `upstream-apparmor-kasan.config`, - FuzzConfig: `bpf`, + Name: `torvalds`, + URL: `https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux`, + Branch: `master`, + EmailLists: nil, // A fallback tree. }, } const ( - netCorpusURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-net-kasan-gce-corpus.db` - bpfCorpusURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-bpf-kasan-gce-corpus.db` - corpusFallbackURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-kasan-gce-root-corpus.db` + netCorpusURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-net-kasan-gce-corpus.db` + bpfCorpusURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-bpf-kasan-gce-corpus.db` + allCorpusURL = `https://storage.googleapis.com/syzkaller/corpus/ci-upstream-kasan-gce-root-corpus.db` ) -// TODO: find a better place for it. -func (tree *Tree) CorpusURL() string { - switch tree.FuzzConfig { - case `net`: - return netCorpusURL - case `bpf`: - return bpfCorpusURL - default: - return corpusFallbackURL - } +// The list is ordered by decreasing importance. +var FuzzConfigs = []*FuzzConfig{ + { + Name: `bpf`, + EmailLists: []string{`bpf@vger.kernel.org`}, + KernelConfig: `upstream-apparmor-kasan.config`, + CorpusURL: bpfCorpusURL, + }, + { + Name: `net`, + EmailLists: []string{`netdev@vger.kernel.org`, `netfilter-devel@vger.kernel.org`}, + KernelConfig: `upstream-apparmor-kasan.config`, + CorpusURL: netCorpusURL, + }, + { + Name: `all`, + EmailLists: nil, // A fallback option. + KernelConfig: `upstream-apparmor-kasan.config`, + CorpusURL: allCorpusURL, + }, } diff --git a/syz-cluster/pkg/api/client.go b/syz-cluster/pkg/api/client.go index 43da4b512..7186e6571 100644 --- a/syz-cluster/pkg/api/client.go +++ b/syz-cluster/pkg/api/client.go @@ -41,7 +41,8 @@ func (client Client) UploadTriageResult(ctx context.Context, sessionID string, r } type TreesResp struct { - Trees []*Tree `json:"trees"` + Trees []*Tree `json:"trees"` + FuzzConfigs []*FuzzConfig `json:"fuzz_configs"` } func (client Client) GetTrees(ctx context.Context) (*TreesResp, error) { diff --git a/syz-cluster/pkg/controller/api.go b/syz-cluster/pkg/controller/api.go index a20860d6f..de8545319 100644 --- a/syz-cluster/pkg/controller/api.go +++ b/syz-cluster/pkg/controller/api.go @@ -202,6 +202,7 @@ func (c APIServer) uploadSession(w http.ResponseWriter, r *http.Request) { func (c APIServer) getTrees(w http.ResponseWriter, r *http.Request) { api.ReplyJSON(w, &api.TreesResp{ - Trees: api.DefaultTrees, + Trees: api.DefaultTrees, + FuzzConfigs: api.FuzzConfigs, }) } diff --git a/syz-cluster/pkg/triage/fuzz_config.go b/syz-cluster/pkg/triage/fuzz_config.go new file mode 100644 index 000000000..fbe576162 --- /dev/null +++ b/syz-cluster/pkg/triage/fuzz_config.go @@ -0,0 +1,28 @@ +// Copyright 2025 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 triage + +import ( + "strings" + + "github.com/google/syzkaller/syz-cluster/pkg/api" +) + +func SelectFuzzConfig(series *api.Series, fuzzConfigs []*api.FuzzConfig) *api.FuzzConfig { + seriesCc := map[string]bool{} + for _, cc := range series.Cc { + seriesCc[strings.ToLower(cc)] = true + } + for _, config := range fuzzConfigs { + intersects := false + for _, cc := range config.EmailLists { + intersects = intersects || seriesCc[cc] + } + if len(config.EmailLists) != 0 && !intersects { + continue + } + return config + } + return nil +} diff --git a/syz-cluster/pkg/triage/fuzz_config_test.go b/syz-cluster/pkg/triage/fuzz_config_test.go new file mode 100644 index 000000000..7a58cf464 --- /dev/null +++ b/syz-cluster/pkg/triage/fuzz_config_test.go @@ -0,0 +1,51 @@ +// Copyright 2025 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 triage + +import ( + "testing" + + "github.com/google/syzkaller/syz-cluster/pkg/api" + "github.com/stretchr/testify/assert" +) + +func TestSelectFuzzConfig(t *testing.T) { + configs := []*api.FuzzConfig{ + { + Name: "bpf", + EmailLists: []string{"bpf@list"}, + }, + { + Name: "net", + EmailLists: []string{"net@list"}, + }, + { + Name: "mainline", + EmailLists: nil, + }, + } + tests := []struct { + testName string + result string + series *api.Series + }{ + { + testName: "select-first", + result: "bpf", + series: &api.Series{Cc: []string{"bpf@list", "net@list"}}, + }, + { + testName: "fallback", + result: "mainline", + series: &api.Series{Cc: []string{"unknown@list"}}, + }, + } + + for _, test := range tests { + t.Run(test.testName, func(t *testing.T) { + ret := SelectFuzzConfig(test.series, configs) + assert.Equal(t, test.result, ret.Name) + }) + } +} diff --git a/syz-cluster/pkg/triage/tree.go b/syz-cluster/pkg/triage/tree.go index 4d0faeeaf..6cd920429 100644 --- a/syz-cluster/pkg/triage/tree.go +++ b/syz-cluster/pkg/triage/tree.go @@ -40,11 +40,8 @@ func SelectTrees(series *api.Series, trees []*api.Tree) []*api.Tree { result = append(result, tree) } sort.SliceStable(result, func(i, j int) bool { - a, b := result[i], result[j] - if tagsMap[a.Name] != tagsMap[b.Name] { - return tagsMap[a.Name] - } - return a.Priority > b.Priority + // First the trees from the patch subject, then everything else. + return tagsMap[result[i].Name] && !tagsMap[result[j].Name] }) return result } diff --git a/syz-cluster/pkg/triage/tree_test.go b/syz-cluster/pkg/triage/tree_test.go index 81352700b..7bd9ed3ed 100644 --- a/syz-cluster/pkg/triage/tree_test.go +++ b/syz-cluster/pkg/triage/tree_test.go @@ -13,30 +13,25 @@ import ( func TestSelectTrees(t *testing.T) { trees := []*api.Tree{ { - Name: "mainline", - EmailLists: nil, - Priority: 0, - }, - { - Name: "net", - EmailLists: []string{"net@list"}, - Priority: 1, + Name: "bpf", + EmailLists: []string{"bpf@list"}, }, { Name: "wireless", EmailLists: []string{"wireless@list"}, - Priority: 2, }, { - Name: "bpf", - EmailLists: []string{"bpf@list"}, - Priority: 3, + Name: "net", + EmailLists: []string{"net@list"}, }, { Name: "test", - Priority: api.TreePriorityNever, EmailLists: []string{"test@list"}, }, + { + Name: "mainline", + EmailLists: nil, + }, } tests := []struct { testName string diff --git a/syz-cluster/workflow/triage-step/main.go b/syz-cluster/workflow/triage-step/main.go index 6aaeab84a..58464ad41 100644 --- a/syz-cluster/workflow/triage-step/main.go +++ b/syz-cluster/workflow/triage-step/main.go @@ -73,13 +73,19 @@ func getVerdict(ctx context.Context, tracer debugtracer.DebugTracer, client *api SkipReason: "no suitable base kernel trees found", }, nil } + fuzzConfig := triage.SelectFuzzConfig(series, treesResp.FuzzConfigs) + if fuzzConfig == nil { + return &api.TriageResult{ + SkipReason: "no suitable fuzz config found", + }, nil + } var triageResult *api.TriageResult for _, tree := range selectedTrees { tracer.Log("considering tree %q", tree.Name) arch := "amd64" lastBuild, err := client.LastBuild(ctx, &api.LastBuildReq{ Arch: arch, - ConfigName: tree.KernelConfig, + ConfigName: fuzzConfig.KernelConfig, TreeName: tree.Name, Status: api.BuildSuccess, }) @@ -107,16 +113,16 @@ func getVerdict(ctx context.Context, tracer debugtracer.DebugTracer, client *api base := api.BuildRequest{ TreeName: tree.Name, TreeURL: tree.URL, - ConfigName: tree.KernelConfig, + ConfigName: fuzzConfig.KernelConfig, CommitHash: result.Commit, Arch: arch, } triageResult = &api.TriageResult{ - Fuzz: &api.FuzzConfig{ + Fuzz: &api.FuzzTask{ Base: base, Patched: base, - Config: tree.FuzzConfig, - CorpusURL: tree.CorpusURL(), + Config: fuzzConfig.Name, + CorpusURL: fuzzConfig.CorpusURL, }, } triageResult.Fuzz.Patched.SeriesID = series.ID |
