// 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, "