blob: 0635fd897c7b70f9dbcb8720dc7fd20bd2f8b21e [file] [log] [blame]
// Copyright 2017 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.
// +build aetest
package dash
import (
"fmt"
"strings"
"testing"
"github.com/google/syzkaller/dashboard/dashapi"
"github.com/google/syzkaller/pkg/email"
)
func TestEmailReport(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client2, key2, "upload_build", build, nil))
crash := testCrash(build, 1)
crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`}
c.expectOK(c.API(client2, key2, "report_crash", crash, nil))
// Report the crash over email and check all fields.
sender0 := ""
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
sender0 = msg.Sender
sender, _, err := email.RemoveAddrContext(msg.Sender)
if err != nil {
t.Fatalf("failed to remove sender context: %v", err)
}
c.expectEQ(sender, fromAddr(c.ctx))
to := config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email
c.expectEQ(msg.To, []string{to})
c.expectEQ(msg.Subject, crash.Title)
c.expectEQ(len(msg.Attachments), 2)
c.expectEQ(msg.Attachments[0].Name, "config.txt")
c.expectEQ(msg.Attachments[0].Data, build.KernelConfig)
c.expectEQ(msg.Attachments[1].Name, "raw.log")
c.expectEQ(msg.Attachments[1].Data, crash.Log)
body := `Hello,
syzkaller hit the following crash on kernel_commit1
repo1/branch1
compiler: compiler1
.config is attached
Raw console output is attached.
Unfortunately, I don't have any reproducer for this bug yet.
CC: [bar@foo.com foo@bar.com]
report1
---
This bug is generated by a dumb bot. It may contain errors.
See https://goo.gl/tpsmEJ for details.
Direct all questions to syzkaller@googlegroups.com.
Please credit me with: Reported-by: syzbot <syzkaller@googlegroups.com>
syzbot will keep track of this bug report.
Once a fix for this bug is merged into any tree, reply to this email with:
#syz fix: exact-commit-title
To mark this as a duplicate of another syzbot report, please reply with:
#syz dup: exact-subject-of-another-report
If it's a one-off invalid bug report, please reply with:
#syz invalid
Note: if the crash happens again, it will cause creation of a new bug report.
Note: all commands must start from beginning of the line in the email body.
To upstream this report, please reply with:
#syz upstream`
if msg.Body != body {
t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body)
}
}
// Emulate receive of the report from a mailing list.
// This should update the bug with the link/Message-ID.
incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <1234>
Subject: crash1
From: %v
To: foo@bar.com
Content-Type: text/plain
Hello
syzbot will keep track of this bug report.
Once a fix for this bug is merged into any tree, reply to this email with:
#syz fix: exact-commit-title
To mark this as a duplicate of another syzbot report, please reply with:
#syz dup: exact-subject-of-another-report
If it's a one-off invalid bug report, please reply with:
#syz invalid
--
You received this message because you are subscribed to the Google Groups "syzkaller" group.
To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller+unsubscribe@googlegroups.com.
To post to this group, send email to syzkaller@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/syzkaller/1234@google.com.
For more options, visit https://groups.google.com/d/optout.
`, sender0)
c.expectOK(c.POST("/_ah/mail/", incoming1))
// Now report syz reproducer and check updated email.
crash.ReproOpts = []byte("repro opts")
crash.ReproSyz = []byte("getpid()")
syzRepro := []byte(fmt.Sprintf("#%s\n%s", crash.ReproOpts, crash.ReproSyz))
c.expectOK(c.API(client2, key2, "report_crash", crash, nil))
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
c.expectEQ(msg.Sender, sender0)
sender, _, err := email.RemoveAddrContext(msg.Sender)
if err != nil {
t.Fatalf("failed to remove sender context: %v", err)
}
c.expectEQ(sender, fromAddr(c.ctx))
var to []string
to = append(to, "foo@bar.com")
to = append(to, config.Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email)
c.expectEQ(msg.To, to)
c.expectEQ(msg.Subject, crash.Title)
c.expectEQ(len(msg.Attachments), 3)
c.expectEQ(msg.Attachments[0].Name, "config.txt")
c.expectEQ(msg.Attachments[0].Data, build.KernelConfig)
c.expectEQ(msg.Attachments[1].Name, "raw.log")
c.expectEQ(msg.Attachments[1].Data, crash.Log)
c.expectEQ(msg.Attachments[2].Name, "repro.txt")
c.expectEQ(msg.Attachments[2].Data, syzRepro)
c.expectEQ(msg.Headers["In-Reply-To"], []string{"<1234>"})
body := `syzkaller has found reproducer for the following crash on kernel_commit1
repo1/branch1
compiler: compiler1
.config is attached
Raw console output is attached.
syzkaller reproducer is attached. See https://goo.gl/kgGztJ
for information about syzkaller reproducers
CC: [bar@foo.com foo@bar.com]
report1
`
c.expectEQ(msg.Body, body)
}
// Now upstream the bug and check that it reaches the next reporting.
c.incomingEmail(sender0, "#syz upstream")
sender1 := ""
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
sender1 = msg.Sender
if sender1 == sender0 {
t.Fatalf("same ID in different reporting")
}
sender, _, err := email.RemoveAddrContext(msg.Sender)
if err != nil {
t.Fatalf("failed to remove sender context: %v", err)
}
c.expectEQ(sender, fromAddr(c.ctx))
c.expectEQ(msg.To, []string{"bar@foo.com", "bugs@syzkaller.com", "foo@bar.com"})
c.expectEQ(msg.Subject, crash.Title)
c.expectEQ(len(msg.Attachments), 3)
c.expectEQ(msg.Attachments[0].Name, "config.txt")
c.expectEQ(msg.Attachments[0].Data, build.KernelConfig)
c.expectEQ(msg.Attachments[1].Name, "raw.log")
c.expectEQ(msg.Attachments[1].Data, crash.Log)
c.expectEQ(msg.Attachments[2].Name, "repro.txt")
c.expectEQ(msg.Attachments[2].Data, syzRepro)
body := `Hello,
syzkaller hit the following crash on kernel_commit1
repo1/branch1
compiler: compiler1
.config is attached
Raw console output is attached.
syzkaller reproducer is attached. See https://goo.gl/kgGztJ
for information about syzkaller reproducers
report1
---
This bug is generated by a dumb bot. It may contain errors.
See https://goo.gl/tpsmEJ for details.
Direct all questions to syzkaller@googlegroups.com.
Please credit me with: Reported-by: syzbot <syzkaller@googlegroups.com>
syzbot will keep track of this bug report.
Once a fix for this bug is merged into any tree, reply to this email with:
#syz fix: exact-commit-title
If you want to test a patch for this bug, please reply with:
#syz test: git://repo/address.git branch
and provide the patch inline or as an attachment.
To mark this as a duplicate of another syzbot report, please reply with:
#syz dup: exact-subject-of-another-report
If it's a one-off invalid bug report, please reply with:
#syz invalid
Note: if the crash happens again, it will cause creation of a new bug report.
Note: all commands must start from beginning of the line in the email body.
`
if msg.Body != body {
t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body)
}
}
// Model that somebody adds more emails to CC list.
incoming3 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <1234>
Subject: crash1
From: foo@bar.com
To: %v
CC: new@new.com, "another" <another@another.com>, bar@foo.com, bugs@syzkaller.com, foo@bar.com
Content-Type: text/plain
+more people
`, sender1)
c.expectOK(c.POST("/_ah/mail/", incoming3))
// Now upload a C reproducer.
crash.ReproC = []byte("int main() {}")
crash.Maintainers = []string{"\"qux\" <qux@qux.com>"}
c.expectOK(c.API(client2, key2, "report_crash", crash, nil))
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
c.expectEQ(msg.Sender, sender1)
sender, _, err := email.RemoveAddrContext(msg.Sender)
if err != nil {
t.Fatalf("failed to remove sender context: %v", err)
}
c.expectEQ(sender, fromAddr(c.ctx))
c.expectEQ(msg.To, []string{"another@another.com", "bar@foo.com", "bugs@syzkaller.com", "foo@bar.com", "new@new.com", "qux@qux.com"})
c.expectEQ(msg.Subject, crash.Title)
c.expectEQ(len(msg.Attachments), 4)
c.expectEQ(msg.Attachments[0].Name, "config.txt")
c.expectEQ(msg.Attachments[0].Data, build.KernelConfig)
c.expectEQ(msg.Attachments[1].Name, "raw.log")
c.expectEQ(msg.Attachments[1].Data, crash.Log)
c.expectEQ(msg.Attachments[2].Name, "repro.txt")
c.expectEQ(msg.Attachments[2].Data, syzRepro)
c.expectEQ(msg.Attachments[3].Name, "repro.c")
c.expectEQ(msg.Attachments[3].Data, crash.ReproC)
body := `syzkaller has found reproducer for the following crash on kernel_commit1
repo1/branch1
compiler: compiler1
.config is attached
Raw console output is attached.
C reproducer is attached
syzkaller reproducer is attached. See https://goo.gl/kgGztJ
for information about syzkaller reproducers
report1
`
if msg.Body != body {
t.Fatalf("got email body:\n%s\n\nwant:\n%s", msg.Body, body)
}
}
// Send an invalid command.
incoming4 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <abcdef>
Subject: title1
From: foo@bar.com
To: %v
Content-Type: text/plain
#syz bad-command
`, sender1)
c.expectOK(c.POST("/_ah/mail/", incoming4))
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
c.expectEQ(msg.To, []string{"<foo@bar.com>"})
c.expectEQ(msg.Subject, crash.Title)
c.expectEQ(msg.Headers["In-Reply-To"], []string{"<abcdef>"})
if !strings.Contains(msg.Body, `> #syz bad-command
unknown command "bad-command"
`) {
t.Fatal("no unknown command reply for bad command")
}
}
// Now mark the bug as fixed.
c.incomingEmail(sender1, "#syz fix: some: commit title")
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 0)
// Check that the commit is now passed to builders.
builderPollReq := &dashapi.BuilderPollReq{build.Manager}
builderPollResp := new(dashapi.BuilderPollResp)
c.expectOK(c.API(client2, key2, "builder_poll", builderPollReq, builderPollResp))
c.expectEQ(len(builderPollResp.PendingCommits), 1)
c.expectEQ(builderPollResp.PendingCommits[0], "some: commit title")
build2 := testBuild(2)
build2.Manager = build.Manager
build2.Commits = []string{"some: commit title"}
c.expectOK(c.API(client2, key2, "upload_build", build2, nil))
// New crash must produce new bug in the first reporting.
c.expectOK(c.API(client2, key2, "report_crash", crash, nil))
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
c.expectEQ(msg.Subject, crash.Title+" (2)")
if msg.Sender == sender0 {
t.Fatalf("same reporting ID for new bug")
}
}
}
// Bug must not be mailed to maintainers if maintainers list is empty.
func TestEmailNoMaintainers(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client2, key2, "upload_build", build, nil))
crash := testCrash(build, 1)
c.expectOK(c.API(client2, key2, "report_crash", crash, nil))
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
sender := (<-c.emailSink).Sender
incoming1 := fmt.Sprintf(`Sender: syzkaller@googlegroups.com
Date: Tue, 15 Aug 2017 14:59:00 -0700
Message-ID: <1234>
Subject: crash1
From: %v
To: foo@bar.com
Content-Type: text/plain
#syz upstream
`, sender)
c.expectOK(c.POST("/_ah/mail/", incoming1))
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 0)
}
// Basic dup scenario: mark one bug as dup of another.
func TestEmailDup(t *testing.T) {
c := NewCtx(t)
defer c.Close()
build := testBuild(1)
c.expectOK(c.API(client2, key2, "upload_build", build, nil))
crash1 := testCrash(build, 1)
crash1.Title = "BUG: slightly more elaborate title"
c.expectOK(c.API(client2, key2, "report_crash", crash1, nil))
crash2 := testCrash(build, 2)
crash1.Title = "KASAN: another title"
c.expectOK(c.API(client2, key2, "report_crash", crash2, nil))
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 2)
msg1 := <-c.emailSink
msg2 := <-c.emailSink
// Dup crash2 to crash1.
c.incomingEmail(msg2.Sender, "#syz dup: BUG: slightly more elaborate title")
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 0)
// Second crash happens again
crash2.ReproC = []byte("int main() {}")
c.expectOK(c.API(client2, key2, "report_crash", crash2, nil))
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 0)
// Now close the original bug, and check that new bugs for dup are now created.
c.incomingEmail(msg1.Sender, "#syz invalid")
// New crash must produce new bug in the first reporting.
c.expectOK(c.API(client2, key2, "report_crash", crash2, nil))
{
c.expectOK(c.GET("/email_poll"))
c.expectEQ(len(c.emailSink), 1)
msg := <-c.emailSink
c.expectEQ(msg.Subject, crash2.Title+" (2)")
}
}