diff --git a/pkg/bisect/bisect.go b/pkg/bisect/bisect.go index 1abd65e6b2cc..73a1971c8177 100644 --- a/pkg/bisect/bisect.go +++ b/pkg/bisect/bisect.go @@ -992,6 +992,11 @@ func mostFrequentReports(reports []*report.Report) (*report.Report, []crash.Type // bisecting this kind of a bug. continue } + if info.t == crash.LostConnection && len(perType) > 1 { + // This crash type is much more often unrelated than not. + // Take it only if it's the only crash type. + continue + } // Take further crash types until we have considered 2/3 of all crashes, but // no more than 3. needTaken := (crashes + 2) * 2 / 3 @@ -1009,12 +1014,24 @@ func mostFrequentReports(reports []*report.Report) (*report.Report, []crash.Type func (env *env) isTransientError(rep *report.Report) bool { // If we're not chasing a SYZFATAL error, ignore them. // Otherwise it indicates some transient problem of the tested kernel revision. - hadSyzFailure := false - for _, t := range env.reportTypes { - hadSyzFailure = hadSyzFailure || t == crash.SyzFailure + if rep.Type == crash.SyzFailure { + hadSyzFailure := false + for _, t := range env.reportTypes { + hadSyzFailure = hadSyzFailure || t == crash.SyzFailure + } + return len(env.reportTypes) > 0 && !hadSyzFailure + } + // Lost connection is a frequent source of flaky results. + // Ignore if it is was not in the canonical crash types set. + if rep.Type == crash.LostConnection { + hadLostConnection := false + for _, t := range env.reportTypes { + hadLostConnection = hadLostConnection || t == crash.LostConnection + } + return len(env.reportTypes) > 0 && !hadLostConnection } - return rep.Type == crash.SyzFailure && - len(env.reportTypes) > 0 && !hadSyzFailure + // All other errors are okay. + return false } func (env *env) saveDebugFile(hash string, idx int, data []byte) { diff --git a/pkg/bisect/bisect_test.go b/pkg/bisect/bisect_test.go index 5160cffedaa1..7d251c9e7709 100644 --- a/pkg/bisect/bisect_test.go +++ b/pkg/bisect/bisect_test.go @@ -116,18 +116,27 @@ func (env *testEnv) Test(numVMs int, reproSyz, reproOpts, reproC []byte) ([]inst } return ret, nil } - ret = make([]instance.EnvTestResult, numVMs-1) + ret = make([]instance.EnvTestResult, numVMs) if env.test.injectSyzFailure { - ret = append(ret, instance.EnvTestResult{ - Error: &instance.TestError{ + ret[0] = instance.EnvTestResult{ + Error: &instance.CrashError{ Report: &report.Report{ Title: "SYZFATAL: test", Type: crash.SyzFailure, }, }, - }) - } else { - ret = append(ret, instance.EnvTestResult{}) + } + } else if env.test.injectLostConnection { + for i := 0; i < numVMs/3; i++ { + ret[i] = instance.EnvTestResult{ + Error: &instance.CrashError{ + Report: &report.Report{ + Title: "lost connection to test machine", + Type: crash.LostConnection, + }, + }, + } + } } return ret, nil } @@ -312,11 +321,12 @@ type BisectionTest struct { expectErr bool expectErrType any // Expect res.Report != nil. - expectRep bool - noopChange bool - isRelease bool - flaky bool - injectSyzFailure bool + expectRep bool + noopChange bool + isRelease bool + flaky bool + injectSyzFailure bool + injectLostConnection bool // Expected number of returned commits for inconclusive bisection. commitLen int // For cause bisection: Oldest commit returned by bisection. @@ -496,6 +506,16 @@ var bisectionTests = []BisectionTest{ fixCommit: "500", isRelease: true, }, + // Tests that bisection returns the correct fix commit despite `lost connection to test machine`. + { + name: "fix-finds-fix-despite-lost-connection", + fix: true, + startCommit: 400, + injectLostConnection: true, + commitLen: 1, + fixCommit: "500", + isRelease: true, + }, // Tests that bisection returns the correct fix commit in case of SYZFATAL. { name: "fix-finds-fix-for-syzfatal", @@ -958,6 +978,34 @@ func TestMostFrequentReport(t *testing.T) { report: "A", other: true, }, + { + name: "do not take lost connection", + reports: []*report.Report{ + {Title: "A", Type: crash.LostConnection}, + {Title: "B", Type: crash.Warning}, + {Title: "C", Type: crash.LostConnection}, + {Title: "D", Type: crash.Warning}, + {Title: "E", Type: crash.LostConnection}, + {Title: "F", Type: crash.Warning}, + }, + types: []crash.Type{crash.Warning}, + report: "B", + other: true, + }, + { + name: "only lost connection", + reports: []*report.Report{ + {Title: "A", Type: crash.LostConnection}, + {Title: "B", Type: crash.LostConnection}, + {Title: "C", Type: crash.LostConnection}, + {Title: "D", Type: crash.LostConnection}, + {Title: "E", Type: crash.LostConnection}, + {Title: "F", Type: crash.LostConnection}, + }, + types: []crash.Type{crash.LostConnection}, + report: "A", + other: false, + }, } for _, test := range tests { test := test diff --git a/pkg/report/crash/types.go b/pkg/report/crash/types.go index 9a5be91361f6..6de004274af2 100644 --- a/pkg/report/crash/types.go +++ b/pkg/report/crash/types.go @@ -19,6 +19,7 @@ const ( AtomicSleep = Type("ATOMIC_SLEEP") KMSAN = Type("KMSAN") SyzFailure = Type("SYZ_FAILURE") + LostConnection = Type("LOST_CONNECTION") ) func (t Type) String() string { diff --git a/vm/vm.go b/vm/vm.go index 91398167d384..7d580e8378c0 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -24,6 +24,7 @@ import ( "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/report" + "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/pkg/stat" "github.com/google/syzkaller/sys/targets" "github.com/google/syzkaller/vm/dispatcher" @@ -498,10 +499,15 @@ func (mon *monitor) createReport(defaultError string) *report.Report { if defaultError == "" { return nil } + typ := crash.UnknownType + if defaultError == lostConnectionCrash { + typ = crash.LostConnection + } return &report.Report{ Title: defaultError, Output: mon.output, Suppressed: report.IsSuppressed(mon.reporter, mon.output), + Type: typ, } } start := rep.StartPos - mon.beforeContext diff --git a/vm/vm_test.go b/vm/vm_test.go index bede53fb7f87..434446083649 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -11,6 +11,7 @@ import ( "github.com/google/syzkaller/pkg/mgrconfig" "github.com/google/syzkaller/pkg/report" + "github.com/google/syzkaller/pkg/report/crash" "github.com/google/syzkaller/sys/targets" "github.com/google/syzkaller/vm/vmimpl" ) @@ -115,6 +116,7 @@ var tests = []*Test{ }, Report: &report.Report{ Title: lostConnectionCrash, + Type: crash.LostConnection, }, }, { @@ -135,6 +137,7 @@ var tests = []*Test{ Output: []byte( "DIAGNOSE\n", ), + Type: crash.LostConnection, }, }, { @@ -150,6 +153,7 @@ var tests = []*Test{ "VM DIAGNOSIS:\n" + "DIAGNOSE\n", ), + Type: crash.LostConnection, }, }, { @@ -237,6 +241,7 @@ var tests = []*Test{ }, Report: &report.Report{ Title: lostConnectionCrash, + Type: crash.LostConnection, }, }, { @@ -417,6 +422,9 @@ func testMonitorExecution(t *testing.T, test *Test) { if test.Report.Output != nil && !bytes.Equal(test.Report.Output, rep.Output) { t.Fatalf("want output:\n%s\n\ngot output:\n%s", test.Report.Output, rep.Output) } + if test.Report.Type != rep.Type { + t.Fatalf("want type %q, got type %q", test.Report.Type, rep.Type) + } } func TestVMType(t *testing.T) {