| // 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" <foo@bar.com>`, `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" <foo@bar.com>`, `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", |
| }, |
| } |
| c.client2.ReportCrash(crash) |
| |
| // Test that the reported 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: |
| mounted in repro #1: http://google.com/disk_image |
| mounted in repro #2: http://google.com/disk_image2 |
| |
| 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 2. |
| needed, err := c.client2.NeededAssetsList() |
| c.expectOK(err) |
| sort.Strings(needed.DownloadURLs) |
| allDownloadURLs := []string{ |
| "http://google.com/disk_image", |
| "http://google.com/disk_image2", |
| } |
| 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{}) |
| } |