aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2019-02-14 10:35:03 +0100
committerDmitry Vyukov <dvyukov@google.com>2019-02-17 15:08:45 +0100
commit3e98cc30803fb5e41504dd08b1325cb074a8a3f2 (patch)
tree5f0a7a4702bedbb9706c158e09e4be1875894625 /pkg
parentf42dee6d5e501a061cdbb807672361369bf28492 (diff)
dashboard/app: poll commits info
This implements 2 features: - syz-ci polls a set of additional repos to discover fixing commits sooner (e.g. it can now discover a fixing commit in netfilter tree before it reaches any of the tested trees). - syz-ci uploads info about commits to dashboard. For example, a user marks a bug as fixed by commit "foo: bar". syz-ci will find this commit in the main namespace repo and upload commmit hash/date/author to dashboard. This in turn allows to show links to fixing commits. Fixes #691 Fixes #610
Diffstat (limited to 'pkg')
-rw-r--r--pkg/html/generated.go13
-rw-r--r--pkg/html/html.go9
-rw-r--r--pkg/vcs/akaros.go12
-rw-r--r--pkg/vcs/fuchsia.go10
-rw-r--r--pkg/vcs/git.go145
-rw-r--r--pkg/vcs/git_repo_test.go180
-rw-r--r--pkg/vcs/git_test.go64
-rw-r--r--pkg/vcs/netbsd.go28
-rw-r--r--pkg/vcs/openbsd.go28
-rw-r--r--pkg/vcs/vcs.go81
-rw-r--r--pkg/vcs/vcs_test.go70
11 files changed, 459 insertions, 181 deletions
diff --git a/pkg/html/generated.go b/pkg/html/generated.go
index 644cbddd9..732ed507a 100644
--- a/pkg/html/generated.go
+++ b/pkg/html/generated.go
@@ -80,6 +80,11 @@ table td, table th {
max-width: 350pt;
}
+.list_table .commit_list {
+ width: 500pt;
+ max-width: 500pt;
+}
+
.list_table .tag {
font-family: monospace;
font-size: 8pt;
@@ -104,8 +109,8 @@ table td, table th {
}
.list_table .kernel {
- width: 60pt;
- max-width: 60pt;
+ width: 80pt;
+ max-width: 80pt;
}
.list_table .maintainers {
@@ -154,6 +159,10 @@ textarea {
width:100%;
font-family: monospace;
}
+
+.mono {
+ font-family: monospace;
+}
`
const js = `
// Copyright 2018 syzkaller project authors. All rights reserved.
diff --git a/pkg/html/html.go b/pkg/html/html.go
index c7f85da88..e985ae977 100644
--- a/pkg/html/html.go
+++ b/pkg/html/html.go
@@ -32,6 +32,7 @@ func CreateGlob(glob string) *template.Template {
}
var funcs = template.FuncMap{
+ "link": link,
"formatTime": FormatTime,
"formatClock": formatClock,
"formatDuration": formatDuration,
@@ -41,6 +42,14 @@ var funcs = template.FuncMap{
"formatShortHash": formatShortHash,
}
+func link(url, text string) template.HTML {
+ text = template.HTMLEscapeString(text)
+ if url != "" {
+ text = fmt.Sprintf(`<a href="%v">%v</a>`, url, text)
+ }
+ return template.HTML(text)
+}
+
func FormatTime(t time.Time) string {
if t.IsZero() {
return ""
diff --git a/pkg/vcs/akaros.go b/pkg/vcs/akaros.go
index aaf9d985e..4f476542a 100644
--- a/pkg/vcs/akaros.go
+++ b/pkg/vcs/akaros.go
@@ -10,7 +10,7 @@ import (
)
type akaros struct {
- git *git
+ *git
dropbear *git
}
@@ -40,15 +40,7 @@ func (ctx *akaros) SwitchCommit(commit string) (*Commit, error) {
return nil, fmt.Errorf("not implemented for akaros")
}
-func (ctx *akaros) HeadCommit() (*Commit, error) {
- return nil, fmt.Errorf("not implemented for akaros")
-}
-
-func (ctx *akaros) ListRecentCommits(baseCommit string) ([]string, error) {
- return ctx.git.ListRecentCommits(baseCommit)
-}
-
-func (ctx *akaros) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) {
+func (ctx *akaros) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
return ctx.git.ExtractFixTagsFromCommits(baseCommit, email)
}
diff --git a/pkg/vcs/fuchsia.go b/pkg/vcs/fuchsia.go
index f9a027f67..04d8e5d36 100644
--- a/pkg/vcs/fuchsia.go
+++ b/pkg/vcs/fuchsia.go
@@ -75,11 +75,19 @@ func (ctx *fuchsia) HeadCommit() (*Commit, error) {
return nil, fmt.Errorf("not implemented for fuchsia")
}
+func (ctx *fuchsia) GetCommitByTitle(title string) (*Commit, error) {
+ return ctx.zircon.GetCommitByTitle(title)
+}
+
+func (ctx *fuchsia) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) {
+ return ctx.zircon.GetCommitsByTitles(titles)
+}
+
func (ctx *fuchsia) ListRecentCommits(baseCommit string) ([]string, error) {
return ctx.zircon.ListRecentCommits(baseCommit)
}
-func (ctx *fuchsia) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) {
+func (ctx *fuchsia) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
return ctx.zircon.ExtractFixTagsFromCommits(baseCommit, email)
}
diff --git a/pkg/vcs/git.go b/pkg/vcs/git.go
index 45a7e340c..4f23bb16c 100644
--- a/pkg/vcs/git.go
+++ b/pkg/vcs/git.go
@@ -148,10 +148,10 @@ func (git *git) getCommit(commit string) (*Commit, error) {
if err != nil {
return nil, err
}
- return gitParseCommit(output)
+ return gitParseCommit(output, nil, nil)
}
-func gitParseCommit(output []byte) (*Commit, error) {
+func gitParseCommit(output, user, domain []byte) (*Commit, error) {
lines := bytes.Split(output, []byte{'\n'})
if len(lines) < 4 || len(lines[0]) != 40 {
return nil, fmt.Errorf("unexpected git log output: %q", output)
@@ -163,7 +163,29 @@ func gitParseCommit(output []byte) (*Commit, error) {
}
cc := make(map[string]bool)
cc[strings.ToLower(string(lines[2]))] = true
+ var tags []string
for _, line := range lines[4:] {
+ if user != nil {
+ userPos := bytes.Index(line, user)
+ if userPos != -1 {
+ domainPos := bytes.Index(line[userPos+len(user)+1:], domain)
+ if domainPos != -1 {
+ startPos := userPos + len(user)
+ endPos := userPos + len(user) + domainPos + 1
+ tag := string(line[startPos:endPos])
+ present := false
+ for _, tag1 := range tags {
+ if tag1 == tag {
+ present = true
+ break
+ }
+ }
+ if !present {
+ tags = append(tags, tag)
+ }
+ }
+ }
+ }
for _, re := range ccRes {
matches := re.FindSubmatchIndex(line)
if matches == nil {
@@ -187,26 +209,82 @@ func gitParseCommit(output []byte) (*Commit, error) {
Title: string(lines[1]),
Author: string(lines[2]),
CC: sortedCC,
+ Tags: tags,
Date: date,
}
return com, nil
}
+func (git *git) GetCommitByTitle(title string) (*Commit, error) {
+ commits, _, err := git.GetCommitsByTitles([]string{title})
+ if err != nil || len(commits) == 0 {
+ return nil, err
+ }
+ return commits[0], nil
+}
+
+func (git *git) GetCommitsByTitles(titles []string) ([]*Commit, []string, error) {
+ var greps []string
+ m := make(map[string]string)
+ for _, title := range titles {
+ canonical := CanonicalizeCommit(title)
+ greps = append(greps, canonical)
+ m[canonical] = title
+ }
+ since := time.Now().Add(-time.Hour * 24 * 365 * 2).Format("01-02-2006")
+ commits, err := git.fetchCommits(since, "HEAD", "", "", greps, true)
+ if err != nil {
+ return nil, nil, err
+ }
+ var results []*Commit
+ for _, com := range commits {
+ canonical := CanonicalizeCommit(com.Title)
+ if orig := m[canonical]; orig != "" {
+ delete(m, canonical)
+ results = append(results, com)
+ com.Title = orig
+ }
+ }
+ var missing []string
+ for _, orig := range m {
+ missing = append(missing, orig)
+ }
+ return results, missing, nil
+}
+
func (git *git) ListRecentCommits(baseCommit string) ([]string, error) {
// On upstream kernel this produces ~11MB of output.
// Somewhat inefficient to collect whole output in a slice
// and then convert to string, but should be bearable.
output, err := runSandboxed(git.dir, "git", "log",
- "--pretty=format:%s", "--no-merges", "-n", "200000", baseCommit)
+ "--pretty=format:%s", "-n", "200000", baseCommit)
if err != nil {
return nil, err
}
return strings.Split(string(output), "\n"), nil
}
-func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) {
+func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
+ user, domain, err := splitEmail(email)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse email %q: %v", email, err)
+ }
+ grep := user + "+.*" + domain
since := time.Now().Add(-time.Hour * 24 * 365).Format("01-02-2006")
- cmd := exec.Command("git", "log", "--no-merges", "--since", since, baseCommit)
+ return git.fetchCommits(since, baseCommit, user, domain, []string{grep}, false)
+}
+
+func (git *git) fetchCommits(since, base, user, domain string, greps []string, fixedStrings bool) ([]*Commit, error) {
+ const commitSeparator = "---===syzkaller-commit-separator===---"
+ args := []string{"log", "--since", since, "--format=%H%n%s%n%ae%n%ad%n%b%n" + commitSeparator}
+ if fixedStrings {
+ args = append(args, "--fixed-strings")
+ }
+ for _, grep := range greps {
+ args = append(args, "--grep", grep)
+ }
+ args = append(args, base)
+ cmd := exec.Command("git", args...)
cmd.Dir = git.dir
stdout, err := cmd.StdoutPipe()
if err != nil {
@@ -217,52 +295,33 @@ func (git *git) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit
}
defer cmd.Wait()
defer cmd.Process.Kill()
- return gitExtractFixTags(stdout, email)
-}
-
-func gitExtractFixTags(r io.Reader, email string) ([]FixCommit, error) {
- user, domain, err := splitEmail(email)
- if err != nil {
- return nil, fmt.Errorf("failed to parse email %q: %v", email, err)
- }
var (
- s = bufio.NewScanner(r)
- commits []FixCommit
- commitTitle = ""
- commitStart = []byte("commit ")
- bodyPrefix = []byte(" ")
- userBytes = []byte(user + "+")
- domainBytes = []byte(domain)
+ s = bufio.NewScanner(stdout)
+ buf = new(bytes.Buffer)
+ separator = []byte(commitSeparator)
+ commits []*Commit
+ userBytes []byte
+ domainBytes []byte
)
+ if user != "" {
+ userBytes = []byte(user + "+")
+ domainBytes = []byte(domain)
+ }
for s.Scan() {
ln := s.Bytes()
- if bytes.HasPrefix(ln, commitStart) {
- commitTitle = ""
+ if !bytes.Equal(ln, separator) {
+ buf.Write(ln)
+ buf.WriteByte('\n')
continue
}
- if !bytes.HasPrefix(ln, bodyPrefix) {
- continue
- }
- ln = ln[len(bodyPrefix):]
- if len(ln) == 0 {
- continue
- }
- if commitTitle == "" {
- commitTitle = string(ln)
- continue
- }
- userPos := bytes.Index(ln, userBytes)
- if userPos == -1 {
- continue
+ com, err := gitParseCommit(buf.Bytes(), userBytes, domainBytes)
+ if err != nil {
+ return nil, err
}
- domainPos := bytes.Index(ln[userPos+len(userBytes)+1:], domainBytes)
- if domainPos == -1 {
- continue
+ if user == "" || len(com.Tags) != 0 {
+ commits = append(commits, com)
}
- startPos := userPos + len(userBytes)
- endPos := userPos + len(userBytes) + domainPos + 1
- tag := string(ln[startPos:endPos])
- commits = append(commits, FixCommit{tag, commitTitle})
+ buf.Reset()
}
return commits, s.Err()
}
diff --git a/pkg/vcs/git_repo_test.go b/pkg/vcs/git_repo_test.go
index 848e369c9..f60bfc737 100644
--- a/pkg/vcs/git_repo_test.go
+++ b/pkg/vcs/git_repo_test.go
@@ -20,7 +20,13 @@ func init() {
os.Setenv("SYZ_DISABLE_SANDBOXING", "yes")
}
+const (
+ userEmail = `test@syzkaller.com`
+ extractFixTagsEmail = `"syzbot" <syzbot@my.mail.com>`
+)
+
func TestGitRepo(t *testing.T) {
+ t.Parallel()
baseDir, err := ioutil.TempDir("", "syz-git-test")
if err != nil {
t.Fatal(err)
@@ -108,6 +114,170 @@ func TestGitRepo(t *testing.T) {
}
}
+func TestMetadata(t *testing.T) {
+ t.Parallel()
+ repoDir, err := ioutil.TempDir("", "syz-git-test")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(repoDir)
+ repo := makeTestRepo(t, repoDir)
+ for i, test := range metadataTests {
+ repo.commitChange(test.description)
+ com, err := repo.repo.HeadCommit()
+ if err != nil {
+ t.Fatal(err)
+ }
+ checkCommit(t, i, test, com, false)
+ }
+ commits, err := repo.repo.ExtractFixTagsFromCommits("HEAD", extractFixTagsEmail)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(metadataTests) != len(commits) {
+ t.Fatalf("want %v commits, got %v", len(metadataTests), len(commits))
+ }
+ for i, test := range metadataTests {
+ checkCommit(t, i, test, commits[len(commits)-i-1], true)
+ for _, title := range []string{test.title, test.title2} {
+ if title == "" {
+ continue
+ }
+ com, err := repo.repo.GetCommitByTitle(title)
+ if err != nil {
+ t.Error(err)
+ } else if com == nil {
+ t.Errorf("no commits found by title %q", title)
+ } else if com.Title != title {
+ t.Errorf("wrong commit %q found by title %q", com.Title, title)
+ }
+ }
+ }
+}
+
+func checkCommit(t *testing.T, idx int, test testCommit, com *Commit, checkTags bool) {
+ if !checkTags {
+ return
+ }
+ if test.title != com.Title {
+ t.Errorf("#%v: want title %q, got %q", idx, test.title, com.Title)
+ }
+ if test.author != com.Author {
+ t.Errorf("#%v: want author %q, got %q", idx, test.author, com.Author)
+ }
+ if diff := cmp.Diff(test.cc, com.CC); diff != "" {
+ t.Logf("%#v", com.CC)
+ t.Error(diff)
+ }
+ if diff := cmp.Diff(test.tags, com.Tags); checkTags && diff != "" {
+ t.Error(diff)
+ }
+}
+
+type testCommit struct {
+ description string
+ title string
+ title2 string
+ author string
+ cc []string
+ tags []string
+}
+
+// nolint: lll
+var metadataTests = []testCommit{
+ {
+ description: `dashboard/app: bump max repros per bug to 10
+
+Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
+`,
+ title: "dashboard/app: bump max repros per bug to 10",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"8e4090902540da8c6e8f"},
+ },
+ {
+ description: `executor: remove dead code
+
+Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
+Reported-by: syzbot <syzbot+a640a0fc325c29c3efcb@my.mail.com>
+`,
+ title: "executor: remove dead code",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"8e4090902540da8c6e8f", "a640a0fc325c29c3efcb"},
+ },
+ {
+ description: `pkg/csource: fix string escaping bug
+
+Reported-and-tested-by: syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com
+Tested-by: syzbot+4234987263748623784623758235@my.mail.com
+`,
+ title: "pkg/csource: fix string escaping bug",
+ author: userEmail,
+ cc: []string{"syzbot+4234987263748623784623758235@my.mail.com", "syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com", userEmail},
+ tags: []string{"8e4090902540da8c6e8fa640a0fc325c29c3efcb", "4234987263748623784623758235"},
+ },
+ {
+ description: `When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic.
+
+ok millert@
+Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com
+`,
+ title: "When freeing a lockf struct that already is part of a linked list, make sure to update the next pointer for the preceding lock. Prevents a double free panic.",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"6dd701dc797b23b8c761"},
+ },
+ {
+ description: `ipmr: properly check rhltable_init() return value
+
+commit 8fb472c09b9d ("ipmr: improve hash scalability")
+added a call to rhltable_init() without checking its return value.
+
+This problem was then later copied to IPv6 and factorized in commit
+0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
+
+Fixes: 8fb472c09b9d ("ipmr: improve hash scalability")
+Fixes: 0bbbf0e7d0e7 ("ipmr, ip6mr: Unite creation of new mr_table")
+Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com
+`,
+ title: "ipmr: properly check rhltable_init() return value",
+ title2: "net-backports: ipmr: properly check rhltable_init() return value",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"6dd701dc797b23b8c761"},
+ },
+ {
+ description: `f2fs: sanity check for total valid node blocks
+
+Reported-by: syzbot+bf9253040425feb155ad@my.mail.com
+Reported-by: syzbot+bf9253040425feb155ad@my.mail.com
+`,
+ title: "f2fs: sanity check for total valid node blocks",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"bf9253040425feb155ad"},
+ },
+ {
+ description: `USB: fix usbmon BUG trigger
+
+Automated tests triggered this by opening usbmon and accessing the
+mmap while simultaneously resizing the buffers. This bug was with
+us since 2006, because typically applications only size the buffers
+once and thus avoid racing. Reported by Kirill A. Shutemov.
+
+Reported-by: <syzbot+f9831b881b3e849829fc@my.mail.com>
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Cc: stable <stable@vger.kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+`,
+ title: "USB: fix usbmon BUG trigger",
+ author: userEmail,
+ cc: []string{userEmail},
+ tags: []string{"f9831b881b3e849829fc"},
+ },
+}
+
func createTestRepo(t *testing.T, baseDir, name string) *testRepo {
repo := makeTestRepo(t, filepath.Join(baseDir, name))
repo.git("checkout", "-b", "master")
@@ -127,6 +297,7 @@ type testRepo struct {
dir string
name string
commits map[string]map[string]*Commit
+ repo *git
}
func makeTestRepo(t *testing.T, dir string) *testRepo {
@@ -138,8 +309,11 @@ func makeTestRepo(t *testing.T, dir string) *testRepo {
dir: dir,
name: filepath.Base(dir),
commits: make(map[string]map[string]*Commit),
+ repo: newGit(dir),
}
repo.git("init")
+ repo.git("config", "--add", "user.email", userEmail)
+ repo.git("config", "--add", "user.name", "Test Syzkaller")
return repo
}
@@ -160,9 +334,13 @@ func (repo *testRepo) commitFileChange(branch, change string) {
if repo.commits[branch] == nil {
repo.commits[branch] = make(map[string]*Commit)
}
- com, err := newGit(repo.dir).HeadCommit()
+ com, err := repo.repo.HeadCommit()
if err != nil {
repo.t.Fatal(err)
}
repo.commits[branch][change] = com
}
+
+func (repo *testRepo) commitChange(description string) {
+ repo.git("commit", "--allow-empty", "-m", description)
+}
diff --git a/pkg/vcs/git_test.go b/pkg/vcs/git_test.go
index 5c036e14c..a22835a69 100644
--- a/pkg/vcs/git_test.go
+++ b/pkg/vcs/git_test.go
@@ -5,11 +5,8 @@ package vcs
import (
"reflect"
- "strings"
"testing"
"time"
-
- "github.com/google/go-cmp/cmp"
)
func TestGitParseCommit(t *testing.T) {
@@ -48,7 +45,7 @@ Signed-off-by: Linux Master <linux@linux-foundation.org>
},
}
for input, com := range tests {
- res, err := gitParseCommit([]byte(input))
+ res, err := gitParseCommit([]byte(input), nil, nil)
if err != nil && com != nil {
t.Fatalf("want %+v, got error: %v", com, err)
}
@@ -120,62 +117,3 @@ v1.
t.Fatalf("got bad tags\ngot: %+v\nwant: %+v", got, want)
}
}
-
-func TestGitExtractFixTags(t *testing.T) {
- commits, err := gitExtractFixTags(strings.NewReader(extractFixTagsInput), extractFixTagsEmail)
- if err != nil {
- t.Fatal(err)
- }
- if diff := cmp.Diff(extractFixTagsOutput, commits); diff != "" {
- t.Fatal(diff)
- }
-}
-
-const extractFixTagsEmail = "\"syzbot\" <syzbot@my.mail.com>"
-
-var extractFixTagsOutput = []FixCommit{
- {"8e4090902540da8c6e8f", "dashboard/app: bump max repros per bug to 10"},
- {"8e4090902540da8c6e8f", "executor: remove dead code"},
- {"a640a0fc325c29c3efcb", "executor: remove dead code"},
- {"8e4090902540da8c6e8fa640a0fc325c29c3efcb", "pkg/csource: fix string escaping bug"},
- {"4234987263748623784623758235", "pkg/csource: fix string escaping bug"},
- {"6dd701dc797b23b8c761", "When freeing a lockf struct that already is part of a linked list, make sure to"},
-}
-
-var extractFixTagsInput = `
-commit 73aba437a774237b1130837b856f3b40b3ec3bf0 (HEAD -> master, origin/master)
-Author: me <foo@bar.com>
-Date: Fri Dec 22 19:59:56 2017 +0100
-
- dashboard/app: bump max repros per bug to 10
-
- Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
-
-commit 26cd53f078db858a6ccca338e13e7f4d1d291c22
-Author: me <foo@bar.com>
-Date: Fri Dec 22 13:42:27 2017 +0100
-
- executor: remove dead code
-
- Reported-by: syzbot+8e4090902540da8c6e8f@my.mail.com
- Reported-by: syzbot <syzbot+a640a0fc325c29c3efcb@my.mail.com>
-
-commit 7b62abdb0abadbaf7b3f3a23ab4d78485fbf9059
-Author: Dmitry Vyukov <dvyukov@google.com>
-Date: Fri Dec 22 11:59:09 2017 +0100
-
- pkg/csource: fix string escaping bug
-
- Reported-and-tested-by: syzbot+8e4090902540da8c6e8fa640a0fc325c29c3efcb@my.mail.com
- Tested-by: syzbot+4234987263748623784623758235@my.mail.com
-
-commit 47546510aa98d3fbff3291a5dc3cefe712e70394
-Author: anton <openbsd@openbsd.org>
-Date: Sat Oct 6 21:12:23 2018 +0000
-
- When freeing a lockf struct that already is part of a linked list, make sure to
- update the next pointer for the preceding lock. Prevents a double free panic.
-
- ok millert@
- Reported-by: syzbot+6dd701dc797b23b8c761@my.mail.com
-`
diff --git a/pkg/vcs/netbsd.go b/pkg/vcs/netbsd.go
index c210e4878..b43523756 100644
--- a/pkg/vcs/netbsd.go
+++ b/pkg/vcs/netbsd.go
@@ -9,7 +9,7 @@ import (
)
type netbsd struct {
- git *git
+ *git
}
func newNetBSD(vm, dir string) *netbsd {
@@ -18,31 +18,7 @@ func newNetBSD(vm, dir string) *netbsd {
}
}
-func (ctx *netbsd) Poll(repo, branch string) (*Commit, error) {
- return ctx.git.Poll(repo, branch)
-}
-
-func (ctx *netbsd) CheckoutBranch(repo, branch string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for netbsd")
-}
-
-func (ctx *netbsd) CheckoutCommit(repo, commit string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for netbsd")
-}
-
-func (ctx *netbsd) SwitchCommit(commit string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for netbsd")
-}
-
-func (ctx *netbsd) HeadCommit() (*Commit, error) {
- return nil, fmt.Errorf("not implemented for netbsd")
-}
-
-func (ctx *netbsd) ListRecentCommits(baseCommit string) ([]string, error) {
- return ctx.git.ListRecentCommits(baseCommit)
-}
-
-func (ctx *netbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) {
+func (ctx *netbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
return ctx.git.ExtractFixTagsFromCommits(baseCommit, email)
}
diff --git a/pkg/vcs/openbsd.go b/pkg/vcs/openbsd.go
index fdcc648f7..3998ce58a 100644
--- a/pkg/vcs/openbsd.go
+++ b/pkg/vcs/openbsd.go
@@ -9,7 +9,7 @@ import (
)
type openbsd struct {
- git *git
+ *git
}
func newOpenBSD(vm, dir string) *openbsd {
@@ -18,31 +18,7 @@ func newOpenBSD(vm, dir string) *openbsd {
}
}
-func (ctx *openbsd) Poll(repo, branch string) (*Commit, error) {
- return ctx.git.Poll(repo, branch)
-}
-
-func (ctx *openbsd) CheckoutBranch(repo, branch string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for openbsd")
-}
-
-func (ctx *openbsd) CheckoutCommit(repo, commit string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for openbsd")
-}
-
-func (ctx *openbsd) SwitchCommit(commit string) (*Commit, error) {
- return nil, fmt.Errorf("not implemented for openbsd")
-}
-
-func (ctx *openbsd) HeadCommit() (*Commit, error) {
- return nil, fmt.Errorf("not implemented for openbsd")
-}
-
-func (ctx *openbsd) ListRecentCommits(baseCommit string) ([]string, error) {
- return ctx.git.ListRecentCommits(baseCommit)
-}
-
-func (ctx *openbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error) {
+func (ctx *openbsd) ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error) {
return ctx.git.ExtractFixTagsFromCommits(baseCommit, email)
}
diff --git a/pkg/vcs/vcs.go b/pkg/vcs/vcs.go
index b315a83ba..def15153c 100644
--- a/pkg/vcs/vcs.go
+++ b/pkg/vcs/vcs.go
@@ -33,13 +33,22 @@ type Repo interface {
// HeadCommit returns info about the HEAD commit of the current branch of git repository.
HeadCommit() (*Commit, error)
+ // GetCommitByTitle finds commit info by the title.
+ // Remote is not fetched, the commit needs to be reachable from the current repo state
+ // (e.g. do CheckoutBranch before). If the commit is not found, nil is returned.
+ GetCommitByTitle(title string) (*Commit, error)
+
+ // GetCommitsByTitles is a batch version of GetCommitByTitle.
+ // Returns list of commits and titles of commits that are not found.
+ GetCommitsByTitles(titles []string) ([]*Commit, []string, error)
+
// ListRecentCommits returns list of recent commit titles starting from baseCommit.
ListRecentCommits(baseCommit string) ([]string, error)
// ExtractFixTagsFromCommits extracts fixing tags for bugs from git log.
// Given email = "user@domain.com", it searches for tags of the form "user+tag@domain.com"
- // and return pairs {tag, commit title}.
- ExtractFixTagsFromCommits(baseCommit, email string) ([]FixCommit, error)
+ // and returns commits with these tags.
+ ExtractFixTagsFromCommits(baseCommit, email string) ([]*Commit, error)
// PreviousReleaseTags returns list of preceding release tags that are reachable from the given commit.
PreviousReleaseTags(commit string) ([]string, error)
@@ -57,14 +66,10 @@ type Commit struct {
Title string
Author string
CC []string
+ Tags []string
Date time.Time
}
-type FixCommit struct {
- Tag string
- Title string
-}
-
type BisectResult int
const (
@@ -153,7 +158,7 @@ func runSandboxed(dir, command string, args ...string) ([]byte, error) {
var (
// nolint: lll
- gitRepoRe = regexp.MustCompile(`^(git|ssh|http|https|ftp|ftps)://[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)+(:[0-9]+)?/[a-zA-Z0-9-_./]+\.git(/)?$`)
+ gitRepoRe = regexp.MustCompile(`^(git|ssh|http|https|ftp|ftps)://[a-zA-Z0-9-_]+(\.[a-zA-Z0-9-_]+)+(:[0-9]+)?(/[a-zA-Z0-9-_./]+)?(/)?$`)
gitBranchRe = regexp.MustCompile("^[a-zA-Z0-9-_/.]{2,200}$")
gitHashRe = regexp.MustCompile("^[a-f0-9]{8,40}$")
releaseTagRe = regexp.MustCompile(`^v([0-9]+).([0-9]+)(?:\.([0-9]+))?$`)
@@ -189,3 +194,63 @@ var commitPrefixes = []string{
"FROMGIT:",
"net-backports:",
}
+
+const SyzkallerRepo = "https://github.com/google/syzkaller"
+
+func CommitLink(url, hash string) string {
+ return link(url, hash, 0)
+}
+
+func TreeLink(url, hash string) string {
+ return link(url, hash, 1)
+}
+
+func LogLink(url, hash string) string {
+ return link(url, hash, 2)
+}
+
+func link(url, hash string, typ int) string {
+ if url == "" || hash == "" {
+ return ""
+ }
+ switch url {
+ case "https://fuchsia.googlesource.com":
+ // We collect hashes from zircon repo.
+ return link(url+"/zircon", hash, typ)
+ }
+ if strings.HasPrefix(url, "https://github.com/") {
+ url = strings.TrimSuffix(url, ".git")
+ switch typ {
+ case 1:
+ return url + "/tree/" + hash
+ case 2:
+ return url + "/commits/" + hash
+ default:
+ return url + "/commit/" + hash
+ }
+ }
+ if strings.HasPrefix(url, "https://git.kernel.org/pub/scm/") ||
+ strings.HasPrefix(url, "git://git.kernel.org/pub/scm/") {
+ url = strings.TrimPrefix(url, "git")
+ url = strings.TrimPrefix(url, "https")
+ switch typ {
+ case 1:
+ return "https" + url + "/tree/?id=" + hash
+ case 2:
+ return "https" + url + "/log/?id=" + hash
+ default:
+ return "https" + url + "/commit/?id=" + hash
+ }
+ }
+ if strings.HasPrefix(url, "https://") && strings.Contains(url, ".googlesource.com") {
+ switch typ {
+ case 1:
+ return url + "/+/" + hash + "/"
+ case 2:
+ return url + "/+log/" + hash
+ default:
+ return url + "/+/" + hash + "^!"
+ }
+ }
+ return ""
+}
diff --git a/pkg/vcs/vcs_test.go b/pkg/vcs/vcs_test.go
index 9e90090a4..457430989 100644
--- a/pkg/vcs/vcs_test.go
+++ b/pkg/vcs/vcs_test.go
@@ -31,12 +31,13 @@ func TestCheckRepoAddress(t *testing.T) {
"https://anonscm.debian.org/git/kernel/linux.git": true,
"git://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": true,
"http://host.xz:123/path/to/repo.git/": true,
+ "https://chromium.googlesource.com/chromiumos/third_party/kernel": true,
+ "https://fuchsia.googlesource.com": true,
"": false,
"foobar": false,
"linux-next": false,
"foo://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": false,
"git://kernel/ubuntu.git": false,
- "git://kernel.com/ubuntu": false,
"gitgit://kernel.ubuntu.com/ubuntu/ubuntu-zesty.git": false,
})
}
@@ -83,3 +84,70 @@ func testPredicate(t *testing.T, fn func(string) bool, tests map[string]bool) {
}
}
}
+
+func TestCommitLink(t *testing.T) {
+ type Test struct {
+ URL string
+ Hash string
+ CommitLink string
+ }
+ tests := []Test{
+ {
+ "https://github.com/google/syzkaller",
+ "76dd003f1b102b791d8b342a1f92a6486ff56a1e",
+ "https://github.com/google/syzkaller/commit/76dd003f1b102b791d8b342a1f92a6486ff56a1e",
+ },
+ {
+ "https://github.com/google/syzkaller.git",
+ "76dd003f1b",
+ "https://github.com/google/syzkaller/commit/76dd003f1b",
+ },
+ {
+ "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git",
+ "8fe28cb58bcb",
+ "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8fe28cb58bcb",
+ },
+ {
+ "git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git",
+ "8fe28cb58b",
+ "https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8fe28cb58b",
+ },
+
+ {
+ "https://android.googlesource.com/kernel/common",
+ "d0c3914ffbe4c00f0a131bae83f811d5606699bc",
+ "https://android.googlesource.com/kernel/common/+/d0c3914ffbe4c00f0a131bae83f811d5606699bc^!",
+ },
+ {
+ "https://gvisor.googlesource.com/gvisor",
+ "5301cbf8430e5436211bc142c0886d8c11cc71ab",
+ "https://gvisor.googlesource.com/gvisor/+/5301cbf8430e5436211bc142c0886d8c11cc71ab^!",
+ },
+ {
+ "https://fuchsia.googlesource.com",
+ "13ee3dc5e4c46bf127977ad28645c47442ec517d",
+ "https://fuchsia.googlesource.com/zircon/+/13ee3dc5e4c46bf127977ad28645c47442ec517d^!",
+ },
+ {
+ "git://git.cmpxchg.org/linux-mmots.git",
+ "8fe28cb58b",
+ "",
+ },
+ {
+ "",
+ "8fe28cb58b",
+ "",
+ },
+ {
+ "https://android.googlesource.com/kernel/common",
+ "",
+ "",
+ },
+ }
+ for _, test := range tests {
+ link := CommitLink(test.URL, test.Hash)
+ if link != test.CommitLink {
+ t.Errorf("URL: %v\nhash: %v\nwant: %v\ngot: %v", test.URL, test.Hash, test.CommitLink, link)
+ }
+ }
+}