// Copyright 2022 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. package main import ( "fmt" "sort" "testing" "time" "github.com/google/syzkaller/dashboard/dashapi" "github.com/google/syzkaller/pkg/email" ) func TestBuildAssetLifetime(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) build.Manager = "test_manager" // Embed one of the assets right away. build.Assets = []dashapi.NewAsset{ { Type: dashapi.KernelObject, DownloadURL: "http://google.com/vmlinux", }, } c.client2.UploadBuild(build) // Add one more build, so that the assets of the previous one could be deprecated. c.advanceTime(time.Minute) build2 := testBuild(2) build2.Manager = "test_manager" c.client2.UploadBuild(build2) // "Upload" several more assets. c.expectOK(c.client2.AddBuildAssets(&dashapi.AddBuildAssetsReq{ BuildID: build.ID, Assets: []dashapi.NewAsset{ { Type: dashapi.BootableDisk, DownloadURL: "http://google.com/bootable_disk", }, }, })) c.expectOK(c.client2.AddBuildAssets(&dashapi.AddBuildAssetsReq{ BuildID: build.ID, Assets: []dashapi.NewAsset{ { Type: dashapi.HTMLCoverageReport, DownloadURL: "http://google.com/coverage.html", }, }, })) crash := testCrash(build, 1) crash.Maintainers = []string{`"Foo Bar" `, `bar@foo.com`, `idont@want.EMAILS`} c.client2.ReportCrash(crash) // Test that the reporting email is correct. msg := c.pollEmailBug() sender, extBugID, err := email.RemoveAddrContext(msg.Sender) c.expectOK(err) _, dbCrash, dbBuild := c.loadBug(extBugID) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) to := c.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), 0) c.expectEQ(msg.Body, fmt.Sprintf(`Hello, syzbot found the following issue on: HEAD commit: 111111111111 kernel_commit_title1 git tree: repo1 branch1 console output: %[2]v kernel config: %[3]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler1 CC: [bar@foo.com foo@bar.com idont@want.EMAILS] Unfortunately, I don't have any reproducer for this issue yet. Downloadable assets: disk image: http://google.com/bootable_disk vmlinux: http://google.com/vmlinux IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 --- This report is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller@googlegroups.com. syzbot will keep track of this issue. See: https://goo.gl/tpsmEJ#status for how to communicate with syzbot. If the report is already addressed, let syzbot know by replying with: #syz fix: exact-commit-title If you want to overwrite report's subsystems, reply with: #syz set subsystems: new-subsystem (See the list of subsystem names on the web dashboard) If the report is a duplicate of another one, reply with: #syz dup: exact-subject-of-another-report If you want to undo deduplication, reply with: #syz undup`, extBugID, crashLogLink, kernelConfigLink)) c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build.KernelConfig) // We query the needed assets. We need all 3. needed, err := c.client2.NeededAssetsList() c.expectOK(err) sort.Strings(needed.DownloadURLs) allDownloadURLs := []string{ "http://google.com/bootable_disk", "http://google.com/coverage.html", "http://google.com/vmlinux", } c.expectEQ(needed.DownloadURLs, allDownloadURLs) // Invalidate the bug. c.client.updateBug(extBugID, dashapi.BugStatusInvalid, "") _, err = c.GET("/cron/deprecate_assets") c.expectOK(err) // Query the needed assets once more, so far there should be no change. needed, err = c.client2.NeededAssetsList() c.expectOK(err) sort.Strings(needed.DownloadURLs) c.expectEQ(needed.DownloadURLs, allDownloadURLs) // Skip one month and deprecate assets. c.advanceTime(time.Hour * 24 * 31) _, err = c.GET("/cron/deprecate_assets") c.expectOK(err) // Only the html asset should have persisted. needed, err = c.client2.NeededAssetsList() c.expectOK(err) c.expectEQ(needed.DownloadURLs, []string{"http://google.com/coverage.html"}) } func TestCoverReportDisplay(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client.UploadBuild(build) // Upload the second build to just make sure coverage reports are assigned per-manager. c.client.UploadBuild(testBuild(2)) // We expect no coverage reports to be present. uiManagers, err := loadManagers(c.ctx, AccessAdmin, "test1", nil) c.expectOK(err) c.expectEQ(len(uiManagers), 2) c.expectEQ(uiManagers[0].CoverLink, "") c.expectEQ(uiManagers[1].CoverLink, "") // Upload an asset. origHTMLAsset := "http://google.com/coverage0.html" c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ BuildID: build.ID, Assets: []dashapi.NewAsset{ { Type: dashapi.HTMLCoverageReport, DownloadURL: origHTMLAsset, }, }, })) uiManagers, err = loadManagers(c.ctx, AccessAdmin, "test1", nil) c.expectOK(err) c.expectEQ(len(uiManagers), 2) c.expectEQ(uiManagers[0].CoverLink, origHTMLAsset) c.expectEQ(uiManagers[1].CoverLink, "") // Upload a newer coverage. newHTMLAsset := "http://google.com/coverage1.html" c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ BuildID: build.ID, Assets: []dashapi.NewAsset{ { Type: dashapi.HTMLCoverageReport, DownloadURL: newHTMLAsset, }, }, })) uiManagers, err = loadManagers(c.ctx, AccessAdmin, "test1", nil) c.expectOK(err) c.expectEQ(len(uiManagers), 2) c.expectEQ(uiManagers[0].CoverLink, newHTMLAsset) c.expectEQ(uiManagers[1].CoverLink, "") } func TestCoverReportDeprecation(t *testing.T) { c := NewCtx(t) defer c.Close() ensureNeeded := func(needed []string) { _, err := c.GET("/cron/deprecate_assets") c.expectOK(err) neededResp, err := c.client.NeededAssetsList() c.expectOK(err) sort.Strings(neededResp.DownloadURLs) sort.Strings(needed) c.expectEQ(neededResp.DownloadURLs, needed) } build := testBuild(1) c.client.UploadBuild(build) uploadReport := func(url string) { c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ BuildID: build.ID, Assets: []dashapi.NewAsset{ { Type: dashapi.HTMLCoverageReport, DownloadURL: url, }, }, })) } // Week 1. Saturday Jan 1st, 2000. weekOneFirst := "http://google.com/coverage1_1.html" uploadReport(weekOneFirst) // Week 1. Sunday Jan 2nd, 2000. weekOneSecond := "http://google.com/coverage1_2.html" c.advanceTime(time.Hour * 24) uploadReport(weekOneSecond) ensureNeeded([]string{weekOneFirst, weekOneSecond}) // Week 2. Tuesday Jan 4nd, 2000. weekTwoFirst := "http://google.com/coverage2_1.html" c.advanceTime(time.Hour * 24 * 2) uploadReport(weekTwoFirst) ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst}) // Week 2. Thu Jan 6nd, 2000. weekTwoSecond := "http://google.com/coverage2_2.html" c.advanceTime(time.Hour * 24 * 2) uploadReport(weekTwoSecond) ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst, weekTwoSecond}) // Week 3. Monday Jan 10th, 2000. weekThreeFirst := "http://google.com/coverage3_1.html" c.advanceTime(time.Hour * 24 * 4) uploadReport(weekThreeFirst) ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst, weekTwoSecond, weekThreeFirst}) // Week 4. Monday Jan 17th, 2000. weekFourFirst := "http://google.com/coverage4_1.html" c.advanceTime(time.Hour * 24 * 7) uploadReport(weekFourFirst) t.Logf("embargo is over, time is %s", timeNow(c.ctx)) // Note that now that the two week deletion embargo has passed, the first asset // begins to falls out. ensureNeeded([]string{weekOneSecond, weekTwoFirst, weekTwoSecond, weekThreeFirst, weekFourFirst}) // Week 5. Monday Jan 24th, 2000. c.advanceTime(time.Hour * 24 * 7) ensureNeeded([]string{weekOneSecond, weekTwoSecond, weekThreeFirst, weekFourFirst}) // A year later. c.advanceTime(time.Hour * 24 * 365) ensureNeeded([]string{weekOneSecond, weekTwoSecond, weekThreeFirst, weekFourFirst}) } func TestFreshBuildAssets(t *testing.T) { c := NewCtx(t) defer c.Close() ensureNeeded := func(needed []string) { _, err := c.GET("/cron/deprecate_assets") c.expectOK(err) neededResp, err := c.client.NeededAssetsList() c.expectOK(err) sort.Strings(neededResp.DownloadURLs) sort.Strings(needed) c.expectEQ(neededResp.DownloadURLs, needed) } build := testBuild(1) build.Manager = "manager" build.Assets = []dashapi.NewAsset{ { Type: dashapi.KernelObject, DownloadURL: "http://google.com/vmlinux", }, } c.client.UploadBuild(build) // No crashes yet, but it's the latest build, so the assets must be preserved. ensureNeeded([]string{"http://google.com/vmlinux"}) // Upload one more build for the same manager. c.advanceTime(time.Minute) build2 := testBuild(2) build2.Manager = "manager" build2.Assets = []dashapi.NewAsset{ { Type: dashapi.KernelObject, DownloadURL: "http://google.com/vmlinux2", }, } c.client.UploadBuild(build2) // The assets of the previous build are reasonably new, so they must be kept. ensureNeeded([]string{"http://google.com/vmlinux", "http://google.com/vmlinux2"}) // The assets of the first build must be deprecated now. c.advanceTime(time.Hour * 24 * 14) ensureNeeded([]string{"http://google.com/vmlinux2"}) // But even if a lot of time passes, but there are no new builds, the assets must stay. c.advanceTime(time.Hour * 24 * 365) ensureNeeded([]string{"http://google.com/vmlinux2"}) } func TestCrashAssetLifetime(t *testing.T) { c := NewCtx(t) defer c.Close() build := testBuild(1) c.client2.UploadBuild(build) crash := testCrash(build, 1) crash.Maintainers = []string{`"Foo Bar" `, `bar@foo.com`, `idont@want.EMAILS`} crash.Assets = []dashapi.NewAsset{ { Type: dashapi.MountInRepro, DownloadURL: "http://google.com/disk_image", }, { Type: dashapi.MountInRepro, DownloadURL: "http://google.com/disk_image2", FsckLog: []byte("good log"), FsIsClean: true, }, { Type: dashapi.MountInRepro, DownloadURL: "http://google.com/disk_image3", FsckLog: []byte("bad log"), FsIsClean: false, }, } c.client2.ReportCrash(crash) // Test that the reported email is correct. msg := c.pollEmailBug() sender, extBugID, err := email.RemoveAddrContext(msg.Sender) c.expectOK(err) bug, dbCrash, dbBuild := c.loadBug(extBugID) crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) c.expectEQ(sender, fromAddr(c.ctx)) to := c.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), 0) bugReport, err := c.client2.LoadBug(bug.key(c.ctx).StringID()) c.expectOK(err) c.expectEQ(msg.Body, fmt.Sprintf(`Hello, syzbot found the following issue on: HEAD commit: 111111111111 kernel_commit_title1 git tree: repo1 branch1 console output: %[2]v kernel config: %[3]v dashboard link: https://testapp.appspot.com/bug?extid=%[1]v compiler: compiler1 CC: [bar@foo.com foo@bar.com idont@want.EMAILS] Unfortunately, I don't have any reproducer for this issue yet. Downloadable assets: mounted in repro #1: http://google.com/disk_image mounted in repro #2: http://google.com/disk_image2 fsck result: OK (log: %[4]v) mounted in repro #3: http://google.com/disk_image3 fsck result: failed (log: %[5]v) IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syzbot+%[1]v@testapp.appspotmail.com report1 --- This report is generated by a bot. It may contain errors. See https://goo.gl/tpsmEJ for more information about syzbot. syzbot engineers can be reached at syzkaller@googlegroups.com. syzbot will keep track of this issue. See: https://goo.gl/tpsmEJ#status for how to communicate with syzbot. If the report is already addressed, let syzbot know by replying with: #syz fix: exact-commit-title If you want to overwrite report's subsystems, reply with: #syz set subsystems: new-subsystem (See the list of subsystem names on the web dashboard) If the report is a duplicate of another one, reply with: #syz dup: exact-subject-of-another-report If you want to undo deduplication, reply with: #syz undup`, extBugID, crashLogLink, kernelConfigLink, bugReport.Assets[1].FsckLogURL, bugReport.Assets[2].FsckLogURL, )) c.checkURLContents(crashLogLink, crash.Log) c.checkURLContents(kernelConfigLink, build.KernelConfig) // We query the needed assets. We need all 3. needed, err := c.client2.NeededAssetsList() c.expectOK(err) sort.Strings(needed.DownloadURLs) allDownloadURLs := []string{ "http://google.com/disk_image", "http://google.com/disk_image2", "http://google.com/disk_image3", } c.expectEQ(needed.DownloadURLs, allDownloadURLs) // Invalidate the bug. c.client.updateBug(extBugID, dashapi.BugStatusInvalid, "") _, err = c.GET("/cron/deprecate_assets") c.expectOK(err) // Query the needed assets once more, so far there should be no change. needed, err = c.client2.NeededAssetsList() c.expectOK(err) sort.Strings(needed.DownloadURLs) c.expectEQ(needed.DownloadURLs, allDownloadURLs) // Skip one month and deprecate assets. c.advanceTime(time.Hour * 24 * 31) _, err = c.GET("/cron/deprecate_assets") c.expectOK(err) // Nothing should have been persisted. needed, err = c.client2.NeededAssetsList() c.expectOK(err) c.expectEQ(needed.DownloadURLs, []string{}) }