// Copyright 2018 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 ( "bytes" "regexp" ) type gvisor struct { ignores []*regexp.Regexp } func ctorGvisor(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter, []string, error) { ctx := &gvisor{ ignores: ignores, } suppressions := []string{ "fatal error: runtime: out of memory", "fatal error: runtime: cannot allocate memory", "panic: ptrace sysemu failed: no such process", // OOM kill "panic: ptrace set fpregs failed: no such process", // OOM kill "panic: ptrace set regs failed: no such process", // OOM kill "panic: failed to start executor binary", "panic: executor failed: pthread_create failed", "ERROR: ThreadSanitizer", // Go race failing due to OOM. "FATAL: ThreadSanitizer", } return ctx, suppressions, nil } func (ctx *gvisor) ContainsCrash(output []byte) bool { return containsCrash(output, gvisorOopses, ctx.ignores) } func (ctx *gvisor) Parse(output []byte) *Report { rep := simpleLineParser(output, gvisorOopses, nil, ctx.ignores) if rep == nil { return nil } rep.Title = replaceTable(gvisorTitleReplacement, rep.Title) rep.Report = ctx.shortenReport(rep.Report) return rep } func (ctx *gvisor) shortenReport(report []byte) []byte { // gvisor panics include stacks of all goroutines. // This output is too lengthy for report and not very useful. // So we always take 5 lines from report and then cut it at the next empty line. // The intention is to capture panic header and traceback of the first goroutine. pos := 0 for i := 0; i < 5; i++ { pos1 := bytes.IndexByte(report[pos:], '\n') if pos1 == -1 { return report } pos += pos1 + 1 } end := bytes.Index(report[pos:], []byte{'\n', '\n'}) if end == -1 { return report } if bytes.Contains(report, []byte("WARNING: DATA RACE")) { // For data races extract both stacks. end2 := bytes.Index(report[pos+end+2:], []byte{'\n', '\n'}) if end2 != -1 { end += end2 + 2 } } return report[:pos+end+1] } func (ctx *gvisor) Symbolize(rep *Report) error { return nil } var gvisorTitleReplacement = []replacement{ { regexp.MustCompile(`container ".*"`), "container NAME", }, } var gvisorOopses = []*oops{ { []byte("panic:"), []oopsFormat{ { title: compile("panic:(.*)"), fmt: "panic:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("Panic:"), []oopsFormat{ { title: compile("Panic:(.*)"), fmt: "Panic:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("fatal error:"), []oopsFormat{ { title: compile("fatal error:(.*)"), fmt: "fatal error:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("runtime error:"), []oopsFormat{ { title: compile("runtime error:(.*)"), fmt: "runtime error:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("SIGSEGV:"), []oopsFormat{ { title: compile("SIGSEGV:(.*)"), fmt: "SIGSEGV:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("SIGBUS:"), []oopsFormat{ { title: compile("SIGBUS:(.*)"), fmt: "SIGBUS:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("FATAL ERROR:"), []oopsFormat{ { title: compile("FATAL ERROR:(.*)"), fmt: "FATAL ERROR:%[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("WARNING: DATA RACE"), []oopsFormat{ { title: compile("WARNING: DATA RACE"), report: compile("WARNING: DATA RACE\n(?:.*\n)*? (?:[a-zA-Z0-9./-_]+/)([a-zA-Z0-9.()*_]+)\\(\\)\n"), fmt: "DATA RACE in %[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, { []byte("Invalid request partialResult"), []oopsFormat{ { title: compile("Invalid request partialResult"), report: compile("Invalid request partialResult .* for (.*) operation"), fmt: "Invalid request partialResult in %[1]v", noStackTrace: true, }, }, []*regexp.Regexp{}, }, }