github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/dashboard/app/bisect_test.go (about)

     1  // Copyright 2019 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package main
     5  
     6  import (
     7  	"bytes"
     8  	"errors"
     9  	"fmt"
    10  	"net/http"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/google/syzkaller/dashboard/dashapi"
    16  	"github.com/google/syzkaller/pkg/email"
    17  	db "google.golang.org/appengine/v2/datastore"
    18  )
    19  
    20  // nolint: funlen
    21  func TestBisectCause(t *testing.T) {
    22  	c := NewCtx(t)
    23  	defer c.Close()
    24  
    25  	build := testBuild(1)
    26  	c.client2.UploadBuild(build)
    27  	crash := testCrash(build, 1)
    28  	c.client2.ReportCrash(crash)
    29  	c.client2.pollEmailBug()
    30  
    31  	// No repro - no bisection.
    32  	pollResp := c.client2.pollJobs(build.Manager)
    33  	c.expectEQ(pollResp.ID, "")
    34  
    35  	// Now upload 4 crashes with repros.
    36  	crash2 := testCrashWithRepro(build, 2)
    37  	c.client2.ReportCrash(crash2)
    38  	msg2 := c.client2.pollEmailBug()
    39  
    40  	// This is later, so will be bisected before the previous crash.
    41  	c.advanceTime(time.Hour)
    42  	crash3 := testCrashWithRepro(build, 3)
    43  	c.client2.ReportCrash(crash3)
    44  	c.client2.pollEmailBug()
    45  
    46  	// This does not have C repro, so will be bisected after the previous ones.
    47  	c.advanceTime(time.Hour)
    48  	crash4 := testCrashWithRepro(build, 4)
    49  	crash4.Title = "skip reporting2 with repro"
    50  	crash4.ReproC = nil
    51  	c.client2.ReportCrash(crash4)
    52  	msg4 := c.client2.pollEmailBug()
    53  
    54  	// This is from a different manager, so won't be bisected.
    55  	c.advanceTime(time.Hour)
    56  	build2 := testBuild(2)
    57  	c.client2.UploadBuild(build2)
    58  	crash5 := testCrashWithRepro(build2, 5)
    59  	c.client2.ReportCrash(crash5)
    60  	c.client2.pollEmailBug()
    61  
    62  	// When polling for jobs the expected order is as follows :=
    63  	//		BisectCause #3
    64  	//		BisectCause #2
    65  	//		BisectCause #4
    66  	// After advancing time by 30 days, we get :=
    67  	//		BisectFix   #2
    68  	//		BisectFix   #3
    69  	//		BisectFix   #4
    70  
    71  	// BisectCause #3
    72  	pollResp = c.client2.pollJobs(build.Manager)
    73  	c.expectNE(pollResp.ID, "")
    74  	c.expectEQ(pollResp.Type, dashapi.JobBisectCause)
    75  	c.expectEQ(pollResp.Manager, build.Manager)
    76  	c.expectEQ(pollResp.KernelConfig, build.KernelConfig)
    77  	c.expectEQ(pollResp.SyzkallerCommit, build.SyzkallerCommit)
    78  	c.expectEQ(pollResp.ReproOpts, []byte("repro opts 3"))
    79  	c.expectEQ(pollResp.ReproSyz, []byte(
    80  		"# See https://goo.gl/kgGztJ for information about syzkaller reproducers.\n"+
    81  			"#repro opts 3\n"+
    82  			"syncfs(3)"))
    83  	c.expectEQ(pollResp.ReproC, []byte("int main() { return 3; }"))
    84  
    85  	// Bisection failed with an error.
    86  	done := &dashapi.JobDoneReq{
    87  		ID:    pollResp.ID,
    88  		Log:   []byte("bisect log 3"),
    89  		Error: []byte("bisect error 3"),
    90  	}
    91  	c.expectOK(c.client2.JobDone(done))
    92  	c.expectNoEmail()
    93  
    94  	// BisectCause #2
    95  	pollResp2 := pollResp
    96  	c.advanceTime(time.Minute)
    97  	pollResp = c.client2.pollJobs(build.Manager)
    98  	c.expectNE(pollResp.ID, pollResp2.ID)
    99  	c.expectEQ(pollResp.ReproOpts, []byte("repro opts 2"))
   100  
   101  	// Bisection succeeded.
   102  	jobID := pollResp.ID
   103  	done = &dashapi.JobDoneReq{
   104  		ID:          jobID,
   105  		Build:       *build,
   106  		Log:         []byte("bisect log 2"),
   107  		CrashTitle:  "bisect crash title",
   108  		CrashLog:    []byte("bisect crash log"),
   109  		CrashReport: []byte("bisect crash report"),
   110  		Commits: []dashapi.Commit{
   111  			{
   112  				Hash:       "36e65cb4a0448942ec316b24d60446bbd5cc7827",
   113  				Title:      "kernel: add a bug",
   114  				Author:     "author@kernel.org",
   115  				AuthorName: "Author Kernelov",
   116  				CC: []string{
   117  					"reviewer1@kernel.org", "\"Reviewer2\" <reviewer2@kernel.org>",
   118  					// These must be filtered out:
   119  					"syzbot@testapp.appspotmail.com",
   120  					"syzbot+1234@testapp.appspotmail.com",
   121  					"\"syzbot\" <syzbot+1234@testapp.appspotmail.com>",
   122  				},
   123  				Date: time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   124  			},
   125  		},
   126  	}
   127  	done.Build.ID = jobID
   128  	c.expectOK(c.client2.JobDone(done))
   129  
   130  	_, extBugID, err := email.RemoveAddrContext(msg2.Sender)
   131  	c.expectOK(err)
   132  	dbBug, dbCrash, _ := c.loadBug(extBugID)
   133  	reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   134  	reproCLink := externalLink(c.ctx, textReproC, dbCrash.ReproC)
   135  	dbJob, dbBuild, dbJobCrash := c.loadJob(jobID)
   136  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   137  	bisectCrashReportLink := externalLink(c.ctx, textCrashReport, dbJob.CrashReport)
   138  	bisectCrashLogLink := externalLink(c.ctx, textCrashLog, dbJob.CrashLog)
   139  	bisectLogLink := externalLink(c.ctx, textLog, dbJob.Log)
   140  	crashLogLink := externalLink(c.ctx, textCrashLog, dbJobCrash.Log)
   141  
   142  	{
   143  		msg := c.pollEmailBug()
   144  		// Not mailed to commit author/cc because !MailMaintainers.
   145  		c.expectEQ(msg.To, []string{"test@syzkaller.com"})
   146  		c.expectEQ(msg.Subject, crash2.Title)
   147  		c.expectEQ(len(msg.Attachments), 0)
   148  		c.expectEQ(msg.Body, fmt.Sprintf(`syzbot has bisected this issue to:
   149  
   150  commit 36e65cb4a0448942ec316b24d60446bbd5cc7827
   151  Author: Author Kernelov <author@kernel.org>
   152  Date:   Wed Feb 9 04:05:06 2000 +0000
   153  
   154      kernel: add a bug
   155  
   156  bisection log:  %[2]v
   157  start commit:   111111111111 kernel_commit_title1
   158  git tree:       repo1 branch1
   159  final oops:     %[3]v
   160  console output: %[4]v
   161  kernel config:  %[5]v
   162  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   163  syz repro:      %[6]v
   164  C reproducer:   %[7]v
   165  
   166  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   167  Fixes: 36e65cb4a044 ("kernel: add a bug")
   168  
   169  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   170  `, extBugID, bisectLogLink, bisectCrashReportLink, bisectCrashLogLink, kernelConfigLink, reproSyzLink, reproCLink))
   171  
   172  		syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s",
   173  			dbBug.keyHash(c.ctx), syzReproPrefix, crash2.ReproOpts, crash2.ReproSyz))
   174  		cRepro := []byte(fmt.Sprintf("// https://testapp.appspot.com/bug?id=%v\n%s",
   175  			dbBug.keyHash(c.ctx), crash2.ReproC))
   176  		c.checkURLContents(bisectLogLink, []byte("bisect log 2"))
   177  		c.checkURLContents(bisectCrashReportLink, []byte("bisect crash report"))
   178  		c.checkURLContents(bisectCrashLogLink, []byte("bisect crash log"))
   179  		c.checkURLContents(kernelConfigLink, []byte("config1"))
   180  		c.checkURLContents(reproSyzLink, syzRepro)
   181  		c.checkURLContents(reproCLink, cRepro)
   182  	}
   183  
   184  	// The next reporting must get bug report with bisection results.
   185  	c.incomingEmail(msg2.Sender, "#syz upstream")
   186  	{
   187  		msg := c.pollEmailBug()
   188  		_, extBugID2, err := email.RemoveAddrContext(msg.Sender)
   189  		c.expectOK(err)
   190  
   191  		c.expectEQ(msg.To, []string{
   192  			"author@kernel.org",
   193  			"bugs@syzkaller.com",
   194  			"default@maintainers.com",
   195  			"reviewer1@kernel.org",
   196  			"reviewer2@kernel.org",
   197  		})
   198  		c.expectEQ(msg.Subject, "[syzbot] "+crash2.Title)
   199  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   200  
   201  syzbot found the following issue on:
   202  
   203  HEAD commit:    111111111111 kernel_commit_title1
   204  git tree:       repo1 branch1
   205  console output: %[2]v
   206  kernel config:  %[3]v
   207  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   208  compiler:       compiler1
   209  syz repro:      %[4]v
   210  C reproducer:   %[5]v
   211  CC:             [author@kernel.org reviewer1@kernel.org reviewer2@kernel.org]
   212  
   213  The issue was bisected to:
   214  
   215  commit 36e65cb4a0448942ec316b24d60446bbd5cc7827
   216  Author: Author Kernelov <author@kernel.org>
   217  Date:   Wed Feb 9 04:05:06 2000 +0000
   218  
   219      kernel: add a bug
   220  
   221  bisection log:  %[6]v
   222  final oops:     %[7]v
   223  console output: %[8]v
   224  
   225  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   226  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   227  Fixes: 36e65cb4a044 ("kernel: add a bug")
   228  
   229  report2
   230  
   231  ---
   232  This report is generated by a bot. It may contain errors.
   233  See https://goo.gl/tpsmEJ for more information about syzbot.
   234  syzbot engineers can be reached at syzkaller@googlegroups.com.
   235  
   236  syzbot will keep track of this issue. See:
   237  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   238  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   239  
   240  If the report is already addressed, let syzbot know by replying with:
   241  #syz fix: exact-commit-title
   242  
   243  If you want syzbot to run the reproducer, reply with:
   244  #syz test: git://repo/address.git branch-or-commit-hash
   245  If you attach or paste a git patch, syzbot will apply it before testing.
   246  
   247  If you want to overwrite report's subsystems, reply with:
   248  #syz set subsystems: new-subsystem
   249  (See the list of subsystem names on the web dashboard)
   250  
   251  If the report is a duplicate of another one, reply with:
   252  #syz dup: exact-subject-of-another-report
   253  
   254  If you want to undo deduplication, reply with:
   255  #syz undup`,
   256  			extBugID2, crashLogLink, kernelConfigLink, reproSyzLink, reproCLink,
   257  			bisectLogLink, bisectCrashReportLink, bisectCrashLogLink))
   258  	}
   259  
   260  	// BisectCause #4
   261  	// Crash 4 is bisected in reporting with MailMaintainers.
   262  	// It also skipped second reporting because of the title.
   263  	c.incomingEmail(msg4.Sender, "#syz upstream")
   264  	msg4 = c.pollEmailBug()
   265  	c.expectEQ(msg4.To, []string{
   266  		"bugs2@syzkaller.com",
   267  		"default2@maintainers.com",
   268  	})
   269  	c.advanceTime(time.Minute)
   270  	pollResp = c.client2.pollJobs(build.Manager)
   271  
   272  	// Bisection succeeded.
   273  	jobID = pollResp.ID
   274  	done = &dashapi.JobDoneReq{
   275  		ID:          jobID,
   276  		Build:       *build,
   277  		Log:         []byte("bisectcause log 4"),
   278  		CrashTitle:  "bisectcause crash title 4",
   279  		CrashLog:    []byte("bisectcause crash log 4"),
   280  		CrashReport: []byte("bisectcause crash report 4"),
   281  		Commits: []dashapi.Commit{
   282  			{
   283  				Hash:       "36e65cb4a0448942ec316b24d60446bbd5cc7827",
   284  				Title:      "kernel: add a bug",
   285  				Author:     "author@kernel.org",
   286  				AuthorName: "Author Kernelov",
   287  				CC: []string{
   288  					"reviewer1@kernel.org", "\"Reviewer2\" <reviewer2@kernel.org>",
   289  					// These must be filtered out:
   290  					"syzbot@testapp.appspotmail.com",
   291  					"syzbot+1234@testapp.appspotmail.com",
   292  					"\"syzbot\" <syzbot+1234@testapp.appspotmail.com>",
   293  				},
   294  				Date: time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   295  			},
   296  		},
   297  	}
   298  	done.Build.ID = jobID
   299  	c.expectOK(c.client2.JobDone(done))
   300  
   301  	{
   302  		msg := c.pollEmailBug()
   303  		c.expectEQ(msg.Subject, crash4.Title)
   304  		c.expectEQ(msg.To, []string{
   305  			"author@kernel.org",
   306  			"bugs2@syzkaller.com",
   307  			"default2@maintainers.com",
   308  			"reviewer1@kernel.org",
   309  			"reviewer2@kernel.org",
   310  		})
   311  	}
   312  
   313  	{
   314  		c.advanceTime(30 * 24 * time.Hour)
   315  		subjects := []string{"title3", "title1", "title5", "title3", "title5", "title1"}
   316  		for i := 0; i < 6; i++ {
   317  			msg := c.pollEmailBug()
   318  			if i < 3 {
   319  				c.expectEQ(msg.Subject, subjects[i])
   320  				c.expectTrue(strings.Contains(msg.Body, "Sending this report to the next reporting stage."))
   321  			} else {
   322  				c.expectEQ(msg.Subject, "[syzbot] "+subjects[i])
   323  				c.expectTrue(strings.Contains(msg.Body, "syzbot found the following issue on"))
   324  			}
   325  		}
   326  	}
   327  
   328  	// BisectFix #2
   329  	c.advanceTime(time.Minute)
   330  	pollResp = c.client2.pollJobs(build.Manager)
   331  	c.expectNE(pollResp.ID, "")
   332  	c.expectEQ(pollResp.Type, dashapi.JobBisectFix)
   333  	c.expectEQ(pollResp.ReproOpts, []byte("repro opts 2"))
   334  	c.advanceTime(5 * 24 * time.Hour)
   335  	done = &dashapi.JobDoneReq{
   336  		ID:    pollResp.ID,
   337  		Log:   []byte("bisect log 2"),
   338  		Error: []byte("bisect error 2"),
   339  	}
   340  	c.expectOK(c.client2.JobDone(done))
   341  
   342  	// BisectFix #3
   343  	c.advanceTime(time.Minute)
   344  	pollResp = c.client2.pollJobs(build.Manager)
   345  	c.expectNE(pollResp.ID, "")
   346  	c.expectEQ(pollResp.Type, dashapi.JobBisectFix)
   347  	c.expectEQ(pollResp.ReproOpts, []byte("repro opts 3"))
   348  	done = &dashapi.JobDoneReq{
   349  		ID:    pollResp.ID,
   350  		Log:   []byte("bisect log 3"),
   351  		Error: []byte("bisect error 3"),
   352  	}
   353  	c.expectOK(c.client2.JobDone(done))
   354  
   355  	// BisectFix #4
   356  	c.advanceTime(time.Minute)
   357  	pollResp = c.client2.pollJobs(build.Manager)
   358  	c.expectNE(pollResp.ID, "")
   359  	c.expectEQ(pollResp.Type, dashapi.JobBisectFix)
   360  	c.expectEQ(pollResp.ReproOpts, []byte("repro opts 4"))
   361  	jobID = pollResp.ID
   362  	done = &dashapi.JobDoneReq{
   363  		ID:          jobID,
   364  		Build:       *build,
   365  		Log:         []byte("bisectfix log 4"),
   366  		CrashTitle:  "bisectfix crash title 4",
   367  		CrashLog:    []byte("bisectfix crash log 4"),
   368  		CrashReport: []byte("bisectfix crash report 4"),
   369  		Commits: []dashapi.Commit{
   370  			{
   371  				Hash:       "46e65cb4a0448942ec316b24d60446bbd5cc7827",
   372  				Title:      "kernel: add a fix",
   373  				Author:     "author@kernel.org",
   374  				AuthorName: "Author Kernelov",
   375  				CC: []string{
   376  					"reviewer1@kernel.org", "\"Reviewer2\" <reviewer2@kernel.org>",
   377  					// These must be filtered out:
   378  					"syzbot@testapp.appspotmail.com",
   379  					"syzbot+1234@testapp.appspotmail.com",
   380  					"\"syzbot\" <syzbot+1234@testapp.appspotmail.com>",
   381  				},
   382  				Date: time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   383  			},
   384  		},
   385  	}
   386  	done.Build.ID = jobID
   387  	c.expectOK(c.client2.JobDone(done))
   388  
   389  	_, extBugID, err = email.RemoveAddrContext(msg4.Sender)
   390  	c.expectOK(err)
   391  	dbBug, dbCrash, _ = c.loadBug(extBugID)
   392  	reproSyzLink = externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   393  	reproCLink = externalLink(c.ctx, textReproC, dbCrash.ReproC)
   394  	dbJob, dbBuild, _ = c.loadJob(jobID)
   395  	kernelConfigLink = externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   396  	bisectCrashReportLink = externalLink(c.ctx, textCrashReport, dbJob.CrashReport)
   397  	bisectCrashLogLink = externalLink(c.ctx, textCrashLog, dbJob.CrashLog)
   398  	bisectLogLink = externalLink(c.ctx, textLog, dbJob.Log)
   399  
   400  	{
   401  		msg := c.pollEmailBug()
   402  		// Not mailed to commit author/cc because !MailMaintainers.
   403  		// c.expectEQ(msg.To, []string{"test@syzkaller.com"})
   404  		c.expectEQ(msg.Subject, crash4.Title)
   405  		c.expectEQ(len(msg.Attachments), 0)
   406  		c.expectEQ(msg.Body, fmt.Sprintf(`syzbot suspects this issue was fixed by commit:
   407  
   408  commit 46e65cb4a0448942ec316b24d60446bbd5cc7827
   409  Author: Author Kernelov <author@kernel.org>
   410  Date:   Wed Feb 9 04:05:06 2000 +0000
   411  
   412      kernel: add a fix
   413  
   414  bisection log:  %[2]v
   415  start commit:   111111111111 kernel_commit_title1
   416  git tree:       repo1 branch1
   417  final oops:     %[3]v
   418  console output: %[4]v
   419  kernel config:  %[5]v
   420  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   421  syz repro:      %[6]v
   422  
   423  If the result looks correct, please mark the issue as fixed by replying with:
   424  
   425  #syz fix: kernel: add a fix
   426  
   427  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   428  `, extBugID, bisectLogLink, bisectCrashReportLink, bisectCrashLogLink, kernelConfigLink, reproSyzLink, reproCLink))
   429  
   430  		syzRepro := []byte(fmt.Sprintf("# https://testapp.appspot.com/bug?id=%v\n%s#%s\n%s",
   431  			dbBug.keyHash(c.ctx), syzReproPrefix, crash4.ReproOpts, crash4.ReproSyz))
   432  		c.checkURLContents(bisectLogLink, []byte("bisectfix log 4"))
   433  		c.checkURLContents(bisectCrashReportLink, []byte("bisectfix crash report 4"))
   434  		c.checkURLContents(bisectCrashLogLink, []byte("bisectfix crash log 4"))
   435  		c.checkURLContents(kernelConfigLink, []byte("config1"))
   436  		c.checkURLContents(reproSyzLink, syzRepro)
   437  	}
   438  
   439  	// No more bisection jobs.
   440  	pollResp = c.client2.pollJobs(build.Manager)
   441  	c.expectEQ(pollResp.ID, "")
   442  }
   443  
   444  func TestBisectCauseInconclusive(t *testing.T) {
   445  	c := NewCtx(t)
   446  	defer c.Close()
   447  
   448  	build := testBuild(1)
   449  	c.client2.UploadBuild(build)
   450  	crash := testCrashWithRepro(build, 1)
   451  	c.client2.ReportCrash(crash)
   452  	msg := c.client2.pollEmailBug()
   453  
   454  	pollResp := c.client2.pollJobs(build.Manager)
   455  	jobID := pollResp.ID
   456  	done := &dashapi.JobDoneReq{
   457  		ID:    jobID,
   458  		Build: *build,
   459  		Log:   []byte("bisect log"),
   460  		Commits: []dashapi.Commit{
   461  			{
   462  				Hash:       "111111111111111111111111",
   463  				Title:      "kernel: break build",
   464  				Author:     "hacker@kernel.org",
   465  				AuthorName: "Hacker Kernelov",
   466  				CC:         []string{"reviewer1@kernel.org", "reviewer2@kernel.org"},
   467  				Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   468  			},
   469  			{
   470  				Hash:       "222222222222222222222222",
   471  				Title:      "kernel: now add a bug to the broken build",
   472  				Author:     "author@kernel.org",
   473  				AuthorName: "Author Kernelov",
   474  				CC:         []string{"reviewer3@kernel.org", "reviewer4@kernel.org"},
   475  				Date:       time.Date(2001, 2, 9, 4, 5, 6, 7, time.UTC),
   476  			},
   477  		},
   478  	}
   479  	done.Build.ID = jobID
   480  	c.expectOK(c.client2.JobDone(done))
   481  
   482  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
   483  	c.expectOK(err)
   484  	_, dbCrash, _ := c.loadBug(extBugID)
   485  	reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   486  	reproCLink := externalLink(c.ctx, textReproC, dbCrash.ReproC)
   487  	dbJob, dbBuild, dbJobCrash := c.loadJob(jobID)
   488  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   489  	bisectLogLink := externalLink(c.ctx, textLog, dbJob.Log)
   490  	crashLogLink := externalLink(c.ctx, textCrashLog, dbJobCrash.Log)
   491  
   492  	{
   493  		msg := c.pollEmailBug()
   494  		// Not mailed to commit author/cc because !MailMaintainers.
   495  		c.expectEQ(msg.To, []string{"test@syzkaller.com"})
   496  		c.expectEQ(msg.Subject, crash.Title)
   497  		c.expectEQ(len(msg.Attachments), 0)
   498  		c.expectEQ(msg.Body, fmt.Sprintf(`Bisection is inconclusive: the first bad commit could be any of:
   499  
   500  111111111111 kernel: break build
   501  222222222222 kernel: now add a bug to the broken build
   502  
   503  bisection log:  %[2]v
   504  start commit:   111111111111 kernel_commit_title1
   505  git tree:       repo1 branch1
   506  kernel config:  %[3]v
   507  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   508  syz repro:      %[4]v
   509  C reproducer:   %[5]v
   510  
   511  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   512  `, extBugID, bisectLogLink, kernelConfigLink, reproSyzLink, reproCLink))
   513  	}
   514  
   515  	// The next reporting must get bug report with bisection results.
   516  	c.incomingEmail(msg.Sender, "#syz upstream")
   517  	{
   518  		msg := c.pollEmailBug()
   519  		_, extBugID2, err := email.RemoveAddrContext(msg.Sender)
   520  		c.expectOK(err)
   521  		c.expectEQ(msg.To, []string{
   522  			"bugs@syzkaller.com",
   523  			"default@maintainers.com",
   524  		})
   525  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   526  
   527  syzbot found the following issue on:
   528  
   529  HEAD commit:    111111111111 kernel_commit_title1
   530  git tree:       repo1 branch1
   531  console output: %[2]v
   532  kernel config:  %[3]v
   533  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   534  compiler:       compiler1
   535  syz repro:      %[4]v
   536  C reproducer:   %[5]v
   537  
   538  Bisection is inconclusive: the first bad commit could be any of:
   539  
   540  111111111111 kernel: break build
   541  222222222222 kernel: now add a bug to the broken build
   542  
   543  bisection log:  %[6]v
   544  
   545  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   546  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   547  
   548  report1
   549  
   550  ---
   551  This report is generated by a bot. It may contain errors.
   552  See https://goo.gl/tpsmEJ for more information about syzbot.
   553  syzbot engineers can be reached at syzkaller@googlegroups.com.
   554  
   555  syzbot will keep track of this issue. See:
   556  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   557  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   558  
   559  If the report is already addressed, let syzbot know by replying with:
   560  #syz fix: exact-commit-title
   561  
   562  If you want syzbot to run the reproducer, reply with:
   563  #syz test: git://repo/address.git branch-or-commit-hash
   564  If you attach or paste a git patch, syzbot will apply it before testing.
   565  
   566  If you want to overwrite report's subsystems, reply with:
   567  #syz set subsystems: new-subsystem
   568  (See the list of subsystem names on the web dashboard)
   569  
   570  If the report is a duplicate of another one, reply with:
   571  #syz dup: exact-subject-of-another-report
   572  
   573  If you want to undo deduplication, reply with:
   574  #syz undup`,
   575  			extBugID2, crashLogLink, kernelConfigLink, reproSyzLink, reproCLink, bisectLogLink))
   576  	}
   577  }
   578  
   579  func TestUnreliableBisect(t *testing.T) {
   580  	c := NewCtx(t)
   581  	defer c.Close()
   582  
   583  	build := testBuild(1)
   584  	c.client2.UploadBuild(build)
   585  	// Upload a crash that has only a syz repro.
   586  	crash := testCrashWithRepro(build, 1)
   587  	crash.ReproC = nil
   588  	c.client2.ReportCrash(crash)
   589  	_ = c.client2.pollEmailBug()
   590  
   591  	pollResp := c.client2.pollJobs(build.Manager)
   592  	jobID := pollResp.ID
   593  	done := &dashapi.JobDoneReq{
   594  		ID:    jobID,
   595  		Build: *build,
   596  		Log:   []byte("bisect log"),
   597  		Flags: dashapi.BisectResultRelease,
   598  		Commits: []dashapi.Commit{
   599  			{
   600  				Hash:       "111111111111111111111111",
   601  				Title:      "Linux 4.10",
   602  				Author:     "abcd@kernel.org",
   603  				AuthorName: "Abcd Efgh",
   604  				CC:         []string{"reviewer1@kernel.org", "reviewer2@kernel.org"},
   605  				Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   606  			},
   607  		},
   608  	}
   609  	done.Build.ID = jobID
   610  	c.expectOK(c.client2.JobDone(done))
   611  
   612  	// The bisection result is unreliable - it shouldn't be reported.
   613  	c.expectNoEmail()
   614  
   615  	// Upload a crash with a C repro.
   616  	crash2 := testCrashWithRepro(build, 1)
   617  	c.client2.ReportCrash(crash2)
   618  
   619  	// Make sure it doesn't mention bisection and doesn't include the emails from it.
   620  	msg := c.pollEmailBug()
   621  	c.expectEQ(msg.To, []string{"test@syzkaller.com"})
   622  	c.expectEQ(msg.Subject, crash.Title)
   623  	c.expectTrue(strings.Contains(msg.Body, "syzbot has found a reproducer for the following issue"))
   624  	c.expectTrue(!strings.Contains(msg.Body, "bisection"))
   625  }
   626  
   627  func TestBisectWrong(t *testing.T) {
   628  	// Test bisection results with BisectResultMerge/BisectResultNoop flags set.
   629  	// If any of these set, the result must not be reported separately,
   630  	// as part of bug report during upstreamming, nor should affect CC list.
   631  	c := NewCtx(t)
   632  	defer c.Close()
   633  
   634  	// Otherwise "Bug obsoleted" emails mix in at random times.
   635  	c.setNoObsoletions()
   636  
   637  	build := testBuild(1)
   638  	c.client2.UploadBuild(build)
   639  	for i := 0; i < 6; i++ {
   640  		var flags dashapi.JobDoneFlags
   641  		switch i {
   642  		case 0:
   643  		case 1:
   644  			flags = dashapi.BisectResultMerge
   645  		case 2:
   646  			flags = dashapi.BisectResultNoop
   647  		case 3:
   648  			flags = dashapi.BisectResultMerge | dashapi.BisectResultNoop
   649  		case 4:
   650  			flags = dashapi.BisectResultRelease
   651  		case 5:
   652  			flags = dashapi.BisectResultIgnore
   653  		default:
   654  			t.Fatalf("assign flags")
   655  		}
   656  		t.Logf("iteration %v: flags=%v", i, flags)
   657  
   658  		crash := testCrashWithRepro(build, i)
   659  		c.client2.ReportCrash(crash)
   660  		c.client2.pollEmailBug()
   661  
   662  		{
   663  			pollResp := c.client2.pollJobs(build.Manager)
   664  			done := &dashapi.JobDoneReq{
   665  				ID:    pollResp.ID,
   666  				Flags: flags,
   667  				Build: *build,
   668  				Log:   []byte("bisect log"),
   669  				Commits: []dashapi.Commit{
   670  					{
   671  						Hash:       "111111111111111111111111",
   672  						Title:      "kernel: break build",
   673  						Author:     "hacker@kernel.org",
   674  						AuthorName: "Hacker Kernelov",
   675  						Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   676  					},
   677  				},
   678  			}
   679  			done.Build.ID = pollResp.ID
   680  			c.expectOK(c.client2.JobDone(done))
   681  			if i == 0 {
   682  				msg := c.pollEmailBug()
   683  				c.expectTrue(strings.Contains(msg.Body, "syzbot has bisected this issue to:"))
   684  			} else {
   685  				c.expectNoEmail()
   686  			}
   687  		}
   688  		{
   689  			c.advanceTime(31 * 24 * time.Hour)
   690  			pollResp := c.client2.pollJobs(build.Manager)
   691  			done := &dashapi.JobDoneReq{
   692  				ID:          pollResp.ID,
   693  				Flags:       flags,
   694  				Build:       *build,
   695  				Log:         []byte("bisectfix log 4"),
   696  				CrashTitle:  "bisectfix crash title 4",
   697  				CrashLog:    []byte("bisectfix crash log 4"),
   698  				CrashReport: []byte("bisectfix crash report 4"),
   699  				Commits: []dashapi.Commit{
   700  					{
   701  						Hash:       "46e65cb4a0448942ec316b24d60446bbd5cc7827",
   702  						Title:      "kernel: add a fix",
   703  						Author:     "fixer@kernel.org",
   704  						AuthorName: "Author Kernelov",
   705  						Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   706  					},
   707  				},
   708  			}
   709  			done.Build.ID = pollResp.ID
   710  			c.expectOK(c.client2.JobDone(done))
   711  			if i == 0 {
   712  				msg := c.pollEmailBug()
   713  				c.expectTrue(strings.Contains(msg.Body, "syzbot suspects this issue was fixed by commit:"))
   714  			}
   715  		}
   716  		{
   717  			// Auto-upstreamming.
   718  			c.advanceTime(31 * 24 * time.Hour)
   719  			msg := c.pollEmailBug()
   720  			c.expectTrue(strings.Contains(msg.Body, "Sending this report to the next reporting stage."))
   721  			msg = c.pollEmailBug()
   722  			c.expectTrue(strings.Contains(msg.Body, "syzbot found the following issue on:"))
   723  			if i == 0 {
   724  				c.expectTrue(strings.Contains(msg.Body, "The issue was bisected to:"))
   725  				c.expectEQ(msg.To, []string{
   726  					"bugs@syzkaller.com",
   727  					"default@maintainers.com",
   728  					"hacker@kernel.org",
   729  				})
   730  			} else {
   731  				c.expectTrue(!strings.Contains(msg.Body, "The issue was bisected to:"))
   732  				c.expectEQ(msg.To, []string{
   733  					"bugs@syzkaller.com",
   734  					"default@maintainers.com",
   735  				})
   736  			}
   737  		}
   738  		c.expectNoEmail()
   739  	}
   740  }
   741  
   742  func TestBisectCauseAncient(t *testing.T) {
   743  	c := NewCtx(t)
   744  	defer c.Close()
   745  
   746  	build := testBuild(1)
   747  	c.client2.UploadBuild(build)
   748  	crash := testCrashWithRepro(build, 1)
   749  	c.client2.ReportCrash(crash)
   750  	msg := c.client2.pollEmailBug()
   751  
   752  	pollResp := c.client2.pollJobs(build.Manager)
   753  	jobID := pollResp.ID
   754  	done := &dashapi.JobDoneReq{
   755  		ID:          jobID,
   756  		Build:       *build,
   757  		Log:         []byte("bisect log"),
   758  		CrashTitle:  "bisect crash title",
   759  		CrashLog:    []byte("bisect crash log"),
   760  		CrashReport: []byte("bisect crash report"),
   761  	}
   762  	done.Build.ID = jobID
   763  	c.expectOK(c.client2.JobDone(done))
   764  
   765  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
   766  	c.expectOK(err)
   767  	_, dbCrash, _ := c.loadBug(extBugID)
   768  	reproSyzLink := externalLink(c.ctx, textReproSyz, dbCrash.ReproSyz)
   769  	reproCLink := externalLink(c.ctx, textReproC, dbCrash.ReproC)
   770  	dbJob, dbBuild, dbJobCrash := c.loadJob(jobID)
   771  	bisectCrashReportLink := externalLink(c.ctx, textCrashReport, dbJob.CrashReport)
   772  	bisectCrashLogLink := externalLink(c.ctx, textCrashLog, dbJob.CrashLog)
   773  	kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig)
   774  	bisectLogLink := externalLink(c.ctx, textLog, dbJob.Log)
   775  	crashLogLink := externalLink(c.ctx, textCrashLog, dbJobCrash.Log)
   776  
   777  	{
   778  		msg := c.pollEmailBug()
   779  		// Not mailed to commit author/cc because !MailMaintainers.
   780  		c.expectEQ(msg.To, []string{"test@syzkaller.com"})
   781  		c.expectEQ(msg.Subject, crash.Title)
   782  		c.expectEQ(len(msg.Attachments), 0)
   783  		c.expectEQ(msg.Body, fmt.Sprintf(`Bisection is inconclusive: the issue happens on the oldest tested release.
   784  
   785  bisection log:  %[2]v
   786  oldest commit:  111111111111 kernel_commit_title1
   787  git tree:       repo1 branch1
   788  final oops:     %[3]v
   789  console output: %[4]v
   790  kernel config:  %[5]v
   791  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   792  syz repro:      %[6]v
   793  C reproducer:   %[7]v
   794  
   795  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   796  `, extBugID, bisectLogLink, bisectCrashReportLink, bisectCrashLogLink,
   797  			kernelConfigLink, reproSyzLink, reproCLink))
   798  	}
   799  
   800  	// The next reporting must get bug report with bisection results.
   801  	c.incomingEmail(msg.Sender, "#syz upstream")
   802  	{
   803  		msg := c.pollEmailBug()
   804  		_, extBugID2, err := email.RemoveAddrContext(msg.Sender)
   805  		c.expectOK(err)
   806  		c.expectEQ(msg.To, []string{
   807  			"bugs@syzkaller.com",
   808  			"default@maintainers.com",
   809  		})
   810  		c.expectEQ(msg.Body, fmt.Sprintf(`Hello,
   811  
   812  syzbot found the following issue on:
   813  
   814  HEAD commit:    111111111111 kernel_commit_title1
   815  git tree:       repo1 branch1
   816  console output: %[2]v
   817  kernel config:  %[3]v
   818  dashboard link: https://testapp.appspot.com/bug?extid=%[1]v
   819  compiler:       compiler1
   820  syz repro:      %[4]v
   821  C reproducer:   %[5]v
   822  
   823  Bisection is inconclusive: the issue happens on the oldest tested release.
   824  
   825  bisection log:  %[6]v
   826  final oops:     %[7]v
   827  console output: %[8]v
   828  
   829  IMPORTANT: if you fix the issue, please add the following tag to the commit:
   830  Reported-by: syzbot+%[1]v@testapp.appspotmail.com
   831  
   832  report1
   833  
   834  ---
   835  This report is generated by a bot. It may contain errors.
   836  See https://goo.gl/tpsmEJ for more information about syzbot.
   837  syzbot engineers can be reached at syzkaller@googlegroups.com.
   838  
   839  syzbot will keep track of this issue. See:
   840  https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
   841  For information about bisection process see: https://goo.gl/tpsmEJ#bisection
   842  
   843  If the report is already addressed, let syzbot know by replying with:
   844  #syz fix: exact-commit-title
   845  
   846  If you want syzbot to run the reproducer, reply with:
   847  #syz test: git://repo/address.git branch-or-commit-hash
   848  If you attach or paste a git patch, syzbot will apply it before testing.
   849  
   850  If you want to overwrite report's subsystems, reply with:
   851  #syz set subsystems: new-subsystem
   852  (See the list of subsystem names on the web dashboard)
   853  
   854  If the report is a duplicate of another one, reply with:
   855  #syz dup: exact-subject-of-another-report
   856  
   857  If you want to undo deduplication, reply with:
   858  #syz undup`,
   859  			extBugID2, crashLogLink, kernelConfigLink, reproSyzLink, reproCLink,
   860  			bisectLogLink, bisectCrashReportLink, bisectCrashLogLink))
   861  	}
   862  }
   863  
   864  func TestBisectCauseExternal(t *testing.T) {
   865  	c := NewCtx(t)
   866  	defer c.Close()
   867  
   868  	build := testBuild(1)
   869  	c.client.UploadBuild(build)
   870  	crash := testCrashWithRepro(build, 1)
   871  	c.client.ReportCrash(crash)
   872  	rep := c.client.pollBug()
   873  
   874  	pollResp := c.client.pollJobs(build.Manager)
   875  	c.expectNE(pollResp.ID, "")
   876  	jobID := pollResp.ID
   877  	done := &dashapi.JobDoneReq{
   878  		ID:    jobID,
   879  		Build: *build,
   880  		Log:   []byte("bisect log"),
   881  		Commits: []dashapi.Commit{
   882  			{
   883  				Hash:       "111111111111111111111111",
   884  				Title:      "kernel: break build",
   885  				Author:     "hacker@kernel.org",
   886  				AuthorName: "Hacker Kernelov",
   887  				CC:         []string{"reviewer1@kernel.org", "reviewer2@kernel.org"},
   888  				Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   889  			},
   890  		},
   891  	}
   892  	done.Build.ID = jobID
   893  	c.expectOK(c.client.JobDone(done))
   894  
   895  	resp, _ := c.client.ReportingPollBugs("test")
   896  	c.expectEQ(len(resp.Reports), 1)
   897  	// Still reported because we did not ack.
   898  	bisect := c.client.pollBug()
   899  	// pollBug acks, must not be reported after that.
   900  	c.client.pollBugs(0)
   901  
   902  	c.expectEQ(bisect.Type, dashapi.ReportBisectCause)
   903  	c.expectEQ(bisect.Title, rep.Title)
   904  }
   905  
   906  func TestBisectFixExternal(t *testing.T) {
   907  	c := NewCtx(t)
   908  	defer c.Close()
   909  
   910  	build := testBuild(1)
   911  	c.client.UploadBuild(build)
   912  	crash := testCrashWithRepro(build, 1)
   913  	c.client.ReportCrash(crash)
   914  	rep := c.client.pollBug()
   915  	{
   916  		// Cause bisection fails.
   917  		pollResp := c.client.pollJobs(build.Manager)
   918  		done := &dashapi.JobDoneReq{
   919  			ID:    pollResp.ID,
   920  			Log:   []byte("bisect log"),
   921  			Error: []byte("bisect error"),
   922  		}
   923  		c.expectOK(c.client.JobDone(done))
   924  	}
   925  	c.advanceTime(31 * 24 * time.Hour)
   926  	{
   927  		// Fix bisection succeeds.
   928  		pollResp := c.client.pollJobs(build.Manager)
   929  		done := &dashapi.JobDoneReq{
   930  			ID:          pollResp.ID,
   931  			Build:       *build,
   932  			Log:         []byte("bisectfix log"),
   933  			CrashTitle:  "bisectfix crash title",
   934  			CrashLog:    []byte("bisectfix crash log"),
   935  			CrashReport: []byte("bisectfix crash report"),
   936  			Commits: []dashapi.Commit{
   937  				{
   938  					Hash:       "46e65cb4a0448942ec316b24d60446bbd5cc7827",
   939  					Title:      "kernel: add a fix",
   940  					Author:     "fixer@kernel.org",
   941  					AuthorName: "Author Kernelov",
   942  					Date:       time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
   943  				},
   944  			},
   945  		}
   946  		done.Build.ID = pollResp.ID
   947  		c.expectOK(c.client.JobDone(done))
   948  		rep := c.client.pollBug()
   949  		c.expectEQ(rep.Type, dashapi.ReportBisectFix)
   950  	}
   951  	{
   952  		// At this point the bug should be marked as fixed by the commit
   953  		// because the namespace has FixBisectionAutoClose set.
   954  		dbBug, _, _ := c.loadBug(rep.ID)
   955  		c.expectEQ(dbBug.Commits, []string{"kernel: add a fix"})
   956  		c.expectEQ(dbBug.HeadReproLevel, ReproLevelNone)
   957  	}
   958  }
   959  
   960  func TestBisectCauseReproSyz(t *testing.T) {
   961  	c := NewCtx(t)
   962  	defer c.Close()
   963  
   964  	build := testBuild(1)
   965  	c.client2.UploadBuild(build)
   966  	crash := testCrashWithRepro(build, 1)
   967  	crash.ReproC = nil
   968  	c.client2.ReportCrash(crash)
   969  
   970  	pollResp := c.client2.pollJobs(build.Manager)
   971  	jobID := pollResp.ID
   972  	done := &dashapi.JobDoneReq{
   973  		ID:         jobID,
   974  		Build:      *build,
   975  		Log:        []byte("bisect log"),
   976  		CrashTitle: "bisect crash title",
   977  		CrashLog:   []byte("bisect crash log"),
   978  	}
   979  	done.Build.ID = jobID
   980  	c.expectOK(c.client2.JobDone(done))
   981  
   982  	crash.ReproC = []byte("int main")
   983  	c.client2.ReportCrash(crash)
   984  
   985  	msg := c.client2.pollEmailBug()
   986  	if !strings.Contains(msg.Body, "syzbot found the following issue") {
   987  		t.Fatalf("wrong email header:\n%v", msg.Body)
   988  	}
   989  	if !strings.Contains(msg.Body, "Bisection is inconclusive") {
   990  		t.Fatalf("report does not contain bisection results:\n%v", msg.Body)
   991  	}
   992  }
   993  
   994  func TestBisectCauseReproSyz2(t *testing.T) {
   995  	c := NewCtx(t)
   996  	defer c.Close()
   997  
   998  	build := testBuild(1)
   999  	c.client2.UploadBuild(build)
  1000  	crash := testCrashWithRepro(build, 1)
  1001  	crash.ReproC = nil
  1002  	c.client2.ReportCrash(crash)
  1003  
  1004  	pollResp := c.client2.pollJobs(build.Manager)
  1005  	jobID := pollResp.ID
  1006  	done := &dashapi.JobDoneReq{
  1007  		ID:         jobID,
  1008  		Build:      *build,
  1009  		Log:        []byte("bisect log"),
  1010  		CrashTitle: "bisect crash title",
  1011  		CrashLog:   []byte("bisect crash log"),
  1012  	}
  1013  	done.Build.ID = jobID
  1014  	c.expectOK(c.client2.JobDone(done))
  1015  
  1016  	msg := c.client2.pollEmailBug()
  1017  	if !strings.Contains(msg.Body, "syzbot found the following issue") {
  1018  		t.Fatalf("wrong email header:\n%v", msg.Body)
  1019  	}
  1020  	if !strings.Contains(msg.Body, "Bisection is inconclusive") {
  1021  		t.Fatalf("report does not contain bisection results:\n%v", msg.Body)
  1022  	}
  1023  
  1024  	crash.ReproC = []byte("int main")
  1025  	c.client2.ReportCrash(crash)
  1026  
  1027  	msg = c.client2.pollEmailBug()
  1028  	if !strings.Contains(msg.Body, "syzbot has found a reproducer for the following issue") {
  1029  		t.Fatalf("wrong email header:\n%v", msg.Body)
  1030  	}
  1031  	// Do we need bisection results in this email as well?
  1032  	// We already mailed them, so we could not mail them here.
  1033  	// But if we don't include bisection results, need to check that CC is correct
  1034  	// (includes bisection CC).
  1035  	if !strings.Contains(msg.Body, "Bisection is inconclusive") {
  1036  		t.Fatalf("report still contains bisection results:\n%v", msg.Body)
  1037  	}
  1038  }
  1039  
  1040  // Test that bisection results show up on UI.
  1041  func TestBugBisectionResults(t *testing.T) {
  1042  	c := NewCtx(t)
  1043  	defer c.Close()
  1044  
  1045  	build, _ := addBuildAndCrash(c)
  1046  	_, bugKey := c.loadSingleBug()
  1047  
  1048  	addBisectCauseJob(c, build)
  1049  	addBisectFixJob(c, build)
  1050  
  1051  	// Ensure expected results show up on web UI
  1052  	url := fmt.Sprintf("/bug?id=%v", bugKey.StringID())
  1053  	content, err := c.GET(url)
  1054  	c.expectEQ(err, nil)
  1055  	c.expectTrue(bytes.Contains(content, []byte("Cause bisection: introduced by")))
  1056  	c.expectTrue(bytes.Contains(content, []byte("kernel: add a bug")))
  1057  	c.expectTrue(bytes.Contains(content, []byte("Fix bisection: fixed by")))
  1058  	c.expectTrue(bytes.Contains(content, []byte("kernel: add a fix")))
  1059  }
  1060  
  1061  // Test that bisection status shows up on main page.
  1062  func TestBugBisectionStatus(t *testing.T) {
  1063  	c := NewCtx(t)
  1064  	defer c.Close()
  1065  
  1066  	// Upload a crash report.
  1067  	build, _ := addBuildAndCrash(c)
  1068  
  1069  	addBisectCauseJob(c, build)
  1070  
  1071  	// Fetch bug, namespace details.
  1072  	var bugs []*Bug
  1073  	_, err := db.NewQuery("Bug").GetAll(c.ctx, &bugs)
  1074  	c.expectEQ(err, nil)
  1075  	c.expectEQ(len(bugs), 1)
  1076  	url := fmt.Sprintf("/%v", bugs[0].Namespace)
  1077  	content, err := c.GET(url)
  1078  	c.expectEQ(err, nil)
  1079  	c.expectTrue(bytes.Contains(content, []byte("done")))
  1080  
  1081  	addBisectFixJob(c, build)
  1082  
  1083  	content, err = c.GET(url)
  1084  	c.expectEQ(err, nil)
  1085  	c.expectTrue(bytes.Contains(content, []byte("done")))
  1086  }
  1087  
  1088  // Test that invalidated bisections are not shown in the UI and marked as invalid.
  1089  func TestBugBisectionInvalidation(t *testing.T) {
  1090  	c := NewCtx(t)
  1091  	defer c.Close()
  1092  
  1093  	build, _ := addBuildAndCrash(c)
  1094  	// Receive the JobBisectCause and send cause information.
  1095  	addBisectCauseJob(c, build)
  1096  
  1097  	// Ensure expected results show up on web UI
  1098  	bug, bugKey := c.loadSingleBug()
  1099  	job, jobKey := c.loadSingleJob()
  1100  	bugURL := fmt.Sprintf("/bug?id=%v", bugKey.StringID())
  1101  	content, err := c.GET(bugURL)
  1102  	c.expectEQ(err, nil)
  1103  	c.expectEQ(bug.BisectCause, BisectYes)
  1104  	c.expectTrue(bytes.Contains(content, []byte("Cause bisection: introduced by")))
  1105  	c.expectTrue(bytes.Contains(content, []byte("kernel: add a bug")))
  1106  	c.expectEQ(job.InvalidatedBy, "")
  1107  
  1108  	// Mark bisection as invalid, but do not restart it.
  1109  	_, err = c.AuthGET(AccessAdmin, "/admin?action=invalidate_bisection&key="+jobKey.Encode())
  1110  	var httpErr *HTTPError
  1111  	c.expectTrue(errors.As(err, &httpErr))
  1112  	c.expectEQ(httpErr.Code, http.StatusFound)
  1113  
  1114  	// The invalidated bisection should have vanished from the web UI
  1115  	job, _ = c.loadSingleJob()
  1116  	content, err = c.GET(bugURL)
  1117  	c.expectEQ(err, nil)
  1118  	c.expectTrue(!bytes.Contains(content, []byte("Cause bisection: introduced by")))
  1119  	c.expectTrue(!bytes.Contains(content, []byte("kernel: add a bug")))
  1120  	c.expectEQ(job.InvalidatedBy, "user@syzkaller.com")
  1121  
  1122  	// Wait 30 days, no new cause bisection jobs should be created.
  1123  	c.advanceTime(24 * 30 * time.Hour)
  1124  	resp := c.client2.pollSpecificJobs(build.Manager, dashapi.ManagerJobs{
  1125  		BisectCause: true,
  1126  	})
  1127  	c.expectEQ(resp.ID, "")
  1128  
  1129  	// Invalidate the bisection once more (why not), but this time ask dashboard to redo it.
  1130  	_, err = c.AuthGET(AccessAdmin, "/admin?action=invalidate_bisection&key="+jobKey.Encode()+"&restart=1")
  1131  	c.expectTrue(errors.As(err, &httpErr))
  1132  	c.expectEQ(httpErr.Code, http.StatusFound)
  1133  	bug, _ = c.loadSingleBug()
  1134  	c.expectEQ(bug.BisectCause, BisectNot)
  1135  
  1136  	// The bisection should be started again.
  1137  	c.advanceTime(time.Hour)
  1138  	resp = c.client2.pollJobs(build.Manager)
  1139  	c.client2.expectNE(resp.ID, "")
  1140  	c.client2.expectEQ(resp.Type, dashapi.JobBisectCause)
  1141  }
  1142  
  1143  // Upload a build, a crash report and poll bug emails.
  1144  func addBuildAndCrash(c *Ctx) (*dashapi.Build, *dashapi.Crash) {
  1145  	build := testBuild(1)
  1146  	c.client2.UploadBuild(build)
  1147  	crash := testCrashWithRepro(build, 1)
  1148  	c.client2.ReportCrash(crash)
  1149  	c.client2.pollEmailBug()
  1150  
  1151  	c.advanceTime(30 * 24 * time.Hour)
  1152  	msg := c.client2.pollEmailBug()
  1153  	c.expectTrue(strings.Contains(msg.Body, "Sending this report to the next reporting stage."))
  1154  	msg = c.client2.pollEmailBug()
  1155  	c.expectTrue(strings.Contains(msg.Body, "syzbot found the following issue"))
  1156  
  1157  	return build, crash
  1158  }
  1159  
  1160  // Poll a JobBisectCause and send cause information.
  1161  func addBisectCauseJob(c *Ctx, build *dashapi.Build) (*dashapi.JobPollResp, *dashapi.JobDoneReq, string) {
  1162  	resp := c.client2.pollJobs(build.Manager)
  1163  	c.client2.expectNE(resp.ID, "")
  1164  	c.client2.expectEQ(resp.Type, dashapi.JobBisectCause)
  1165  	jobID := resp.ID
  1166  	done := &dashapi.JobDoneReq{
  1167  		ID:          jobID,
  1168  		Build:       *build,
  1169  		Log:         []byte("bisectfix log 4"),
  1170  		CrashTitle:  "bisectfix crash title 4",
  1171  		CrashLog:    []byte("bisectfix crash log 4"),
  1172  		CrashReport: []byte("bisectfix crash report 4"),
  1173  		Commits: []dashapi.Commit{
  1174  			{
  1175  				Hash:       "36e65cb4a0448942ec316b24d60446bbd5cc7827",
  1176  				Title:      "kernel: add a bug",
  1177  				Author:     "author@kernel.org",
  1178  				AuthorName: "Author Kernelov",
  1179  				CC: []string{
  1180  					"reviewer1@kernel.org", "\"Reviewer2\" <reviewer2@kernel.org>",
  1181  					// These must be filtered out:
  1182  					"syzbot@testapp.appspotmail.com",
  1183  					"syzbot+1234@testapp.appspotmail.com",
  1184  					"\"syzbot\" <syzbot+1234@testapp.appspotmail.com>",
  1185  				},
  1186  				Date: time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
  1187  			},
  1188  		},
  1189  	}
  1190  	c.expectOK(c.client2.JobDone(done))
  1191  
  1192  	c.advanceTime(24 * time.Hour)
  1193  	msg := c.client2.pollEmailBug()
  1194  	c.expectTrue(strings.Contains(msg.Body, "syzbot has bisected this issue to:"))
  1195  
  1196  	return resp, done, jobID
  1197  }
  1198  
  1199  // Poll a JobBisectfix and send fix information.
  1200  func addBisectFixJob(c *Ctx, build *dashapi.Build) (*dashapi.JobPollResp, *dashapi.JobDoneReq, string) {
  1201  	resp := c.client2.pollJobs(build.Manager)
  1202  	c.client2.expectNE(resp.ID, "")
  1203  	c.client2.expectEQ(resp.Type, dashapi.JobBisectFix)
  1204  	jobID := resp.ID
  1205  	done := &dashapi.JobDoneReq{
  1206  		ID:          jobID,
  1207  		Build:       *build,
  1208  		Log:         []byte("bisectfix log 4"),
  1209  		CrashTitle:  "bisectfix crash title 4",
  1210  		CrashLog:    []byte("bisectfix crash log 4"),
  1211  		CrashReport: []byte("bisectfix crash report 4"),
  1212  		Commits: []dashapi.Commit{
  1213  			{
  1214  				Hash:       "46e65cb4a0448942ec316b24d60446bbd5cc7827",
  1215  				Title:      "kernel: add a fix",
  1216  				Author:     "author@kernel.org",
  1217  				AuthorName: "Author Kernelov",
  1218  				CC: []string{
  1219  					"reviewer1@kernel.org", "\"Reviewer2\" <reviewer2@kernel.org>",
  1220  					// These must be filtered out:
  1221  					"syzbot@testapp.appspotmail.com",
  1222  					"syzbot+1234@testapp.appspotmail.com",
  1223  					"\"syzbot\" <syzbot+1234@testapp.appspotmail.com>",
  1224  				},
  1225  				Date: time.Date(2000, 2, 9, 4, 5, 6, 7, time.UTC),
  1226  			},
  1227  		},
  1228  	}
  1229  	c.expectOK(c.client2.JobDone(done))
  1230  	msg := c.client2.pollEmailBug()
  1231  	c.expectTrue(strings.Contains(msg.Body, "syzbot suspects this issue was fixed by commit:"))
  1232  
  1233  	// Ensure we do not automatically close the bug.
  1234  	c.expectTrue(!c.config().Namespaces["test2"].FixBisectionAutoClose)
  1235  	_, extBugID, err := email.RemoveAddrContext(msg.Sender)
  1236  	c.expectOK(err)
  1237  	dbBug, _, _ := c.loadBug(extBugID)
  1238  	c.expectTrue(len(dbBug.Commits) == 0)
  1239  	return resp, done, jobID
  1240  }