// 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 { *config obj string } var ( zirconRIP = regexp.MustCompile(` RIP: (0x[0-9a-f]+) `) zirconBT = regexp.MustCompile(`^bt#[0-9]+: (0x[0-9a-f]+)`) zirconReportEnd = []byte("Halted") zirconAssertFailed = []byte("ASSERT FAILED at") zirconLinePrefix = regexp.MustCompile(`^\[\d+\.\d+\] \d+\.\d+> `) zirconUnrelated = []*regexp.Regexp{ regexp.MustCompile(`^$`), regexp.MustCompile(`stopping other cpus`), regexp.MustCompile(`^halting cpu`), regexp.MustCompile(`^dso: `), regexp.MustCompile(`^UPTIME: `), regexp.MustCompile(`^BUILDID `), regexp.MustCompile(`^Halting\.\.\.`), } ) func ctorFuchsia(cfg *config) (Reporter, []string, error) { ctx := &fuchsia{ config: cfg, } if ctx.kernelObj != "" { ctx.obj = filepath.Join(ctx.kernelObj, ctx.target.KernelObject) } suppressions := []string{ "fatal exception: process /tmp/syz-fuzzer", // OOM presumably } return ctx, suppressions, nil } func (ctx *fuchsia) ContainsCrash(output []byte) bool { return containsCrash(output, zirconOopses, ctx.ignores) } func (ctx *fuchsia) Parse(output []byte) *Report { // We symbolize here because zircon output does not contain even function names. symbolized := ctx.symbolize(output) rep := simpleLineParser(symbolized, zirconOopses, zirconStackParams, ctx.ignores) if rep == nil { return nil } rep.Output = output if report := ctx.shortenReport(rep.Report); len(report) != 0 { rep.Report = report } return rep } func (ctx *fuchsia) shortenReport(report []byte) []byte { out := new(bytes.Buffer) for s := bufio.NewScanner(bytes.NewReader(report)); s.Scan(); { line := zirconLinePrefix.ReplaceAll(s.Bytes(), nil) if matchesAny(line, zirconUnrelated) { continue } if bytes.Contains(line, zirconReportEnd) { break } out.Write(line) out.WriteByte('\n') } return out.Bytes() } func (ctx *fuchsia) symbolize(output []byte) []byte { symb := symbolizer.NewSymbolizer(ctx.config.target) defer symb.Close() out := new(bytes.Buffer) for s := bufio.NewScanner(bytes.NewReader(output)); s.Scan(); { line := s.Bytes() if bytes.Contains(line, zirconAssertFailed) && len(line) == 127 { // This is super hacky: but zircon splits the most important information in long assert lines // (and they are always long) into several lines in irreversible way. Try to restore full line. line = append([]byte{}, line...) if s.Scan() { line = append(line, s.Bytes()...) } } if ctx.obj != "" { if match := zirconRIP.FindSubmatchIndex(line); match != nil { if ctx.processPC(out, symb, line, match, false) { continue } } else if match := zirconBT.FindSubmatchIndex(line); match != nil { if ctx.processPC(out, symb, line, match, true) { continue } } } out.Write(line) out.WriteByte('\n') } return out.Bytes() } func (ctx *fuchsia) processPC(out *bytes.Buffer, symb *symbolizer.Symbolizer, line []byte, match []int, call bool) bool { 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, "