aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/bisect/bisect.go3
-rw-r--r--pkg/build/build.go11
-rw-r--r--pkg/report/linux.go23
-rw-r--r--pkg/report/report.go5
-rw-r--r--pkg/vcs/git.go16
-rw-r--r--pkg/vcs/git_repo_test.go4
-rw-r--r--pkg/vcs/git_test.go6
-rw-r--r--pkg/vcs/linux.go54
-rw-r--r--pkg/vcs/vcs.go55
-rw-r--r--pkg/vcs/vcs_test.go37
-rw-r--r--syz-ci/jobs.go4
-rw-r--r--syz-ci/manager.go12
-rw-r--r--syz-manager/manager.go28
-rw-r--r--tools/syz-symbolize/symbolize.go4
14 files changed, 188 insertions, 74 deletions
diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go
index 281969b5d..cf6bae5bc 100644
--- a/pkg/bisect/bisect.go
+++ b/pkg/bisect/bisect.go
@@ -180,7 +180,8 @@ func runImpl(cfg *Config, repo vcs.Repo, inst instance.Env) (*Result, error) {
}
com := res.Commits[0]
env.log("first %v commit: %v %v", what, com.Hash, com.Title)
- env.log("cc: %q", com.CC)
+ env.log("recipients (to): %q", com.Recipients.GetEmails(vcs.To))
+ env.log("recipients (cc): %q", com.Recipients.GetEmails(vcs.Cc))
if res.Report != nil {
env.log("crash: %v\n%s", res.Report.Title, res.Report.Report)
}
diff --git a/pkg/build/build.go b/pkg/build/build.go
index ffc5b0703..370bf25ab 100644
--- a/pkg/build/build.go
+++ b/pkg/build/build.go
@@ -15,6 +15,7 @@ import (
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/report"
+ "github.com/google/syzkaller/pkg/vcs"
)
// Params is input arguments for the Image function.
@@ -90,10 +91,10 @@ func Clean(targetOS, targetArch, vmType, kernelDir string) error {
}
type KernelError struct {
- Report []byte
- Output []byte
- Maintainers []string
- guiltyFile string
+ Report []byte
+ Output []byte
+ Recipients vcs.Recipients
+ guiltyFile string
}
func (err *KernelError) Error() string {
@@ -195,7 +196,7 @@ func extractRootCause(err error, OS, kernelSrc string) error {
if err != nil {
kernelErr.Output = append(kernelErr.Output, err.Error()...)
}
- kernelErr.Maintainers = maintainers
+ kernelErr.Recipients = maintainers
}
return kernelErr
}
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index de906adf9..85b62a89b 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -7,7 +7,6 @@ import (
"bufio"
"bytes"
"fmt"
- "net/mail"
"path/filepath"
"regexp"
"strconv"
@@ -16,6 +15,7 @@ import (
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/symbolizer"
+ "github.com/google/syzkaller/pkg/vcs"
)
type linux struct {
@@ -351,7 +351,7 @@ func (ctx *linux) Symbolize(rep *Report) error {
if err != nil {
return err
}
- rep.Maintainers = maintainers
+ rep.Recipients = maintainers
}
return nil
}
@@ -466,14 +466,14 @@ nextFile:
return ""
}
-func (ctx *linux) getMaintainers(file string) ([]string, error) {
+func (ctx *linux) getMaintainers(file string) (vcs.Recipients, error) {
if ctx.kernelSrc == "" {
return nil, nil
}
return GetLinuxMaintainers(ctx.kernelSrc, file)
}
-func GetLinuxMaintainers(kernelSrc, file string) ([]string, error) {
+func GetLinuxMaintainers(kernelSrc, file string) (vcs.Recipients, error) {
mtrs, err := getMaintainersImpl(kernelSrc, file, false)
if err != nil {
return nil, err
@@ -487,9 +487,9 @@ func GetLinuxMaintainers(kernelSrc, file string) ([]string, error) {
return mtrs, nil
}
-func getMaintainersImpl(kernelSrc, file string, blame bool) ([]string, error) {
+func getMaintainersImpl(kernelSrc, file string, blame bool) (vcs.Recipients, error) {
// See #1441 re --git-min-percent.
- args := []string{"--no-n", "--no-rolestats", "--git-min-percent=15"}
+ args := []string{"--git-min-percent=15"}
if blame {
args = append(args, "--git-blame")
}
@@ -499,16 +499,7 @@ func getMaintainersImpl(kernelSrc, file string, blame bool) ([]string, error) {
if err != nil {
return nil, err
}
- lines := strings.Split(string(output), "\n")
- var mtrs []string
- for _, line := range lines {
- addr, err := mail.ParseAddress(line)
- if err != nil {
- continue
- }
- mtrs = append(mtrs, addr.Address)
- }
- return mtrs, nil
+ return vcs.ParseMaintainersLinux(output), nil
}
func (ctx *linux) extractFiles(report []byte) []string {
diff --git a/pkg/report/report.go b/pkg/report/report.go
index fa2b887e6..f6fbd08a3 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -13,6 +13,7 @@ import (
"strings"
"github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/pkg/vcs"
"github.com/google/syzkaller/sys/targets"
)
@@ -50,8 +51,8 @@ type Report struct {
Corrupted bool
// CorruptedReason contains reason why the report is marked as corrupted.
CorruptedReason string
- // Maintainers is list of maintainer emails (filled in by Symbolize).
- Maintainers []string
+ // Recipients is a list of RecipientInfo with Email, Display Name, and type.
+ Recipients vcs.Recipients
// guiltyFile is the source file that we think is to blame for the crash (filled in by Symbolize).
guiltyFile string
// reportPrefixLen is length of additional prefix lines that we added before actual crash report.
diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go
index 57cc215c6..ae4325a7f 100644
--- a/pkg/vcs/git.go
+++ b/pkg/vcs/git.go
@@ -196,8 +196,8 @@ func gitParseCommit(output, user, domain []byte, ignoreCC map[string]bool) (*Com
if err != nil {
return nil, fmt.Errorf("failed to parse date in git log output: %v\n%q", err, output)
}
- cc := make(map[string]bool)
- cc[strings.ToLower(string(lines[2]))] = true
+ recipients := make(map[string]bool)
+ recipients[strings.ToLower(string(lines[2]))] = true
var tags []string
// Use summary line + all description lines.
for _, line := range append([][]byte{lines[1]}, lines[6:]...) {
@@ -235,15 +235,15 @@ func gitParseCommit(output, user, domain []byte, ignoreCC map[string]bool) (*Com
if ignoreCC[email] {
continue
}
- cc[email] = true
+ recipients[email] = true
break
}
}
- sortedCC := make([]string, 0, len(cc))
- for addr := range cc {
- sortedCC = append(sortedCC, addr)
+ sortedRecipients := make(Recipients, 0, len(recipients))
+ for addr := range recipients {
+ sortedRecipients = append(sortedRecipients, RecipientInfo{mail.Address{Address: addr}, To})
}
- sort.Strings(sortedCC)
+ sort.Sort(sortedRecipients)
parents := strings.Split(string(lines[5]), " ")
com := &Commit{
Hash: string(lines[0]),
@@ -251,7 +251,7 @@ func gitParseCommit(output, user, domain []byte, ignoreCC map[string]bool) (*Com
Author: string(lines[2]),
AuthorName: string(lines[3]),
Parents: parents,
- CC: sortedCC,
+ Recipients: sortedRecipients,
Tags: tags,
Date: date,
}
diff --git a/pkg/vcs/git_repo_test.go b/pkg/vcs/git_repo_test.go
index 2bb8570d1..4deb1a381 100644
--- a/pkg/vcs/git_repo_test.go
+++ b/pkg/vcs/git_repo_test.go
@@ -167,8 +167,8 @@ func checkCommit(t *testing.T, idx int, test testCommit, com *Commit, checkTags
if userName != com.AuthorName {
t.Errorf("#%v: want author name %q, got %q", idx, userName, com.Author)
}
- if diff := cmp.Diff(test.cc, com.CC); diff != "" {
- t.Logf("%#v", com.CC)
+ if diff := cmp.Diff(test.cc, com.Recipients.GetEmails(To)); diff != "" {
+ t.Logf("%#v", com.Recipients)
t.Error(diff)
}
if diff := cmp.Diff(test.tags, com.Tags); checkTags && diff != "" {
diff --git a/pkg/vcs/git_test.go b/pkg/vcs/git_test.go
index eb4ca5328..62a5ee06f 100644
--- a/pkg/vcs/git_test.go
+++ b/pkg/vcs/git_test.go
@@ -37,7 +37,7 @@ Signed-off-by: Linux Master <linux@linux-foundation.org>
Title: "rbtree: include rcu.h",
Author: "foobar@foobar.de",
AuthorName: "Foo Bar",
- CC: []string{
+ Recipients: NewRecipients([]string{
"and@me.com",
"another@email.de",
"foobar@foobar.de",
@@ -46,7 +46,7 @@ Signed-off-by: Linux Master <linux@linux-foundation.org>
"name@name.com",
"subsystem@reviewer.com",
"yetanother@email.org",
- },
+ }, To),
Date: time.Date(2018, 5, 11, 16, 02, 14, 0, time.FixedZone("", -7*60*60)),
},
}
@@ -70,7 +70,7 @@ Signed-off-by: Linux Master <linux@linux-foundation.org>
if com.Author != res.Author {
t.Fatalf("want author %q, got %q", com.Author, res.Author)
}
- if diff := cmp.Diff(com.CC, res.CC); diff != "" {
+ if diff := cmp.Diff(com.Recipients, res.Recipients); diff != "" {
t.Fatalf("bad CC: %v", diff)
}
if !com.Date.Equal(res.Date) {
diff --git a/pkg/vcs/linux.go b/pkg/vcs/linux.go
index a9a823621..069493411 100644
--- a/pkg/vcs/linux.go
+++ b/pkg/vcs/linux.go
@@ -12,12 +12,12 @@ import (
"net/mail"
"os"
"path/filepath"
+ "regexp"
"sort"
"strconv"
"strings"
"time"
- "github.com/google/syzkaller/pkg/email"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/prog"
)
@@ -214,21 +214,22 @@ func (ctx *linux) Bisect(bad, good string, trace io.Writer, pred func() (BisectR
}
func (ctx *linux) addMaintainers(com *Commit) {
- if len(com.CC) > 2 {
+ if len(com.Recipients) > 2 {
return
}
- list := ctx.getMaintainers(com.Hash, false)
- if len(list) < 3 {
- list = ctx.getMaintainers(com.Hash, true)
+ mtrs := ctx.getMaintainers(com.Hash, false)
+ if len(mtrs) < 3 {
+ mtrs = ctx.getMaintainers(com.Hash, true)
}
- com.CC = email.MergeEmailLists(com.CC, list)
+ com.Recipients = append(com.Recipients, mtrs...)
+ sort.Sort(com.Recipients)
}
-func (ctx *linux) getMaintainers(hash string, blame bool) []string {
+func (ctx *linux) getMaintainers(hash string, blame bool) Recipients {
// See #1441 re --git-min-percent.
args := "git show " + hash + " | " +
filepath.FromSlash("scripts/get_maintainer.pl") +
- " --no-n --no-rolestats --git-min-percent=20"
+ " --git-min-percent=20"
if blame {
args += " --git-blame"
}
@@ -236,15 +237,42 @@ func (ctx *linux) getMaintainers(hash string, blame bool) []string {
if err != nil {
return nil
}
- var list []string
- for _, line := range strings.Split(string(output), "\n") {
- addr, err := mail.ParseAddress(line)
+ return ParseMaintainersLinux(output)
+}
+
+func ParseMaintainersLinux(text []byte) Recipients {
+ lines := strings.Split(string(text), "\n")
+ reRole := regexp.MustCompile(` \([^)]+\)$`)
+ var mtrs Recipients
+ // LMKL is To by default, but it changes to Cc if there's also a subsystem list.
+ lkmlType := To
+ foundLkml := false
+ for _, line := range lines {
+ role := reRole.FindString(line)
+ address := strings.Replace(line, role, "", 1)
+ addr, err := mail.ParseAddress(address)
if err != nil {
continue
}
- list = append(list, strings.ToLower(addr.Address))
+ var roleType RecipientType
+ if addr.Address == "linux-kernel@vger.kernel.org" {
+ foundLkml = true
+ continue
+ } else if strings.Contains(role, "list") {
+ lkmlType = Cc
+ roleType = To
+ } else if strings.Contains(role, "maintainer") || strings.Contains(role, "supporter") {
+ roleType = To
+ } else {
+ roleType = Cc // Reviewer or other role; default to Cc.
+ }
+ mtrs = append(mtrs, RecipientInfo{*addr, roleType})
+ }
+ if foundLkml {
+ mtrs = append(mtrs, RecipientInfo{mail.Address{Address: "linux-kernel@vger.kernel.org"}, lkmlType})
}
- return list
+ sort.Sort(mtrs)
+ return mtrs
}
const configBisectTag = "# Minimized by syzkaller"
diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go
index e533e2436..4f1887549 100644
--- a/pkg/vcs/vcs.go
+++ b/pkg/vcs/vcs.go
@@ -8,13 +8,66 @@ import (
"bytes"
"fmt"
"io"
+ "net/mail"
"regexp"
+ "sort"
"strings"
"time"
+ "github.com/google/syzkaller/dashboard/dashapi"
"github.com/google/syzkaller/pkg/osutil"
)
+type RecipientType int
+
+const (
+ To RecipientType = iota
+ Cc
+)
+
+func (t RecipientType) String() string {
+ return [...]string{"To", "Cc"}[t]
+}
+
+type RecipientInfo struct {
+ Address mail.Address
+ Type RecipientType
+}
+
+type Recipients []RecipientInfo
+
+func (r Recipients) GetEmails(filter RecipientType) []string {
+ emails := []string{}
+ for _, user := range r {
+ if user.Type == filter {
+ emails = append(emails, user.Address.Address)
+ }
+ }
+ sort.Strings(emails)
+ return emails
+}
+
+func NewRecipients(emails []string, t RecipientType) Recipients {
+ r := Recipients{}
+ for _, e := range emails {
+ r = append(r, RecipientInfo{mail.Address{Address: e}, t})
+ }
+ sort.Sort(r)
+ return r
+}
+
+func (r Recipients) Len() int { return len(r) }
+func (r Recipients) Less(i, j int) bool { return r[i].Address.Address < r[j].Address.Address }
+func (r Recipients) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
+
+func (r Recipients) ToDash() dashapi.Recipients {
+ d := dashapi.Recipients{}
+ for _, user := range r {
+ d = append(d, dashapi.RecipientInfo{Address: user.Address, Type: dashapi.RecipientType(user.Type)})
+ }
+ return d
+}
+
type Repo interface {
// Poll checkouts the specified repository/branch.
// This involves fetching/resetting/cloning as necessary to recover from all possible problems.
@@ -78,7 +131,7 @@ type Commit struct {
Title string
Author string
AuthorName string
- CC []string
+ Recipients Recipients
Tags []string
Parents []string
Date time.Time
diff --git a/pkg/vcs/vcs_test.go b/pkg/vcs/vcs_test.go
index 90df3c42e..7cfb7347a 100644
--- a/pkg/vcs/vcs_test.go
+++ b/pkg/vcs/vcs_test.go
@@ -4,7 +4,10 @@
package vcs
import (
+ "net/mail"
"testing"
+
+ "github.com/google/go-cmp/cmp"
)
func TestCanonicalizeCommit(t *testing.T) {
@@ -155,3 +158,37 @@ func TestCommitLink(t *testing.T) {
}
}
}
+
+func TestParse(t *testing.T) {
+ // nolint: lll
+ test1 := []byte(`Foo (Maintainer) Bar <a@email.com> (maintainer:KERNEL)
+ Foo Bar(Reviewer) <b@email.com> (reviewer:KERNEL)
+ <somelist@list.com> (open list:FOO)
+ "Supporter Foo" <c@email.com> (supporter:KERNEL)
+ linux-kernel@vger.kernel.org (open list)`)
+ // nolint: lll
+ test2 := []byte(`Foo (Maintainer) Bar <a@email.com> (maintainer:KERNEL)
+ Foo Bar(Reviewer) <b@email.com> (reviewer:KERNEL)
+ "Supporter Foo" <c@email.com> (supporter:KERNEL)
+ linux-kernel@vger.kernel.org (open list)`)
+
+ maintainers1 := Recipients{{mail.Address{Name: "Foo (Maintainer) Bar", Address: "a@email.com"}, To},
+ {mail.Address{Name: "Foo Bar(Reviewer)", Address: "b@email.com"}, Cc},
+ {mail.Address{Name: "Supporter Foo", Address: "c@email.com"}, To},
+ {mail.Address{Name: "", Address: "linux-kernel@vger.kernel.org"}, Cc},
+ {mail.Address{Name: "", Address: "somelist@list.com"}, To}}
+ maintainers2 := Recipients{{mail.Address{Name: "Foo (Maintainer) Bar", Address: "a@email.com"}, To},
+ {mail.Address{Name: "Foo Bar(Reviewer)", Address: "b@email.com"}, Cc},
+ {mail.Address{Name: "Supporter Foo", Address: "c@email.com"}, To},
+ {mail.Address{Name: "", Address: "linux-kernel@vger.kernel.org"}, To}}
+
+ if diff := cmp.Diff(ParseMaintainersLinux(test1), maintainers1); diff != "" {
+ t.Fatal(diff)
+ }
+ if diff := cmp.Diff(ParseMaintainersLinux(test2), maintainers2); diff != "" {
+ t.Fatal(diff)
+ }
+ if diff := cmp.Diff(ParseMaintainersLinux([]byte("")), Recipients(nil)); diff != "" {
+ t.Fatal(diff)
+ }
+}
diff --git a/syz-ci/jobs.go b/syz-ci/jobs.go
index 90c07be3e..faa35c439 100644
--- a/syz-ci/jobs.go
+++ b/syz-ci/jobs.go
@@ -438,7 +438,7 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
Title: com.Title,
Author: com.Author,
AuthorName: com.AuthorName,
- CC: com.CC,
+ Recipients: com.Recipients.ToDash(),
Date: com.Date,
})
}
@@ -469,7 +469,7 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
resp.CrashReport = res.Report.Report
resp.CrashLog = res.Report.Output
if len(resp.Commits) != 0 {
- resp.Commits[0].CC = append(resp.Commits[0].CC, res.Report.Maintainers...)
+ resp.Commits[0].Recipients = append(resp.Commits[0].Recipients, res.Report.Recipients.ToDash()...)
} else {
// If there is a report and there is no commit, it means a crash
// occurred on HEAD(for BisectFix) and oldest tested release(for BisectCause).
diff --git a/syz-ci/manager.go b/syz-ci/manager.go
index 1e3c95c70..2d7d6e8c7 100644
--- a/syz-ci/manager.go
+++ b/syz-ci/manager.go
@@ -313,7 +313,7 @@ func (mgr *Manager) build(kernelCommit *vcs.Commit) error {
case *build.KernelError:
rep.Report = err1.Report
rep.Output = err1.Output
- rep.Maintainers = err1.Maintainers
+ rep.Recipients = err1.Recipients
case *osutil.VerboseError:
rep.Report = []byte(err1.Title)
rep.Output = err1.Output
@@ -440,11 +440,11 @@ func (mgr *Manager) reportBuildError(rep *report.Report, info *BuildInfo, imageD
req := &dashapi.BuildErrorReq{
Build: *build,
Crash: dashapi.Crash{
- Title: rep.Title,
- Corrupted: false, // Otherwise they get merged with other corrupted reports.
- Maintainers: rep.Maintainers,
- Log: rep.Output,
- Report: rep.Report,
+ Title: rep.Title,
+ Corrupted: false, // Otherwise they get merged with other corrupted reports.
+ Recipients: rep.Recipients.ToDash(),
+ Log: rep.Output,
+ Report: rep.Report,
},
}
return mgr.dash.ReportBuildError(req)
diff --git a/syz-manager/manager.go b/syz-manager/manager.go
index 9ed3ff0e0..ff6855bb3 100644
--- a/syz-manager/manager.go
+++ b/syz-manager/manager.go
@@ -645,12 +645,12 @@ func (mgr *Manager) saveCrash(crash *Crash) bool {
return true
}
dc := &dashapi.Crash{
- BuildID: mgr.cfg.Tag,
- Title: crash.Title,
- Corrupted: crash.Corrupted,
- Maintainers: crash.Maintainers,
- Log: crash.Output,
- Report: crash.Report.Report,
+ BuildID: mgr.cfg.Tag,
+ Title: crash.Title,
+ Corrupted: crash.Corrupted,
+ Recipients: crash.Recipients.ToDash(),
+ Log: crash.Output,
+ Report: crash.Report.Report,
}
resp, err := mgr.dash.ReportCrash(dc)
if err != nil {
@@ -811,14 +811,14 @@ func (mgr *Manager) saveRepro(res *repro.Result, stats *repro.Stats, hub bool) {
// so maybe corrupted report detection is broken.
// 3. Reproduction is expensive so it's good to persist the result.
dc := &dashapi.Crash{
- BuildID: mgr.cfg.Tag,
- Title: res.Report.Title,
- Maintainers: res.Report.Maintainers,
- Log: res.Report.Output,
- Report: res.Report.Report,
- ReproOpts: res.Opts.Serialize(),
- ReproSyz: res.Prog.Serialize(),
- ReproC: cprogText,
+ BuildID: mgr.cfg.Tag,
+ Title: res.Report.Title,
+ Recipients: res.Report.Recipients.ToDash(),
+ Log: res.Report.Output,
+ Report: res.Report.Report,
+ ReproOpts: res.Opts.Serialize(),
+ ReproSyz: res.Prog.Serialize(),
+ ReproC: cprogText,
}
if _, err := mgr.dash.ReportCrash(dc); err != nil {
log.Logf(0, "failed to report repro to dashboard: %v", err)
diff --git a/tools/syz-symbolize/symbolize.go b/tools/syz-symbolize/symbolize.go
index a98ffcf3c..5a1d21d37 100644
--- a/tools/syz-symbolize/symbolize.go
+++ b/tools/syz-symbolize/symbolize.go
@@ -15,6 +15,7 @@ import (
"github.com/google/syzkaller/pkg/mgrconfig"
"github.com/google/syzkaller/pkg/osutil"
"github.com/google/syzkaller/pkg/report"
+ "github.com/google/syzkaller/pkg/vcs"
)
var (
@@ -69,7 +70,8 @@ func main() {
}
fmt.Printf("TITLE: %v\n", rep.Title)
fmt.Printf("CORRUPTED: %v (%v)\n", rep.Corrupted, rep.CorruptedReason)
- fmt.Printf("MAINTAINERS: %v\n", rep.Maintainers)
+ fmt.Printf("MAINTAINERS (TO): %v\n", rep.Recipients.GetEmails(vcs.To))
+ fmt.Printf("MAINTAINERS (CC): %v\n", rep.Recipients.GetEmails(vcs.Cc))
fmt.Printf("\n")
os.Stdout.Write(rep.Report)
fmt.Printf("\n\n")