aboutsummaryrefslogtreecommitdiffstats
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-rw-r--r--pkg/report/linux.go172
-rw-r--r--pkg/report/report.go100
-rw-r--r--pkg/report/testdata/linux/report/1961
-rw-r--r--pkg/report/testdata/linux/report/1971
-rw-r--r--pkg/report/testdata/linux/report/1983
-rw-r--r--pkg/report/testdata/linux/report/1993
-rw-r--r--pkg/report/testdata/linux/report/2003
7 files changed, 181 insertions, 102 deletions
diff --git a/pkg/report/linux.go b/pkg/report/linux.go
index 833828ee8..fcc390907 100644
--- a/pkg/report/linux.go
+++ b/pkg/report/linux.go
@@ -416,7 +416,8 @@ func (ctx *linux) isCorrupted(title string, report []byte, format oopsFormat) bo
return true
}
// Check if the report contains stack trace.
- if !format.noStackTrace && !bytes.Contains(report, []byte("Call Trace")) && !bytes.Contains(report, []byte("backtrace")) {
+ if !format.noStackTrace && !bytes.Contains(report, []byte("Call Trace")) &&
+ !bytes.Contains(report, []byte("backtrace")) {
return true
}
// Check for common title corruptions.
@@ -486,12 +487,12 @@ var linuxStackKeywords = []*regexp.Regexp{
regexp.MustCompile(`[^k] backtrace:`),
}
+var linuxRipFrame = compile("IP: (?:(?:[0-9]+:)?(?:{{PC}} +){0,2}{{FUNC}}|[0-9]+:0x[0-9a-f]+)")
+
var linuxStackParams = &stackParams{
stackStartRes: linuxStackKeywords,
frameRes: []*regexp.Regexp{
compile("^ +(?:{{PC}} )?{{FUNC}}"),
- compile("IP: (?:{{PC}} +)?{{FUNC}}"),
- compile("IP: [0-9]+:(?:{{PC}} +{{PC}} +)?{{FUNC}}"),
},
skipPatterns: []string{
"__sanitizer",
@@ -532,11 +533,20 @@ var linuxStackParams = &stackParams{
},
}
-var linuxWarningStackStart = []string{
- // In newer kernels WARNING traps and actual stack starts after invalid_op frame,
- // older kernels does not have that and just print stack.
- " invalid_op",
- "Call Trace:",
+func warningStackFmt(skip ...string) *stackFmt {
+ return &stackFmt{
+ // In newer kernels WARNING traps and actual stack starts after invalid_op frame,
+ // older kernels just print stack.
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ parseStackTrace,
+ },
+ parts2: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: skip,
+ }
}
var linuxOopses = []*oops{
@@ -547,7 +557,11 @@ var linuxOopses = []*oops{
title: compile("BUG: KASAN: ([a-z\\-]+) in {{FUNC}}(?:.*\\n)+?.*(Read|Write) of size (?:[0-9]+)"),
fmt: "KASAN: %[1]v %[3]v in %[4]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
+ parts: []*regexp.Regexp{
+ compile("BUG: KASAN: (?:[a-z\\-]+) in {{FUNC}}"),
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
},
},
{
@@ -566,12 +580,24 @@ var linuxOopses = []*oops{
{
title: compile("BUG: unable to handle kernel paging request"),
fmt: "BUG: unable to handle kernel paging request in %[1]v",
- stack: &stackFmt{},
+ stack: &stackFmt{
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ },
},
{
title: compile("BUG: unable to handle kernel NULL pointer dereference"),
fmt: "BUG: unable to handle kernel NULL pointer dereference in %[1]v",
- stack: &stackFmt{},
+ stack: &stackFmt{
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ },
},
{
// Sometimes with such BUG failures, the second part of the header doesn't get printed
@@ -584,15 +610,21 @@ var linuxOopses = []*oops{
title: compile("BUG: spinlock (lockup suspected|already unlocked|recursion|bad magic|wrong owner|wrong CPU)"),
fmt: "BUG: spinlock %[1]v in %[2]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
- skip: []string{"spin_"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: []string{"spin_"},
},
},
{
title: compile("BUG: soft lockup"),
fmt: "BUG: soft lockup in %[1]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
},
},
{
@@ -650,8 +682,11 @@ var linuxOopses = []*oops{
title: compile("BUG: using __this_cpu_([a-z_]+)\\(\\) in preemptible"),
fmt: "BUG: using __this_cpu_%[1]v() in preemptible code in %[2]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
- skip: []string{"dump_stack", "preemption", "preempt"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: []string{"dump_stack", "preemption", "preempt"},
},
},
{
@@ -663,7 +698,10 @@ var linuxOopses = []*oops{
title: compile("BUG: memory leak"),
fmt: "memory leak in %[1]v",
stack: &stackFmt{
- start: []string{"backtrace:"},
+ parts: []*regexp.Regexp{
+ compile("backtrace:"),
+ parseStackTrace,
+ },
skip: []string{"kmemleak", "kmalloc", "kmem", "slab",
"alloc", "create_object"},
},
@@ -682,59 +720,39 @@ var linuxOopses = []*oops{
{
title: compile("WARNING: .*lib/debugobjects\\.c.* debug_print_object"),
fmt: "WARNING: ODEBUG bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- // Skip all users of ODEBUG as well.
- skip: []string{"debug_", "rcu", "hrtimer_", "timer_",
- "work_", "percpu_", "kmem_", "slab_", "kfree"},
- },
+ // Skip all users of ODEBUG as well.
+ stack: warningStackFmt("debug_", "rcu", "hrtimer_", "timer_",
+ "work_", "percpu_", "kmem_", "slab_", "kfree"),
},
{
title: compile("WARNING: .*mm/usercopy\\.c.* usercopy_warn"),
fmt: "WARNING: bad usercopy in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- skip: []string{"usercopy", "__check"},
- },
+ stack: warningStackFmt("usercopy", "__check"),
},
{
title: compile("WARNING: .*lib/kobject\\.c.* kobject_"),
fmt: "WARNING: kobject bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- skip: []string{"kobject_"},
- },
+ stack: warningStackFmt("kobject_"),
},
{
title: compile("WARNING: .*fs/proc/generic\\.c.* proc_register"),
fmt: "WARNING: proc registration bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- skip: []string{"proc_"},
- },
+ stack: warningStackFmt("proc_"),
},
{
title: compile("WARNING: .*lib/refcount\\.c.* refcount_"),
fmt: "WARNING: refcount bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- skip: []string{"refcount"},
- },
+ stack: warningStackFmt("refcount"),
},
{
title: compile("WARNING: .*kernel/locking/lockdep\\.c.*lock_"),
fmt: "WARNING: locking bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- },
+ stack: warningStackFmt(),
},
{
title: compile("WARNING: .*mm/slab_common\\.c.* kmalloc_slab"),
fmt: "WARNING: kmalloc bug in %[1]v",
- stack: &stackFmt{
- start: linuxWarningStackStart,
- skip: []string{"kmalloc", "slab"},
- },
+ stack: warningStackFmt("kmalloc", "slab"),
},
{
title: compile("WARNING: .* at {{SRC}} {{FUNC}}"),
@@ -770,8 +788,11 @@ var linuxOopses = []*oops{
report: compile("WARNING: suspicious RCU usage(?:.*\n)+?.*?{{SRC}}"),
fmt: "WARNING: suspicious RCU usage in %[2]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
- skip: []string{"rcu"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: []string{"rcu"},
},
},
{
@@ -831,24 +852,33 @@ var linuxOopses = []*oops{
title: compile("INFO: rcu_(?:preempt|sched|bh) detected(?: expedited)? stall"),
fmt: "INFO: rcu detected stall in %[1]v",
stack: &stackFmt{
- start: []string{" apic_timer_interrupt"},
- skip: []string{"apic_timer_interrupt", "rcu"},
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ parseStackTrace,
+ },
+ skip: []string{"apic_timer_interrupt", "rcu"},
},
},
{
title: compile("INFO: rcu_(?:preempt|sched|bh) self-detected stall"),
fmt: "INFO: rcu detected stall in %[1]v",
stack: &stackFmt{
- start: []string{" apic_timer_interrupt"},
- skip: []string{"apic_timer_interrupt", "rcu"},
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ parseStackTrace,
+ },
+ skip: []string{"apic_timer_interrupt", "rcu"},
},
},
{
title: compile("INFO: trying to register non-static key"),
fmt: "INFO: trying to register non-static key in %[1]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
- skip: []string{"stack", "lock", "IRQ"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: []string{"stack", "lock", "IRQ"},
},
},
{
@@ -856,15 +886,21 @@ var linuxOopses = []*oops{
report: compile("INFO: suspicious RCU usage(?:.*\n)+?.*?:{{SRC}}"),
fmt: "INFO: suspicious RCU usage in %[2]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
- skip: []string{"rcu"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ skip: []string{"rcu"},
},
},
{
title: compile("INFO: task .* blocked for more than [0-9]+ seconds"),
fmt: "INFO: task hung in %[1]v",
stack: &stackFmt{
- start: []string{"Call Trace:"},
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
skip: []string{"sched", "_lock", "completion", "kthread",
"wait", "synchronize"},
},
@@ -900,7 +936,13 @@ var linuxOopses = []*oops{
{
title: compile("general protection fault:"),
fmt: "general protection fault in %[1]v",
- stack: &stackFmt{},
+ stack: &stackFmt{
+ parts: []*regexp.Regexp{
+ linuxRipFrame,
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ },
},
},
[]*regexp.Regexp{},
@@ -943,12 +985,22 @@ var linuxOopses = []*oops{
{
title: compile("kernel BUG at mm/usercopy.c"),
fmt: "BUG: bad usercopy in %[1]v",
- stack: &stackFmt{start: []string{"Call Trace:"}},
+ stack: &stackFmt{
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ },
},
{
title: compile("kernel BUG at lib/list_debug.c"),
fmt: "BUG: corrupted list in %[1]v",
- stack: &stackFmt{start: []string{"Call Trace:"}},
+ stack: &stackFmt{
+ parts: []*regexp.Regexp{
+ compile("Call Trace:"),
+ parseStackTrace,
+ },
+ },
},
},
[]*regexp.Regexp{},
diff --git a/pkg/report/report.go b/pkg/report/report.go
index 399b497bc..89d780b4b 100644
--- a/pkg/report/report.go
+++ b/pkg/report/report.go
@@ -93,13 +93,22 @@ type oopsFormat struct {
}
type stackFmt struct {
- // If start is empty, starting matching stack trace from the beginning of the report.
- // Otherwise start matching trace after first start match.
- start []string
+ // parts describe how guilty stack frame must be extracted from the report.
+ // parts are matched consecutively potentially capturing frames.
+ // parts can be of 3 types:
+ // - non-capturing regexp, matched against report and advances current position
+ // - capturing regexp, same as above, but also yields a frame
+ // - special value parseStackTrace means that a stack trace must be parsed
+ // starting from current position
+ parts []*regexp.Regexp
+ // If parts2 is present it is tried when parts matching fails.
+ parts2 []*regexp.Regexp
// Skip these functions in stack traces (matched as substring).
skip []string
}
+var parseStackTrace *regexp.Regexp
+
func compile(re string) *regexp.Regexp {
re = strings.Replace(re, "{{ADDR}}", "0x[0-9a-f]+", -1)
re = strings.Replace(re, "{{PC}}", "\\[\\<[0-9a-f]+\\>\\]", -1)
@@ -175,8 +184,8 @@ func extractDescription(output []byte, oops *oops, params *stackParams) (
args = append(args, string(output[match[i]:match[i+1]]))
}
if f.stack != nil {
- frame, ok := extractStackFrame(params, f.stack, output[match[0]:])
- if !ok {
+ frame := extractStackFrame(params, f.stack, output[match[0]:])
+ if frame == "" {
continue
}
args = append(args, frame)
@@ -234,43 +243,66 @@ func startReportPrefix(output []byte, prefixes []string) []byte {
return nil
}
-func extractStackFrame(params *stackParams, stack *stackFmt, output []byte) (frame string, ok bool) {
- output = startReportPrefix(output, stack.start)
- if len(output) == 0 {
- return
- }
- stackTraces := 0
+func extractStackFrame(params *stackParams, stack *stackFmt, output []byte) string {
skip := append([]string{}, params.skipPatterns...)
skip = append(skip, stack.skip...)
- skipRe := regexp.MustCompile(strings.Join(skip, "|"))
- for s := bufio.NewScanner(bytes.NewReader(output)); s.Scan(); {
- ln := s.Bytes()
- for _, re := range params.stackStartRes {
- if re.Match(ln) {
- stackTraces++
- if stackTraces > 1 {
- return "", false
+ var skipRe *regexp.Regexp
+ if len(skip) != 0 {
+ skipRe = regexp.MustCompile(strings.Join(skip, "|"))
+ }
+ frame := extractStackFrameImpl(params, output, skipRe, stack.parts)
+ if frame != "" || len(stack.parts2) == 0 {
+ return frame
+ }
+ return extractStackFrameImpl(params, output, skipRe, stack.parts2)
+}
+
+func extractStackFrameImpl(params *stackParams, output []byte, skipRe *regexp.Regexp,
+ parts []*regexp.Regexp) string {
+ s := bufio.NewScanner(bytes.NewReader(output))
+nextPart:
+ for _, part := range parts {
+ if part == parseStackTrace {
+ for s.Scan() {
+ ln := s.Bytes()
+ for _, re := range params.stackStartRes {
+ if re.Match(ln) {
+ continue nextPart
+ }
+ }
+ var match []int
+ for _, re := range params.frameRes {
+ match = re.FindSubmatchIndex(ln)
+ if match != nil {
+ break
+ }
+ }
+ if match == nil {
+ continue
+ }
+ frame := ln[match[2]:match[3]]
+ if skipRe == nil || !skipRe.Match(frame) {
+ return string(frame)
}
- break
}
- }
- var match []int
- for _, re := range params.frameRes {
- match = re.FindSubmatchIndex(ln)
- if match != nil {
+ } else {
+ for s.Scan() {
+ ln := s.Bytes()
+ match := part.FindSubmatchIndex(ln)
+ if match == nil {
+ continue
+ }
+ if len(match) == 4 && match[2] != -1 {
+ frame := ln[match[2]:match[3]]
+ if skipRe == nil || !skipRe.Match(frame) {
+ return string(frame)
+ }
+ }
break
}
}
- if match == nil {
- continue
- }
- frame = string(ln[match[2]:match[3]])
- if len(skip) != 0 && skipRe.MatchString(frame) {
- continue
- }
- return frame, true
}
- return "", false
+ return ""
}
// replace replaces [start:end] in where with what, inplace.
diff --git a/pkg/report/testdata/linux/report/196 b/pkg/report/testdata/linux/report/196
index dd3ae765d..042889bc3 100644
--- a/pkg/report/testdata/linux/report/196
+++ b/pkg/report/testdata/linux/report/196
@@ -1,5 +1,4 @@
TITLE: KASAN: wild-memory-access Read in sg_read
-CORRUPTED: Y
[ 67.633749] ==================================================================
[ 67.633767] BUG: KASAN: wild-memory-access in sg_read+0xe5c/0x1440
diff --git a/pkg/report/testdata/linux/report/197 b/pkg/report/testdata/linux/report/197
index 150ef6edb..78ae656db 100644
--- a/pkg/report/testdata/linux/report/197
+++ b/pkg/report/testdata/linux/report/197
@@ -1,5 +1,4 @@
TITLE: KASAN: global-out-of-bounds Read in show_timer
-CORRUPTED: Y
[ 66.768767] ==================================================================
[ 66.776196] BUG: KASAN: global-out-of-bounds in show_timer+0x27a/0x2b0 at addr ffffffff82cda558
diff --git a/pkg/report/testdata/linux/report/198 b/pkg/report/testdata/linux/report/198
index 7c3f1e568..6373ecbcd 100644
--- a/pkg/report/testdata/linux/report/198
+++ b/pkg/report/testdata/linux/report/198
@@ -1,5 +1,4 @@
-# TODO: must be "in ip6t_do_table"
-TITLE: general protection fault in __vmalloc_node_range
+TITLE: general protection fault in ip6t_do_table
[ 159.247590] syz-executor6: vmalloc: allocation failure: 8589934588 bytes, mode:0x14080c0(GFP_KERNEL|__GFP_ZERO), nodemask=(null)
[ 159.259380] syz-executor6 cpuset=/ mems_allowed=0
diff --git a/pkg/report/testdata/linux/report/199 b/pkg/report/testdata/linux/report/199
index ea2aa11a8..1c606e5f1 100644
--- a/pkg/report/testdata/linux/report/199
+++ b/pkg/report/testdata/linux/report/199
@@ -1,5 +1,4 @@
-# TODO: must be "in iov_iter_advance".
-TITLE: KASAN: stack-out-of-bounds Read in warn_alloc_failed
+TITLE: KASAN: stack-out-of-bounds Read in iov_iter_advance
[ 81.174109] ==================================================================
[ 81.174125] BUG: KASAN: stack-out-of-bounds in iov_iter_advance+0x4c0/0x4f0 at addr ffff8801ca657d38
diff --git a/pkg/report/testdata/linux/report/200 b/pkg/report/testdata/linux/report/200
index 2187f2f03..c30a296e8 100644
--- a/pkg/report/testdata/linux/report/200
+++ b/pkg/report/testdata/linux/report/200
@@ -1,5 +1,4 @@
-# TODO: must be "in ip6t_do_table"
-TITLE: general protection fault in should_fail
+TITLE: general protection fault in ip6t_do_table
[ 73.452724] FAULT_INJECTION: forcing a failure.
[ 73.452724] name failslab, interval 1, probability 0, space 0, times 0