pkg/report: fix guilty file extraction

Account for the case that some file names can appear _before_ crash report starts.
Start extracting guilty file starting from StartPos.
This commit is contained in:
Dmitry Vyukov 2018-09-26 12:02:43 +02:00
parent 455b6354e8
commit db716d6653
5 changed files with 125 additions and 39 deletions

View File

@ -27,7 +27,7 @@ type linux struct {
consoleOutputRe *regexp.Regexp
questionableRe *regexp.Regexp
guiltyFileBlacklist []*regexp.Regexp
reportStartIgnores [][]byte
reportStartIgnores []*regexp.Regexp
infoMessagesWithStack [][]byte
eoi []byte
}
@ -82,10 +82,10 @@ func ctorLinux(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter,
regexp.MustCompile(`^fs/proc/generic.c`),
}
// These pattern do _not_ start a new report, i.e. can be in a middle of another report.
ctx.reportStartIgnores = [][]byte{
[]byte("invalid opcode: 0000"),
[]byte("Kernel panic - not syncing: panic_on_warn set"),
[]byte("unregister_netdevice: waiting for"),
ctx.reportStartIgnores = []*regexp.Regexp{
compile(`invalid opcode: 0000`),
compile(`Kernel panic - not syncing: panic_on_warn set`),
compile(`unregister_netdevice: waiting for`),
}
// These pattern math kernel reports which are not bugs in itself but contain stack traces.
// If we see them in the middle of another report, we know that the report is potentially corrupted.
@ -200,14 +200,7 @@ func (ctx *linux) parseOutput(output []byte) (
startPos = pos
break
} else if secondReportPos == 0 {
ignored := false
for _, ignore := range ctx.reportStartIgnores {
if bytes.Contains(line, ignore) {
ignored = true
break
}
}
if !ignored {
if !matchesAny(line, ctx.reportStartIgnores) {
secondReportPos = pos
}
}
@ -274,20 +267,22 @@ func (ctx *linux) parseOutput(output []byte) (
}
func (ctx *linux) Symbolize(rep *Report) error {
if ctx.vmlinux == "" {
return nil
}
symbolized, err := ctx.symbolize(rep.Report)
if err != nil {
return err
}
rep.Report = symbolized
guiltyFile := ctx.extractGuiltyFile(rep.Report)
if guiltyFile != "" {
rep.Maintainers, err = ctx.getMaintainers(guiltyFile)
if ctx.vmlinux != "" {
symbolized, err := ctx.symbolize(rep.Report)
if err != nil {
return err
}
rep.Report = symbolized
}
// We still do this even if we did not symbolize,
// because tests pass in already symbolized input.
rep.guiltyFile = ctx.extractGuiltyFile(rep)
if rep.guiltyFile != "" {
maintainers, err := ctx.getMaintainers(rep.guiltyFile)
if err != nil {
return err
}
rep.Maintainers = maintainers
}
return nil
}
@ -381,7 +376,8 @@ func symbolizeLine(symbFunc func(bin string, pc uint64) ([]symbolizer.Frame, err
return symbolized
}
func (ctx *linux) extractGuiltyFile(report []byte) string {
func (ctx *linux) extractGuiltyFile(rep *Report) string {
report := rep.Report[rep.StartPos:]
if linuxRcuStall.Match(report) {
// Special case for rcu stalls.
// There are too many frames that we want to skip before actual guilty frames,
@ -412,6 +408,9 @@ nextFile:
}
func (ctx *linux) getMaintainers(file string) ([]string, error) {
if ctx.kernelSrc == "" {
return nil, nil
}
mtrs, err := ctx.getMaintainersImpl(file, false)
if err != nil {
return nil, err

View File

@ -43,8 +43,10 @@ type Report struct {
Corrupted bool
// CorruptedReason contains reason why the report is marked as corrupted.
CorruptedReason string
// Maintainers is list of maintainer emails.
// Maintainers is list of maintainer emails (filled in by Symbolize).
Maintainers []string
// guiltyFile is the source file that we think is to blame for the crash (filled in by Symbolize).
guiltyFile string
}
// NewReporter creates reporter for the specified OS/Type.
@ -189,17 +191,6 @@ func sanitizeTitle(title string) string {
return strings.TrimSpace(string(res))
}
type guilter interface {
extractGuiltyFile([]byte) string
}
func (wrap reporterWrapper) extractGuiltyFile(report []byte) string {
if g, ok := wrap.Reporter.(guilter); ok {
return g.extractGuiltyFile(report)
}
panic("not implemented")
}
type oops struct {
header []byte
formats []oopsFormat

View File

@ -220,8 +220,26 @@ func testGuiltyFile(t *testing.T, reporter Reporter, fn string) {
}
file := string(data[len(prefix) : len(prefix)+nlnl])
report := data[len(prefix)+nlnl:]
if guilty := reporter.(guilter).extractGuiltyFile(report); guilty != file {
t.Fatalf("got guilty %q, want %q", guilty, file)
rep := reporter.Parse(report)
if rep == nil {
t.Fatalf("did not find crash in the input")
}
// Parse doesn't generally run on already symbolized output,
// but here we run it on symbolized output because we can't symbolize in tests.
// The problem is with duplicated lines due to inlined frames,
// Parse can strip such report after first title line because it thinks
// that the duplicated title line is beginning on another report.
// In such case we restore whole report, but still keep StartPos that
// Parse produces at least in some cases.
if !bytes.HasSuffix(report, rep.Report) {
rep.Report = report
rep.StartPos = 0
}
if err := reporter.Symbolize(rep); err != nil {
t.Fatalf("failed to symbolize report: %v", err)
}
if rep.guiltyFile != file {
t.Fatalf("got guilty %q, want %q", rep.guiltyFile, file)
}
}

View File

@ -1,3 +1,4 @@
FILE: driver/foo/lib/foo.c
WARNING: CPU: 2 PID: 24023 at kernel/locking/lockdep.c:3344 __lock_acquire+0x10e5/0x3690 kernel/locking/lockdep.c:3344
driver/foo/lib/foo.c:10

77
pkg/report/testdata/linux/guilty/39 vendored Normal file
View File

@ -0,0 +1,77 @@
FILE: security/apparmor/policy_ns.c
sock_common_setsockopt+0x9a/0xe0 net/core/sock.c:3038
__sys_setsockopt+0x1ba/0x3c0 net/socket.c:1902
__do_sys_setsockopt net/socket.c:1913 [inline]
__se_sys_setsockopt net/socket.c:1910 [inline]
__x64_sys_setsockopt+0xbe/0x150 net/socket.c:1910
do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
==================================================================
BUG: KASAN: global-out-of-bounds in memcmp+0xe3/0x160 lib/string.c:861
Read of size 1 at addr ffffffff88000008 by task syz-executor0/10914
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x457579
Code: 1d b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 eb b3 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f4c14533c78 EFLAGS: 00000246 ORIG_RAX: 0000000000000036
RAX: ffffffffffffffda RBX: 00007f4c14533c90 RCX: 0000000000457579
RDX: 0000000000000040 RSI: 0000000000000000 RDI: 0000000000000003
RBP: 000000000072bf00 R08: 0000000000000004 R09: 0000000000000000
R10: 0000000020000080 R11: 0000000000000246 R12: 00007f4c145346d4
R13: 00000000004c3ed9 R14: 00000000004d6260 R15: 0000000000000004
CPU: 0 PID: 10914 Comm: syz-executor0 Not tainted 4.19.0-rc5+ #252
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1c4/0x2b4 lib/dump_stack.c:113
print_address_description.cold.8+0x58/0x1ff mm/kasan/report.c:256
kasan_report_error mm/kasan/report.c:354 [inline]
kasan_report.cold.9+0x242/0x309 mm/kasan/report.c:412
__asan_report_load1_noabort+0x14/0x20 mm/kasan/report.c:430
memcmp+0xe3/0x160 lib/string.c:861
strnstr+0x4b/0x70 lib/string.c:934
__aa_lookupn_ns+0xc1/0x570 security/apparmor/policy_ns.c:209
aa_lookupn_ns+0x88/0x1e0 security/apparmor/policy_ns.c:240
aa_fqlookupn_profile+0x1b9/0x1010 security/apparmor/policy.c:468
fqlookupn_profile+0x80/0xc0 security/apparmor/label.c:1844
aa_label_strn_parse+0xa3a/0x1230 security/apparmor/label.c:1908
aa_label_parse+0x42/0x50 security/apparmor/label.c:1943
aa_change_profile+0x513/0x3510 security/apparmor/domain.c:1362
apparmor_setprocattr+0xa8b/0x1150 security/apparmor/lsm.c:656
security_setprocattr+0x66/0xc0 security/security.c:1298
proc_pid_attr_write+0x301/0x540 fs/proc/base.c:2555
__vfs_write+0x119/0x9f0 fs/read_write.c:485
vfs_write+0x1fc/0x560 fs/read_write.c:549
ksys_write+0x101/0x260 fs/read_write.c:598
__do_sys_write fs/read_write.c:610 [inline]
__se_sys_write fs/read_write.c:607 [inline]
__x64_sys_write+0x73/0xb0 fs/read_write.c:607
do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
entry_SYSCALL_64_after_hwframe+0x49/0xbe
RIP: 0033:0x457579
Code: 1d b4 fb ff c3 66 2e 0f 1f 84 00 00 00 00 00 66 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 0f 83 eb b3 fb ff c3 66 2e 0f 1f 84 00 00 00 00
RSP: 002b:00007f5a92ec2c78 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 0000000000457579
RDX: 000000000000002c RSI: 00000000200000c0 RDI: 0000000000000004
RBP: 000000000072bf00 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f5a92ec36d4
R13: 00000000004c5454 R14: 00000000004d8c78 R15: 00000000ffffffff
CPU: 1 PID: 10921 Comm: syz-executor3 Not tainted 4.19.0-rc5+ #252
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
The buggy address belongs to the variable:
__start_rodata+0x8/0x1000
Call Trace:
__dump_stack lib/dump_stack.c:77 [inline]
dump_stack+0x1c4/0x2b4 lib/dump_stack.c:113
Memory state around the buggy address:
ffffffff87ffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffffffff87ffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
fail_dump lib/fault-inject.c:51 [inline]
should_fail.cold.4+0xa/0x17 lib/fault-inject.c:149
ffffffff88000000: 00 fa fa fa fa fa fa fa 00 01 fa fa fa fa fa fa
^
ffffffff88000080: 00 00 00 07 fa fa fa fa 00 04 fa fa fa fa fa fa
ffffffff88000100: 05 fa fa fa fa fa fa fa 00 00 00 00 05 fa fa fa
==================================================================