diff options
| author | Andrey Konovalov <andreyknvl@google.com> | 2017-11-21 16:45:08 +0100 |
|---|---|---|
| committer | Dmitry Vyukov <dvyukov@google.com> | 2017-11-22 13:17:21 +0100 |
| commit | 53a23f2a379f1d6982bef00164556d2dff7a3229 (patch) | |
| tree | d0f370ab3387a4bee70861f7f2ccc078ce7c80fc /pkg | |
| parent | 8c7cd9275d42c9dd3cf5d93c1a9fb01b99f88db8 (diff) | |
pkg/report: corrupted report detection fixes
Diffstat (limited to 'pkg')
| -rw-r--r-- | pkg/report/freebsd.go | 4 | ||||
| -rw-r--r-- | pkg/report/linux.go | 151 | ||||
| -rw-r--r-- | pkg/report/linux_test.go | 8 | ||||
| -rw-r--r-- | pkg/report/report.go | 13 |
4 files changed, 116 insertions, 60 deletions
diff --git a/pkg/report/freebsd.go b/pkg/report/freebsd.go index 544ee68c0..3e1113114 100644 --- a/pkg/report/freebsd.go +++ b/pkg/report/freebsd.go @@ -70,7 +70,7 @@ func (ctx *freebsd) Parse(output []byte) *Report { if oops == nil { return nil } - rep.Title = extractDescription(output[rep.StartPos:], oops) + rep.Title, rep.Corrupted = extractDescription(output[rep.StartPos:], oops) return rep } @@ -101,6 +101,7 @@ var freebsdOopses = []*oops{ "trap_pfault|trap|calltrap|m_copydata|__rw_wlock_hard)" + "\\+{{ADDR}}\\r?\\n)*#[0-9]+ {{ADDR}} at {{FUNC}}{{ADDR}}"), "Fatal trap %[1]v in %[2]v", + false, }, }, []*regexp.Regexp{}, @@ -111,6 +112,7 @@ var freebsdOopses = []*oops{ { compile("panic: ffs_write: type {{ADDR}} [0-9]+ \\([0-9]+,[0-9]+\\)"), "panic: ffs_write: type ADDR X (Y,Z)", + false, }, }, []*regexp.Regexp{}, diff --git a/pkg/report/linux.go b/pkg/report/linux.go index cb8a361a7..a1489a553 100644 --- a/pkg/report/linux.go +++ b/pkg/report/linux.go @@ -150,7 +150,10 @@ func (ctx *linux) Parse(output []byte) *Report { if oops == nil { return nil } - rep.Title = extractDescription(output[rep.StartPos:], oops) + rep.Title, rep.Corrupted = extractDescription(output[rep.StartPos:], oops) + if !rep.Corrupted { + rep.Corrupted = ctx.isCorrupted(rep.Title, rep.Report) + } // Executor PIDs are not interesting. rep.Title = executorRe.ReplaceAllLiteralString(rep.Title, "syz-executor") // Replace that everything looks like an address with "ADDR", @@ -165,7 +168,6 @@ func (ctx *linux) Parse(output []byte) *Report { rep.Title = funcRe.ReplaceAllString(rep.Title, "$1") // CPU numbers are not interesting. rep.Title = cpuRe.ReplaceAllLiteralString(rep.Title, "CPU") - rep.Corrupted = ctx.isCorrupted(rep.Title, rep.Report) return rep } @@ -345,44 +347,6 @@ func (ctx *linux) isCorrupted(title string, report []byte) bool { return true } } - corruptedDescStrings := []string{ - // Sometimes with such BUG failures, the second part of the header doesn't get printed - // or gets corrupted, because kernel prints it as two separate printk() calls. - "BUG: unable to handle kernel", - // If we failed to extract function name where the fault happened, - // the report is most likely truncated. - "general protection fault", - "BUG: bad unlock balance", - "divide error", - "invalid opcode", - } - for _, s := range corruptedDescStrings { - if strings.TrimSpace(title) == s { - return true - } - } - corruptedTextRegExps := []*regexp.Regexp{ - // If report contains 'printk messages dropped' it is most likely corrupted. - regexp.MustCompile(`printk messages dropped`), - } - for _, re := range corruptedTextRegExps { - if re.Match(report) { - return true - } - } - for _, crash := range []string{"BUG", "WARNING", "INFO", "KASAN", "KMSAN", "UBSAN"} { - // If description contains 'BUG', 'WARNING', etc, report must also contain it. - if strings.Contains(title, crash) && !bytes.Contains(report, []byte(crash)) { - return true - } - } - if strings.HasPrefix(title, "possible deadlock") { - // For 'possible deadlock' reports lets use 'unsafe locking scenario' - // string in report as a signal whether the report got truncated. - if !bytes.Contains(report, []byte("unsafe locking scenario")) { - return true - } - } if strings.HasPrefix(title, "KASAN") { // For KASAN reports lets use 'Allocated' and 'Freed' as signals. if !bytes.Contains(report, []byte("Allocated")) { @@ -422,17 +386,8 @@ var ( ) var linuxCorruptedTitles = []*regexp.Regexp{ - // 'kernel panic: Fatal exception' is usually printed after BUG, - // so if we captured it as a report description, that means the - // report got truncated and we missed the actual BUG header. - regexp.MustCompile(`kernel panic: Fatal exception`), - // Same, but for WARNINGs and KASAN reports. - regexp.MustCompile(`kernel panic: panic_on_warn set`), // Sometimes timestamps get merged into the middle of report description. regexp.MustCompile(`\[ *[0-9]+\.[0-9]+\]`), - regexp.MustCompile(`\[ *[0-9]+\.NUM\]`), - regexp.MustCompile(`\[ *NUM\.NUM\]`), - regexp.MustCompile(`\[ *NUM\.[0-9]+\]`), } var linuxOopses = []*oops{ @@ -442,78 +397,106 @@ var linuxOopses = []*oops{ { compile("BUG: KASAN: ([a-z\\-]+) in {{FUNC}}(?:.*\\n)+?.*(Read|Write) of size ([0-9]+)"), "KASAN: %[1]v %[3]v in %[2]v", + false, }, { compile("BUG: KASAN: ([a-z\\-]+) on address(?:.*\\n)+?.*(Read|Write) of size ([0-9]+)"), "KASAN: %[1]v %[2]v", + false, }, { compile("BUG: KASAN: (.*)"), "KASAN: %[1]v", + false, }, { compile("BUG: unable to handle kernel paging request(?:.*\\n)+?.*IP: (?:{{PC}} +)?{{FUNC}}"), "BUG: unable to handle kernel paging request in %[1]v", + false, }, { compile("BUG: unable to handle kernel paging request"), "BUG: unable to handle kernel paging request", + false, }, { compile("BUG: unable to handle kernel NULL pointer dereference(?:.*\\n)+?.*IP: (?:{{PC}} +)?{{FUNC}}"), "BUG: unable to handle kernel NULL pointer dereference in %[1]v", + false, + }, + { + // Sometimes with such BUG failures, the second part of the header doesn't get printed + // or gets corrupted, because kernel prints it as two separate printk() calls. + compile("BUG: unable to handle kernel"), + "BUG: unable to handle kernel", + true, }, { compile("BUG: spinlock (lockup suspected|already unlocked|recursion|bad magic|wrong owner|wrong CPU)"), "BUG: spinlock %[1]v", + false, }, { compile("BUG: soft lockup"), "BUG: soft lockup", + false, }, { compile("BUG: .*still has locks held!(?:.*\\n)+?.*{{PC}} +{{FUNC}}"), "BUG: still has locks held in %[1]v", + false, }, { compile("BUG: bad unlock balance detected!(?:.*\\n)+?.*{{PC}} +{{FUNC}}"), "BUG: bad unlock balance in %[1]v", + false, }, { + // If we failed to extract function name where the fault happened, + // the report is most likely truncated. compile("BUG: bad unlock balance detected!"), "BUG: bad unlock balance", + true, }, { compile("BUG: held lock freed!(?:.*\\n)+?.*{{PC}} +{{FUNC}}"), "BUG: held lock freed in %[1]v", + false, }, { compile("BUG: Bad rss-counter state"), "BUG: Bad rss-counter state", + false, }, { compile("BUG: non-zero nr_ptes on freeing mm"), "BUG: non-zero nr_ptes on freeing mm", + false, }, { compile("BUG: non-zero nr_pmds on freeing mm"), "BUG: non-zero nr_pmds on freeing mm", + false, }, { compile("BUG: Dentry .* still in use \\([0-9]+\\) \\[unmount of ([^\\]]+)\\]"), "BUG: Dentry still in use [unmount of %[1]v]", + false, }, { compile("BUG: Bad page state.*"), "BUG: Bad page state", + false, }, { compile("BUG: spinlock bad magic.*"), "BUG: spinlock bad magic", + false, }, { compile("BUG: workqueue lockup.*"), "BUG: workqueue lockup", + false, }, }, []*regexp.Regexp{ @@ -527,54 +510,67 @@ var linuxOopses = []*oops{ { compile("WARNING: .* at {{SRC}} {{FUNC}}"), "WARNING in %[2]v", + false, }, { compile("WARNING: possible circular locking dependency detected(?:.*\\n)+?.*is trying to acquire lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("WARNING: possible circular locking dependency detected"), "possible deadlock", + false, }, { compile("WARNING: possible irq lock inversion dependency detected(?:.*\\n)+?.*just changed the state of lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("WARNING: possible irq lock inversion dependency detected"), "possible deadlock", + false, }, { compile("WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected(?:.*\\n)+?.*is trying to acquire(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("WARNING: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected"), "possible deadlock", + false, }, { compile("WARNING: possible recursive locking detected(?:.*\\n)+?.*is trying to acquire lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("WARNING: possible recursive locking detected"), "possible deadlock", + false, }, { compile("WARNING: inconsistent lock state(?:.*\\n)+?.*takes(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "inconsistent lock state in %[1]v", + false, }, { compile("WARNING: suspicious RCU usage(?:.*\n)+?.*?{{SRC}}"), "suspicious RCU usage at %[1]v", + false, }, { compile("WARNING: kernel stack regs at [0-9a-f]+ in [^ ]* has bad '([^']+)' value"), "WARNING: kernel stack regs has bad '%[1]v' value", + false, }, { compile("WARNING: kernel stack frame pointer at [0-9a-f]+ in [^ ]* has bad value"), "WARNING: kernel stack frame pointer has bad value", + false, }, }, []*regexp.Regexp{ @@ -587,82 +583,102 @@ var linuxOopses = []*oops{ { compile("INFO: possible circular locking dependency detected \\](?:.*\\n)+?.*is trying to acquire lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("INFO: possible circular locking dependency detected"), "possible deadlock", + false, }, { compile("INFO: possible irq lock inversion dependency detected \\](?:.*\\n)+?.*just changed the state of lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("INFO: possible irq lock inversion dependency detected"), "possible deadlock", + false, }, { compile("INFO: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected \\](?:.*\\n)+?.*is trying to acquire(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("INFO: SOFTIRQ-safe -> SOFTIRQ-unsafe lock order detected"), "possible deadlock", + false, }, { compile("INFO: possible recursive locking detected \\](?:.*\\n)+?.*is trying to acquire lock(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "possible deadlock in %[1]v", + false, }, { compile("INFO: possible recursive locking detected"), "possible deadlock", + false, }, { compile("INFO: inconsistent lock state \\](?:.*\\n)+?.*takes(?:.*\\n)+?.*at: {{PC}} +{{FUNC}}"), "inconsistent lock state in %[1]v", + false, }, { compile("INFO: rcu_preempt detected stalls(?:.*\\n)+?.*</IRQ>.*\n(?:.* \\? .*\\n)+?(?:.*rcu.*\\n)+?.*\\] {{FUNC}}"), "INFO: rcu detected stall in %[1]v", + false, }, { compile("INFO: rcu_preempt detected stalls"), "INFO: rcu detected stall", + false, }, { compile("INFO: rcu_sched detected(?: expedited)? stalls(?:.*\\n)+?.*</IRQ>.*\n(?:.* \\? .*\\n)+?(?:.*rcu.*\\n)+?.*\\] {{FUNC}}"), "INFO: rcu detected stall in %[1]v", + false, }, { compile("INFO: rcu_sched detected(?: expedited)? stalls"), "INFO: rcu detected stall", + false, }, { compile("INFO: rcu_preempt self-detected stall on CPU(?:.*\\n)+?.*</IRQ>.*\n(?:.* \\? .*\\n)+?(?:.*rcu.*\\n)+?.*\\] {{FUNC}}"), "INFO: rcu detected stall in %[1]v", + false, }, { compile("INFO: rcu_preempt self-detected stall on CPU"), "INFO: rcu detected stall", + false, }, { compile("INFO: rcu_sched self-detected stall on CPU(?:.*\\n)+?.*</IRQ>.*\n(?:.* \\? .*\\n)+?(?:.*rcu.*\\n)+?.*\\] {{FUNC}}"), "INFO: rcu detected stall in %[1]v", + false, }, { compile("INFO: rcu_sched self-detected stall on CPU"), "INFO: rcu detected stall", + false, }, { compile("INFO: rcu_bh detected stalls on CPU"), "INFO: rcu detected stall", + false, }, { compile("INFO: suspicious RCU usage(?:.*\n)+?.*?{{SRC}}"), "suspicious RCU usage at %[1]v", + false, }, { compile("INFO: task .* blocked for more than [0-9]+ seconds"), "INFO: task hung", + false, }, }, []*regexp.Regexp{ @@ -678,6 +694,7 @@ var linuxOopses = []*oops{ { compile("Unable to handle kernel paging request(?:.*\\n)+?.*PC is at {{FUNC}}"), "unable to handle kernel paging request in %[1]v", + false, }, }, []*regexp.Regexp{}, @@ -688,14 +705,19 @@ var linuxOopses = []*oops{ { compile("general protection fault:(?:.*\\n)+?.*RIP: [0-9]+:{{PC}} +{{PC}} +{{FUNC}}"), "general protection fault in %[1]v", + false, }, { compile("general protection fault:(?:.*\\n)+?.*RIP: [0-9]+:{{FUNC}}"), "general protection fault in %[1]v", + false, }, { + // If we failed to extract function name where the fault happened, + // the report is most likely truncated. compile("general protection fault"), "general protection fault", + true, }, }, []*regexp.Regexp{}, @@ -706,14 +728,31 @@ var linuxOopses = []*oops{ { compile("Kernel panic - not syncing: Attempted to kill init!"), "kernel panic: Attempted to kill init!", + false, }, { compile("Kernel panic - not syncing: Couldn't open N_TTY ldisc for [^ ]+ --- error -[0-9]+"), "kernel panic: Couldn't open N_TTY ldisc", + false, + }, + { + // 'kernel panic: Fatal exception' is usually printed after BUG, + // so if we captured it as a report description, that means the + // report got truncated and we missed the actual BUG header. + compile("Kernel panic - not syncing: Fatal exception"), + "kernel panic: Fatal exception", + true, + }, + { + // Same, but for WARNINGs and KASAN reports. + compile("Kernel panic - not syncing: panic_on_warn set"), + "kernel panic: panic_on_warn set", + true, }, { compile("Kernel panic - not syncing: (.*)"), "kernel panic: %[1]v", + false, }, }, []*regexp.Regexp{}, @@ -724,6 +763,7 @@ var linuxOopses = []*oops{ { compile("kernel BUG (.*)"), "kernel BUG %[1]v", + false, }, }, []*regexp.Regexp{}, @@ -734,6 +774,7 @@ var linuxOopses = []*oops{ { compile("Kernel BUG (.*)"), "kernel BUG %[1]v", + false, }, }, []*regexp.Regexp{}, @@ -744,6 +785,7 @@ var linuxOopses = []*oops{ { compile("BUG kmalloc-.*: Object already free"), "BUG: Object already free", + false, }, }, []*regexp.Regexp{}, @@ -754,14 +796,19 @@ var linuxOopses = []*oops{ { compile("divide error: (?:.*\\n)+?.*RIP: [0-9]+:{{PC}} +{{PC}} +{{FUNC}}"), "divide error in %[1]v", + false, }, { compile("divide error: (?:.*\\n)+?.*RIP: [0-9]+:{{FUNC}}"), "divide error in %[1]v", + false, }, { + // If we failed to extract function name where the fault happened, + // the report is most likely truncated. compile("divide error"), "divide error", + true, }, }, []*regexp.Regexp{}, @@ -772,14 +819,19 @@ var linuxOopses = []*oops{ { compile("invalid opcode: (?:.*\\n)+?.*RIP: [0-9]+:{{PC}} +{{PC}} +{{FUNC}}"), "invalid opcode in %[1]v", + false, }, { compile("invalid opcode: (?:.*\\n)+?.*RIP: [0-9]+:{{FUNC}}"), "invalid opcode in %[1]v", + false, }, { + // If we failed to extract function name where the fault happened, + // the report is most likely truncated. compile("invalid opcode"), "invalid opcode", + true, }, }, []*regexp.Regexp{}, @@ -790,6 +842,7 @@ var linuxOopses = []*oops{ { compile("unreferenced object {{ADDR}} \\(size ([0-9]+)\\):(?:.*\n.*)+backtrace:.*\n.*{{PC}}.*\n.*{{PC}}.*\n.*{{PC}} {{FUNC}}"), "memory leak in %[2]v (size %[1]v)", + false, }, }, []*regexp.Regexp{}, diff --git a/pkg/report/linux_test.go b/pkg/report/linux_test.go index ffc6d04b7..898911e94 100644 --- a/pkg/report/linux_test.go +++ b/pkg/report/linux_test.go @@ -556,7 +556,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0) }, { ` [ 616.344091] Kernel panic - not syncing: Fatal exception in interrupt -`, `kernel panic: Fatal exception in interrupt`, true, +`, `kernel panic: Fatal exception`, true, }, { ` [ 616.309156] divide error: 0000 [#1] SMP DEBUG_PAGEALLOC KASAN @@ -981,7 +981,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0) [ 238.187128] ? __internal_add_timer+0x275/0x2d0 [ 238.191766] kasan_end_report+0x50/0x50 [ 238.195711] kasan_report+0x144/0x340 -`, `kernel panic: panic_on_warn set ...`, true, +`, `kernel panic: panic_on_warn set`, true, }, { ` [ 308.130685] ====================================================== @@ -1001,7 +1001,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0) [ 1722.511384] PGD 5a25067 [ 1722.511384] P4D 5a25067 [ 1722.511384] PUD 0 -`, `BUG: unable to handle kernel `, true, +`, `BUG: unable to handle kernel`, true, }, { ` [ 1722.511384] kasan: CONFIG_KASAN_INLINE enabled @@ -1021,7 +1021,7 @@ r0 = ioctl$KVM_CREATE_VM(0xffffffffffffffff, 0xae01, 0x0) [ 153.634416] PGD a0ab067 PUD 21ffff067 PMD 80000000b3c001e3 [ 153.640483] Oops: 0011 [#1] SMP KASAN [ 153.644615] Modules linked in: -`, `BUG: unable to handle kernel [ 153.NUM] deprecated getsockopt IP_VLAN used by syz-executor!`, true, +`, `BUG: unable to handle kernel`, true, }, { ` [ 46.415093] syz2: link speed 10 Mbps diff --git a/pkg/report/report.go b/pkg/report/report.go index 403f7bc76..8a6146f2e 100644 --- a/pkg/report/report.go +++ b/pkg/report/report.go @@ -74,8 +74,9 @@ type oops struct { } type oopsFormat struct { - re *regexp.Regexp - fmt string + re *regexp.Regexp + fmt string + corrupted bool } func compile(re string) *regexp.Regexp { @@ -124,8 +125,7 @@ func matchOops(line []byte, oops *oops, ignores []*regexp.Regexp) int { return match } -func extractDescription(output []byte, oops *oops) string { - desc := "" +func extractDescription(output []byte, oops *oops) (desc string, corrupted bool) { startPos := -1 for _, format := range oops.formats { match := format.re.FindSubmatchIndex(output) @@ -141,8 +141,9 @@ func extractDescription(output []byte, oops *oops) string { args = append(args, string(output[match[i]:match[i+1]])) } desc = fmt.Sprintf(format.fmt, args...) + corrupted = format.corrupted } - if desc == "" { + if len(desc) == 0 { pos := bytes.Index(output, oops.header) if pos == -1 { panic("non matching oops") @@ -163,7 +164,7 @@ func extractDescription(output []byte, oops *oops) string { if len(desc) > maxDescLen { desc = desc[:maxDescLen] } - return desc + return } // replace replaces [start:end] in where with what, inplace. |
