blob: 68d0aa9afc9658e724321bc1ded106af52ec7783 [file] [log] [blame]
// 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 (
"bufio"
"bytes"
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"github.com/google/syzkaller/pkg/symbolizer"
"github.com/ianlancetaylor/demangle"
)
type fuchsia struct {
obj string
ignores []*regexp.Regexp
}
var (
zirconPanic = []byte("ZIRCON KERNEL PANIC")
zirconPanicShort = []byte("KERNEL PANIC")
zirconKernelHang = []byte("stopping other cpus")
zirconRIP = regexp.MustCompile(` RIP: (0x[0-9a-f]+) `)
zirconBT = regexp.MustCompile(`^bt#[0-9]+: (0x[0-9a-f]+)`)
zirconReportEnd = []byte("Halted")
zirconUnrelated = []*regexp.Regexp{
regexp.MustCompile(`^\[\d+\.\d+\] \d+\.\d+`),
regexp.MustCompile(`stopping other cpus`),
regexp.MustCompile(`^halting cpu`),
regexp.MustCompile(`^dso: `),
regexp.MustCompile(`^UPTIME: `),
regexp.MustCompile(`^BUILDID `),
regexp.MustCompile(`^Halting\.\.\.`),
}
zirconSkip = []*regexp.Regexp{
regexp.MustCompile("^platform_halt$"),
regexp.MustCompile("^exception_die$"),
regexp.MustCompile("^_panic$"),
}
)
func ctorFuchsia(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter, []string, error) {
ctx := &fuchsia{
ignores: ignores,
}
if kernelObj != "" {
ctx.obj = filepath.Join(kernelObj, "zircon.elf")
}
suppressions := []string{
"fatal exception: process /tmp/syz-fuzzer", // OOM presumably
}
return ctx, suppressions, nil
}
func (ctx *fuchsia) ContainsCrash(output []byte) bool {
return bytes.Contains(output, zirconPanic) ||
bytes.Contains(output, zirconKernelHang)
}
func (ctx *fuchsia) Parse(output []byte) *Report {
rep := &Report{
Output: output,
EndPos: len(output),
}
wantLocation := true
if pos := bytes.Index(output, zirconPanic); pos != -1 {
rep.Title = string(zirconPanicShort)
rep.StartPos = pos
} else if pos := bytes.Index(output, zirconKernelHang); pos != -1 {
rep.Title = string(zirconKernelHang)
rep.StartPos = pos
wantLocation = false // these tend to produce random locations
} else {
return nil
}
symb := symbolizer.NewSymbolizer()
defer symb.Close()
where := ""
for s := bufio.NewScanner(bytes.NewReader(output[rep.StartPos:])); s.Scan(); {
line := s.Bytes()
if len(line) == 0 || matchesAny(line, zirconUnrelated) {
continue
}
if bytes.Equal(line, zirconReportEnd) {
break
}
if bytes.Contains(line, []byte("DEBUG ASSERT FAILED")) {
rep.Title = "ASSERT FAILED"
}
if bytes.Contains(line, []byte("Supervisor Page Fault exception")) {
rep.Title = "Supervisor fault"
}
if bytes.Contains(line, []byte("recursion in interrupt handler")) {
rep.Title = "recursion in interrupt handler"
}
if bytes.Contains(line, []byte("double fault")) {
rep.Title = "double fault"
}
if match := zirconRIP.FindSubmatchIndex(line); match != nil {
ctx.processPC(rep, symb, line, match, false, &where)
} else if match := zirconBT.FindSubmatchIndex(line); match != nil {
if ctx.processPC(rep, symb, line, match, true, &where) {
continue
}
}
rep.Report = append(rep.Report, line...)
rep.Report = append(rep.Report, '\n')
}
if wantLocation && where != "" {
rep.Title = fmt.Sprintf("%v in %v", rep.Title, where)
}
return rep
}
func (ctx *fuchsia) processPC(rep *Report, symb *symbolizer.Symbolizer,
line []byte, match []int, call bool, where *string) bool {
if ctx.obj == "" {
return false
}
prefix := line[match[0]:match[1]]
pcStart := match[2] - match[0]
pcEnd := match[3] - match[0]
pcStr := prefix[pcStart:pcEnd]
pc, err := strconv.ParseUint(string(pcStr), 0, 64)
if err != nil {
return false
}
shortPC := pc & 0xfffffff
pc = 0xffffffff80000000 | shortPC
if call {
pc--
}
frames, err := symb.Symbolize(ctx.obj, pc)
if err != nil || len(frames) == 0 {
return false
}
for _, frame := range frames {
file := ctx.trimFile(frame.File)
name := demangle.Filter(frame.Func, demangle.NoParams, demangle.NoTemplateParams)
if strings.Contains(name, "<lambda(") {
// demangle produces super long (full) names for lambdas.
name = "lambda"
}
if *where == "" && !matchesAny([]byte(name), zirconSkip) {
*where = name
}
id := "[ inline ]"
if !frame.Inline {
id = fmt.Sprintf("0x%08x", shortPC)
}
start := replace(append([]byte{}, prefix...), pcStart, pcEnd, []byte(id))
frameLine := fmt.Sprintf("%s %v %v:%v\n", start, name, file, frame.Line)
rep.Report = append(rep.Report, frameLine...)
}
return true
}
func (ctx *fuchsia) trimFile(file string) string {
const (
prefix1 = "zircon/kernel/"
prefix2 = "zircon/"
)
if pos := strings.LastIndex(file, prefix1); pos != -1 {
return file[pos+len(prefix1):]
}
if pos := strings.LastIndex(file, prefix2); pos != -1 {
return file[pos+len(prefix2):]
}
return file
}
func (ctx *fuchsia) Symbolize(rep *Report) error {
return nil
}