pkg/report: support freebsd

This commit is contained in:
Dmitry Vyukov 2017-10-17 18:16:29 +02:00
parent 85c802e4cf
commit 6368c469a5
6 changed files with 275 additions and 80 deletions

View File

@ -4,6 +4,8 @@
package report
import (
"bytes"
"fmt"
"regexp"
"github.com/google/syzkaller/pkg/symbolizer"
@ -28,25 +30,88 @@ func ctorFreebsd(kernelSrc, kernelObj string, symbols map[string][]symbolizer.Sy
}
func (ctx *freebsd) ContainsCrash(output []byte) bool {
panic("not implemented")
return containsCrash(output, freebsdOopses, ctx.ignores)
}
func (ctx *freebsd) Parse(output []byte) (desc string, text []byte, start int, end int) {
panic("not implemented")
var oops *oops
for pos := 0; pos < len(output); {
next := bytes.IndexByte(output[pos:], '\n')
if next != -1 {
next += pos
} else {
next = len(output)
}
for _, oops1 := range freebsdOopses {
match := matchOops(output[pos:next], oops1, ctx.ignores)
if match == -1 {
continue
}
if oops == nil {
oops = oops1
start = pos
desc = string(output[pos+match : next])
}
end = next
}
// Console output is indistinguishable from fuzzer output,
// so we just collect everything after the oops.
if oops != nil {
lineEnd := next
if lineEnd != 0 && output[lineEnd-1] == '\r' {
lineEnd--
}
text = append(text, output[pos:lineEnd]...)
text = append(text, '\n')
}
pos = next + 1
}
if oops == nil {
return
}
desc = extractDescription(output[start:], oops)
return
}
func (ctx *freebsd) Symbolize(text []byte) ([]byte, error) {
panic("not implemented")
return nil, fmt.Errorf("not implemented")
}
func (ctx *freebsd) ExtractConsoleOutput(output []byte) (result []byte) {
panic("not implemented")
return output
}
func (ctx *freebsd) ExtractGuiltyFile(report []byte) string {
panic("not implemented")
return ""
}
func (ctx *freebsd) GetMaintainers(file string) ([]string, error) {
panic("not implemented")
return nil, fmt.Errorf("not implemented")
}
var freebsdOopses = []*oops{
&oops{
[]byte("Fatal trap"),
[]oopsFormat{
{
compile("Fatal trap (.+?)\\r?\\n(?:.*\\n)+?" +
"KDB: stack backtrace:\\r?\\n" +
"(?:#[0-9]+ {{ADDR}} at (?:kdb_backtrace|vpanic|panic|trap_fatal|" +
"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",
},
},
[]*regexp.Regexp{},
},
&oops{
[]byte("panic:"),
[]oopsFormat{
{
compile("panic: ffs_write: type {{ADDR}} [0-9]+ \\([0-9]+,[0-9]+\\)"),
"panic: ffs_write: type ADDR X (Y,Z)",
},
},
[]*regexp.Regexp{},
},
}

126
pkg/report/freebsd_test.go Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2017 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
package report
import (
"testing"
)
func TestFreebsdParse(t *testing.T) {
testParse(t, "freebsd", freebsdTests)
}
var freebsdTests = map[string]string{
`
Fatal trap 12: page fault while in kernel mode
cpuid = 0; apic id = 00
fault virtual address = 0xffffffff12852143
fault code = supervisor read data, page not present
instruction pointer = 0x20:0xffffffff8102fe62
stack pointer = 0x28:0xfffffe009524a960
frame pointer = 0x28:0xfffffe009524a990
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags = interrupt enabled, resume, IOPL = 0
current process = 3094 (syz-executor0)
trap number = 12
panic: page fault
cpuid = 0
KDB: stack backtrace:
#0 0xffffffff80aada97 at kdb_backtrace+0x67
#1 0xffffffff80a6bb76 at vpanic+0x186
#2 0xffffffff80a6b9e3 at panic+0x43
#3 0xffffffff80edf832 at trap_fatal+0x322
#4 0xffffffff80edf889 at trap_pfault+0x49
#5 0xffffffff80edf0c6 at trap+0x286
#6 0xffffffff80ec3641 at calltrap+0x8
#7 0xffffffff810302fc at atrtc_settime+0xc
#8 0xffffffff80ab7361 at resettodr+0xf1
#9 0xffffffff80a7fcc6 at settime+0x156
#10 0xffffffff80a7fae5 at sys_clock_settime+0x85
#11 0xffffffff80ee0394 at amd64_syscall+0x6c4
#12 0xffffffff80ec392b at Xfast_syscall+0xfb
`: `Fatal trap 12: page fault while in kernel mode in atrtc_settime`,
`
Fatal trap 12: page fault while in kernel mode
cpuid = 3; apic id = 03
fault virtual address = 0xfffff7ffb48e19a8
fault code = supervisor read data, page not present
instruction pointer = 0x20:0xffffffff80edd52a
stack pointer = 0x28:0xfffffe009524a7a0
frame pointer = 0x28:0xfffffe009524a7a0
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags = interrupt enabled, resume, IOPL = 0
current process = 40394 (syz-executor1)
trap number = 12
panic: page fault
cpuid = 3
KDB: stack backtrace:
#0 0xffffffff80aada97 at kdb_backtrace+0x67
#1 0xffffffff80a6bb76 at vpanic+0x186
#2 0xffffffff80a6b9e3 at panic+0x43
#3 0xffffffff80edf832 at trap_fatal+0x322
#4 0xffffffff80edf889 at trap_pfault+0x49
#5 0xffffffff80edf0c6 at trap+0x286
#6 0xffffffff80ec3641 at calltrap+0x8
#7 0xffffffff80ae96e1 at m_copydata+0x61
#8 0xffffffff80c05ba7 at sctp_sosend+0x157
#9 0xffffffff80afa411 at kern_sendit+0x291
#10 0xffffffff80afa773 at sendit+0x1a3
#11 0xffffffff80afa831 at sys_sendmsg+0x61
#12 0xffffffff80ee0394 at amd64_syscall+0x6c4
#13 0xffffffff80ec392b at Xfast_syscall+0xfb
`: `Fatal trap 12: page fault while in kernel mode in sctp_sosend`,
`
Fatal trap 9: general protection fault while in kernel mode
cpuid = 0; apic id = 00
instruction pointer = 0x20:0xffffffff80ac2563
stack pointer = 0x28:0xfffffe00003bd6e0
frame pointer = 0x28:0xfffffe00003bd720
code segment = base 0x0, limit 0xfffff, type 0x1b
= DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags = resume, IOPL = 0
current process = 51304 (syz-executor5)
trap number = 9
panic: general protection fault
cpuid = 0
KDB: stack backtrace:
#0 0xffffffff80aada97 at kdb_backtrace+0x67
#1 0xffffffff80a6bb76 at vpanic+0x186
#2 0xffffffff80a6b9e3 at panic+0x43
#3 0xffffffff80edf832 at trap_fatal+0x322
#4 0xffffffff80edee9e at trap+0x5e
#5 0xffffffff80ec3641 at calltrap+0x8
#6 0xffffffff80a6780b at __rw_wlock_hard+0x32b
#7 0xffffffff80c65e72 at udp_close+0x142
#8 0xffffffff80af2b41 at soclose+0xe1
#9 0xffffffff80a1ace9 at closef+0x269
#10 0xffffffff80a1a7bd at fdescfree_fds+0x7d
#11 0xffffffff80a1a397 at fdescfree+0x517
#12 0xffffffff80a29348 at exit1+0x508
#13 0xffffffff80a28e3d at sys_sys_exit+0xd
#14 0xffffffff80ee0394 at amd64_syscall+0x6c4
#15 0xffffffff80ec392b at Xfast_syscall+0xfb
`: `Fatal trap 9: general protection fault while in kernel mode in udp_close`,
`
panic: ffs_write: type 0xfffff80036275ce8 8 (0,230)
cpuid = 0
KDB: stack backtrace:
#0 0xffffffff80aada97 at kdb_backtrace+0x67
#1 0xffffffff80a6bb76 at vpanic+0x186
#2 0xffffffff80a6b9e3 at panic+0x43
#3 0xffffffff80d3611c at ffs_write+0x57c
#4 0xffffffff8104c6b1 at VOP_WRITE_APV+0x111
#5 0xffffffff80b3ade0 at vn_write+0x240
#6 0xffffffff80b36902 at vn_io_fault+0x112
#7 0xffffffff80ac8d08 at dofilewrite+0xc8
#8 0xffffffff80ac87fb at sys_write+0xdb
#9 0xffffffff80ee0394 at amd64_syscall+0x6c4
#10 0xffffffff80ec392b at Xfast_syscall+0xfb
`: `panic: ffs_write: type ADDR X (Y,Z)`,
}

View File

@ -150,9 +150,6 @@ func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end
return
}
desc = extractDescription(output[start:], oops)
if len(desc) > 0 && desc[len(desc)-1] == '\r' {
desc = desc[:len(desc)-1]
}
// Executor PIDs are not interesting.
desc = executorRe.ReplaceAllLiteralString(desc, "syz-executor")
// Replace that everything looks like an address with "ADDR",
@ -167,48 +164,9 @@ func (ctx *linux) Parse(output []byte) (desc string, text []byte, start int, end
desc = funcRe.ReplaceAllString(desc, "$1")
// CPU numbers are not interesting.
desc = cpuRe.ReplaceAllLiteralString(desc, "CPU")
// Corrupted/intermixed lines can be very long.
const maxDescLen = 180
if len(desc) > maxDescLen {
desc = desc[:maxDescLen]
}
return
}
func extractDescription(output []byte, oops *oops) string {
result := ""
startPos := -1
for _, format := range oops.formats {
match := format.re.FindSubmatchIndex(output)
if match == nil {
continue
}
if startPos != -1 && startPos <= match[0] {
continue
}
startPos = match[0]
var args []interface{}
for i := 2; i < len(match); i += 2 {
args = append(args, string(output[match[i]:match[i+1]]))
}
result = fmt.Sprintf(format.fmt, args...)
}
if result != "" {
return result
}
pos := bytes.Index(output, oops.header)
if pos == -1 {
panic("non matching oops")
}
end := bytes.IndexByte(output[pos:], '\n')
if end == -1 {
end = len(output)
} else {
end += pos
}
return string(output[pos:end])
}
func (ctx *linux) Symbolize(text []byte) ([]byte, error) {
symb := symbolizer.NewSymbolizer()
defer symb.Close()

View File

@ -6,7 +6,6 @@ package report
import (
"fmt"
"regexp"
"strings"
"testing"
"github.com/google/syzkaller/pkg/symbolizer"
@ -705,7 +704,7 @@ WARNING: /etc/ssh/moduli does not exist, using fixed modulus
`
[ 92.396607] general protection fault: 0000 [#1] [ 387.811073] audit: type=1326 audit(1486238739.637:135): auid=4294967295 uid=0 gid=0 ses=4294967295 pid=10020 comm="syz-executor1" exe="/root/syz-executor1" sig=31 arch=c000003e syscall=202 compat=0 ip=0x44fad9 code=0x0
`: `general protection fault: 0000 [#1] [ 387.NUM] audit: type=1326 audit(ADDR.637:LINE): auid=ADDR uid=0 gid=0 ses=ADDR pid=NUM comm="syz-executor" exe="/root/syz-executor" sig=31 arc`,
`: `general protection fault: 0000 [#1] [ 387.NUM] audit: type=1326 audit(ADDR.637:LINE): auid=ADDR uid=0 gid=0 ses=ADDR pid=NUM comm="syz-executor" exe="/root/s`,
`
[ 40.438790] BUG: Bad page map in process syz-executor6 pte:ffff8801a700ff00 pmd:1a700f067
@ -778,36 +777,7 @@ other info that might help us debug this:
[ 16.763144] [syscamera][msm_companion_pll_init::594][BIN_INFO::0x0008][WAFER_INFO::0xcf80][voltage 0.775]
`: ``,
}
reporter, err := NewReporter("linux", "", "", nil, nil)
if err != nil {
t.Fatal(err)
}
for log, crash := range tests {
if strings.Index(log, "\r\n") != -1 {
continue
}
tests[strings.Replace(log, "\n", "\r\n", -1)] = crash
}
for log, crash := range tests {
containsCrash := reporter.ContainsCrash([]byte(log))
expectCrash := (crash != "")
if expectCrash && !containsCrash {
t.Fatalf("ContainsCrash did not find crash")
}
if !expectCrash && containsCrash {
t.Fatalf("ContainsCrash found unexpected crash")
}
desc, _, _, _ := reporter.Parse([]byte(log))
if desc == "" && crash != "" {
t.Fatalf("did not find crash message '%v' in:\n%v", crash, log)
}
if desc != "" && crash == "" {
t.Fatalf("found bogus crash message '%v' in:\n%v", desc, log)
}
if desc != crash {
t.Fatalf("extracted bad crash message:\n%+q\nwant:\n%+q", desc, crash)
}
}
testParse(t, "linux", tests)
}
func TestLinuxIgnores(t *testing.T) {

View File

@ -113,6 +113,48 @@ func matchOops(line []byte, oops *oops, ignores []*regexp.Regexp) int {
return match
}
func extractDescription(output []byte, oops *oops) string {
desc := ""
startPos := -1
for _, format := range oops.formats {
match := format.re.FindSubmatchIndex(output)
if match == nil {
continue
}
if startPos != -1 && startPos <= match[0] {
continue
}
startPos = match[0]
var args []interface{}
for i := 2; i < len(match); i += 2 {
args = append(args, string(output[match[i]:match[i+1]]))
}
desc = fmt.Sprintf(format.fmt, args...)
}
if desc == "" {
pos := bytes.Index(output, oops.header)
if pos == -1 {
panic("non matching oops")
}
end := bytes.IndexByte(output[pos:], '\n')
if end == -1 {
end = len(output)
} else {
end += pos
}
desc = string(output[pos:end])
}
if len(desc) > 0 && desc[len(desc)-1] == '\r' {
desc = desc[:len(desc)-1]
}
// Corrupted/intermixed lines can be very long.
const maxDescLen = 180
if len(desc) > maxDescLen {
desc = desc[:maxDescLen]
}
return desc
}
// replace replaces [start:end] in where with what, inplace.
func replace(where []byte, start, end int, what []byte) []byte {
if len(what) >= end-start {

View File

@ -5,6 +5,7 @@ package report
import (
"fmt"
"strings"
"testing"
)
@ -38,3 +39,36 @@ func TestReplace(t *testing.T) {
})
}
}
func testParse(t *testing.T, os string, tests map[string]string) {
reporter, err := NewReporter(os, "", "", nil, nil)
if err != nil {
t.Fatal(err)
}
for log, crash := range tests {
if strings.Index(log, "\r\n") != -1 {
continue
}
tests[strings.Replace(log, "\n", "\r\n", -1)] = crash
}
for log, crash := range tests {
containsCrash := reporter.ContainsCrash([]byte(log))
expectCrash := (crash != "")
if expectCrash && !containsCrash {
t.Fatalf("ContainsCrash did not find crash")
}
if !expectCrash && containsCrash {
t.Fatalf("ContainsCrash found unexpected crash")
}
desc, _, _, _ := reporter.Parse([]byte(log))
if desc == "" && crash != "" {
t.Fatalf("did not find crash message '%v' in:\n%v", crash, log)
}
if desc != "" && crash == "" {
t.Fatalf("found bogus crash message '%v' in:\n%v", desc, log)
}
if desc != crash {
t.Fatalf("extracted bad crash message:\n%+q\nwant:\n%+q", desc, crash)
}
}
}