Golang程序  |  180行  |  4.84 KB

// 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
}