aboutsummaryrefslogtreecommitdiffstats
path: root/syz-cluster
diff options
context:
space:
mode:
authorAleksandr Nogikh <nogikh@google.com>2025-07-30 15:38:19 +0200
committerAleksandr Nogikh <nogikh@google.com>2025-07-31 12:54:40 +0000
commita9814992554d3a157c61716e82aace97ad7860af (patch)
tree2c6c57cef0f9e43a38353da6b3b651e8b494a3e3 /syz-cluster
parent3526e777e9f189bbd898e10cf3042abe4c75f179 (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.go133
-rw-r--r--syz-cluster/pkg/api/client.go3
-rw-r--r--syz-cluster/pkg/controller/api.go3
-rw-r--r--syz-cluster/pkg/triage/fuzz_config.go28
-rw-r--r--syz-cluster/pkg/triage/fuzz_config_test.go51
-rw-r--r--syz-cluster/pkg/triage/tree.go7
-rw-r--r--syz-cluster/pkg/triage/tree_test.go21
-rw-r--r--syz-cluster/workflow/triage-step/main.go16
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