Skip to content

Commit 01daca5

Browse files
committed
syz-cluster: prioritize blob-based base commits
Consider Cc'd mailing lists when selecting the exact base commit. Among the base commits determined based on blob sha value from the git patch, first select the ones that match both the trees of the Cc'd subsystems and their primary branches. If it gives no exact match, select a base commit that comes from a tree of a Cc'd subsystem. As fallback, take any subsystem tree. This should prevent valid, but suprising patch series triage results.
1 parent ff638bb commit 01daca5

File tree

6 files changed

+145
-38
lines changed

6 files changed

+145
-38
lines changed

syz-cluster/pkg/triage/commit.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,40 @@ func (cs *CommitSelector) Select(series *api.Series, tree *api.Tree, lastBuild *
8080
}
8181
return SelectResult{Reason: reasonNotApplies}, nil
8282
}
83+
84+
func FromBaseCommits(series *api.Series, baseCommits []*vcs.BaseCommit, trees []*api.Tree) (*api.Tree, string) {
85+
// Technically, any one of baseCommits could be a good match.
86+
// However, the developers have their own expectations regarding
87+
// what tree and what branch are actually preferred there.
88+
// So, among baseCommits, we still give preference to those that
89+
// align with the mailing lists Cc'd by the patch series.
90+
tree, commit := bestCommit(baseCommits, SelectTrees(series, trees))
91+
if tree != nil {
92+
return tree, commit
93+
}
94+
return bestCommit(baseCommits, trees)
95+
}
96+
97+
func bestCommit(baseCommits []*vcs.BaseCommit, trees []*api.Tree) (*api.Tree, string) {
98+
retTreeIdx, retSameBranch, retCommit := -1, false, ""
99+
for _, commit := range baseCommits {
100+
for _, commitBranch := range commit.Branches {
101+
treeIdx, branch := FindTree(trees, commitBranch)
102+
if treeIdx < 0 {
103+
continue
104+
}
105+
sameBranch := branch == trees[treeIdx].Branch
106+
// If, for the same tree, we also have matched the branch, even better.
107+
if retTreeIdx < 0 || treeIdx < retTreeIdx ||
108+
treeIdx == retTreeIdx && !retSameBranch && sameBranch {
109+
retTreeIdx = treeIdx
110+
retSameBranch = sameBranch
111+
retCommit = commit.Hash
112+
}
113+
}
114+
}
115+
if retTreeIdx < 0 {
116+
return nil, ""
117+
}
118+
return trees[retTreeIdx], retCommit
119+
}

syz-cluster/pkg/triage/commit_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,80 @@ func TestCommitSelector(t *testing.T) {
8787
}
8888
}
8989

90+
func TestFromBaseCommits(t *testing.T) {
91+
trees := []*api.Tree{
92+
{
93+
Name: "A",
94+
Branch: "master",
95+
EmailLists: []string{"list_A"},
96+
},
97+
{
98+
Name: "B",
99+
Branch: "master",
100+
EmailLists: []string{"list_B"},
101+
},
102+
{
103+
Name: "C",
104+
Branch: "main",
105+
EmailLists: []string{"list_C"},
106+
},
107+
{
108+
Name: "D",
109+
Branch: "main",
110+
EmailLists: nil,
111+
},
112+
}
113+
commits := []*vcs.BaseCommit{
114+
{
115+
Commit: &vcs.Commit{Hash: "first"},
116+
Branches: []string{"C/main"},
117+
},
118+
{
119+
Commit: &vcs.Commit{Hash: "second"},
120+
Branches: []string{"A/other", "B/other"},
121+
},
122+
{
123+
Commit: &vcs.Commit{Hash: "third"},
124+
Branches: []string{"A/master", "A/other"},
125+
},
126+
}
127+
t.Run("best branch", func(t *testing.T) {
128+
tree, commit := FromBaseCommits(&api.Series{
129+
Cc: []string{"list_A"},
130+
}, commits, trees)
131+
assert.Equal(t, "A", tree.Name)
132+
assert.Equal(t, "third", commit)
133+
})
134+
t.Run("best tree", func(t *testing.T) {
135+
// Even though C/main matches perfectly, there's a commit
136+
// in the higher prio tree B.
137+
tree, commit := FromBaseCommits(&api.Series{
138+
Cc: []string{"list_B", "list_C"},
139+
}, commits, trees)
140+
assert.Equal(t, "B", tree.Name)
141+
assert.Equal(t, "second", commit)
142+
})
143+
t.Run("any tree", func(t *testing.T) {
144+
// If no trees matching by Cc'd list are in the base commit list,
145+
// consider all trees.
146+
commits := []*vcs.BaseCommit{
147+
{
148+
Commit: &vcs.Commit{Hash: "first"},
149+
Branches: []string{"B/main"},
150+
},
151+
{
152+
Commit: &vcs.Commit{Hash: "second"},
153+
Branches: []string{"C/main"},
154+
},
155+
}
156+
tree, commit := FromBaseCommits(&api.Series{
157+
Cc: []string{"list_A"},
158+
}, commits, trees)
159+
assert.Equal(t, "B", tree.Name)
160+
assert.Equal(t, "first", commit)
161+
})
162+
}
163+
90164
func date(date string) time.Time {
91165
t, err := time.Parse("2006-Jan-02", date)
92166
if err != nil {

syz-cluster/pkg/triage/git.go

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ func (ops *GitTreeOps) ApplySeries(commit string, patches [][]byte) error {
5757
return nil
5858
}
5959

60-
func (ops *GitTreeOps) BaseForDiff(patch []byte, tracer debugtracer.DebugTracer) (*vcs.BaseCommit, error) {
61-
list, err := ops.Git.BaseForDiff(patch, tracer)
62-
if len(list) == 0 || err != nil {
63-
return nil, err
64-
}
65-
return list[0], nil
60+
func (ops *GitTreeOps) BaseForDiff(patch []byte, tracer debugtracer.DebugTracer) ([]*vcs.BaseCommit, error) {
61+
return ops.Git.BaseForDiff(patch, tracer)
6662
}

syz-cluster/pkg/triage/tree.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ func SelectTrees(series *api.Series, trees []*api.Tree) []*api.Tree {
4646
return result
4747
}
4848

49-
func TreeFromBranch(trees []*api.Tree, branch string) *api.Tree {
50-
for _, tree := range trees {
51-
if strings.HasPrefix(branch, tree.Name+"/") {
52-
return tree
49+
func FindTree(trees []*api.Tree, branch string) (int, string) {
50+
for idx, tree := range trees {
51+
branchName, ok := strings.CutPrefix(branch, tree.Name+"/")
52+
if ok {
53+
return idx, branchName
5354
}
5455
}
55-
return nil
56+
return -1, ""
5657
}

syz-cluster/pkg/triage/tree_test.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ func TestSelectTrees(t *testing.T) {
7676
}
7777

7878
func TestTreeFromBranch(t *testing.T) {
79-
treeA, treeB := &api.Tree{Name: "a"}, &api.Tree{Name: "b"}
80-
assert.Equal(t, treeA, TreeFromBranch([]*api.Tree{treeA, treeB}, "a/some_branch"))
81-
assert.Equal(t, treeB, TreeFromBranch([]*api.Tree{treeA, treeB}, "b/some_branch"))
79+
trees := []*api.Tree{{Name: "a"}, {Name: "b"}}
80+
treeIdx, branch := FindTree(trees, "a/some_branch")
81+
assert.Equal(t, 0, treeIdx)
82+
assert.Equal(t, "some_branch", branch)
83+
treeIdx, branch = FindTree(trees, "b/some_branch")
84+
assert.Equal(t, 1, treeIdx)
85+
assert.Equal(t, "some_branch", branch)
86+
treeIdx, _ = FindTree(trees, "c/some_branch")
87+
assert.Equal(t, -1, treeIdx)
8288
}

syz-cluster/workflow/triage-step/main.go

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,6 @@ func (triager *seriesTriager) GetVerdict(ctx context.Context, sessionID string)
7979
if err != nil {
8080
return nil, fmt.Errorf("failed to query trees: %w", err)
8181
}
82-
selectedTrees := triage.SelectTrees(series, treesResp.Trees)
83-
if len(selectedTrees) == 0 {
84-
return &api.TriageResult{
85-
SkipReason: "no suitable base kernel trees found",
86-
}, nil
87-
}
8882
fuzzConfigs := triage.MergeKernelFuzzConfigs(triage.SelectFuzzConfigs(series, treesResp.FuzzTargets))
8983
if len(fuzzConfigs) == 0 {
9084
return &api.TriageResult{
@@ -93,7 +87,7 @@ func (triager *seriesTriager) GetVerdict(ctx context.Context, sessionID string)
9387
}
9488
ret := &api.TriageResult{}
9589
for _, campaign := range fuzzConfigs {
96-
fuzzTask, err := triager.prepareFuzzingTask(ctx, series, selectedTrees, campaign)
90+
fuzzTask, err := triager.prepareFuzzingTask(ctx, series, treesResp.Trees, campaign)
9791
var skipErr *SkipTriageError
9892
if errors.As(err, &skipErr) {
9993
ret.SkipReason = skipErr.Reason.Error()
@@ -123,7 +117,7 @@ func (triager *seriesTriager) prepareFuzzingTask(ctx context.Context, series *ap
123117
}
124118
}
125119
if result != nil {
126-
triager.Log("continuing with %+v", result)
120+
triager.Log("continuing with %v in %v", result.Commit, result.Tree.Name)
127121
base := api.BuildRequest{
128122
TreeName: result.Tree.Name,
129123
TreeURL: result.Tree.URL,
@@ -158,31 +152,30 @@ func (triager *seriesTriager) selectFromBlobs(series *api.Series, trees []*api.T
158152
diff = append(diff, patch.Body...)
159153
diff = append(diff, '\n')
160154
}
161-
base, err := triager.ops.BaseForDiff(diff, triager.DebugTracer)
155+
baseList, err := triager.ops.BaseForDiff(diff, triager.DebugTracer)
162156
if err != nil {
163157
return nil, err
164-
} else if base == nil {
158+
}
159+
tree, commit := triage.FromBaseCommits(series, baseList, trees)
160+
if tree == nil {
165161
triager.Log("no candidate base commit is found")
166162
return nil, nil
167163
}
168-
for _, branch := range base.Branches {
169-
tree := triage.TreeFromBranch(trees, branch)
170-
if tree != nil {
171-
return &SelectResult{
172-
Tree: tree,
173-
Commit: base.Hash,
174-
Arch: fuzzArch,
175-
}, nil
176-
}
177-
}
178-
triager.Log("cannot identify the tree from %q", base.Branches)
179-
return nil, nil
164+
return &SelectResult{
165+
Tree: tree,
166+
Commit: commit,
167+
Arch: fuzzArch,
168+
}, nil
180169
}
181170

182171
func (triager *seriesTriager) selectFromList(ctx context.Context, series *api.Series, trees []*api.Tree,
183172
target *triage.MergedFuzzConfig) (*SelectResult, error) {
184-
skipErr := SkipError("empty tree list")
185-
for _, tree := range trees {
173+
selectedTrees := triage.SelectTrees(series, trees)
174+
if len(selectedTrees) == 0 {
175+
return nil, SkipError("no suitable base kernel trees found")
176+
}
177+
var skipErr error
178+
for _, tree := range selectedTrees {
186179
triager.Log("considering tree %q", tree.Name)
187180
lastBuild, err := triager.client.LastBuild(ctx, &api.LastBuildReq{
188181
Arch: fuzzArch,

0 commit comments

Comments
 (0)