diff options
| -rw-r--r-- | pkg/bisect/bisect.go | 3 | ||||
| -rw-r--r-- | pkg/build/build.go | 11 | ||||
| -rw-r--r-- | pkg/report/linux.go | 23 | ||||
| -rw-r--r-- | pkg/report/report.go | 5 | ||||
| -rw-r--r-- | pkg/vcs/git.go | 16 | ||||
| -rw-r--r-- | pkg/vcs/git_repo_test.go | 4 | ||||
| -rw-r--r-- | pkg/vcs/git_test.go | 6 | ||||
| -rw-r--r-- | pkg/vcs/linux.go | 54 | ||||
| -rw-r--r-- | pkg/vcs/vcs.go | 55 | ||||
| -rw-r--r-- | pkg/vcs/vcs_test.go | 37 | ||||
| -rw-r--r-- | syz-ci/jobs.go | 4 | ||||
| -rw-r--r-- | syz-ci/manager.go | 12 | ||||
| -rw-r--r-- | syz-manager/manager.go | 28 | ||||
| -rw-r--r-- | tools/syz-symbolize/symbolize.go | 4 |
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") |
