1// Copyright 2017 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package report 5 6import ( 7 "bufio" 8 "bytes" 9 "fmt" 10 "path/filepath" 11 "regexp" 12 "strconv" 13 "strings" 14 15 "github.com/google/syzkaller/pkg/symbolizer" 16 "github.com/ianlancetaylor/demangle" 17) 18 19type fuchsia struct { 20 obj string 21 ignores []*regexp.Regexp 22} 23 24var ( 25 zirconPanic = []byte("ZIRCON KERNEL PANIC") 26 zirconPanicShort = []byte("KERNEL PANIC") 27 zirconKernelHang = []byte("stopping other cpus") 28 zirconRIP = regexp.MustCompile(` RIP: (0x[0-9a-f]+) `) 29 zirconBT = regexp.MustCompile(`^bt#[0-9]+: (0x[0-9a-f]+)`) 30 zirconReportEnd = []byte("Halted") 31 zirconUnrelated = []*regexp.Regexp{ 32 regexp.MustCompile(`^\[\d+\.\d+\] \d+\.\d+`), 33 regexp.MustCompile(`stopping other cpus`), 34 regexp.MustCompile(`^halting cpu`), 35 regexp.MustCompile(`^dso: `), 36 regexp.MustCompile(`^UPTIME: `), 37 regexp.MustCompile(`^BUILDID `), 38 regexp.MustCompile(`^Halting\.\.\.`), 39 } 40 zirconSkip = []*regexp.Regexp{ 41 regexp.MustCompile("^platform_halt$"), 42 regexp.MustCompile("^exception_die$"), 43 regexp.MustCompile("^_panic$"), 44 } 45) 46 47func ctorFuchsia(kernelSrc, kernelObj string, ignores []*regexp.Regexp) (Reporter, []string, error) { 48 ctx := &fuchsia{ 49 ignores: ignores, 50 } 51 if kernelObj != "" { 52 ctx.obj = filepath.Join(kernelObj, "zircon.elf") 53 } 54 suppressions := []string{ 55 "fatal exception: process /tmp/syz-fuzzer", // OOM presumably 56 } 57 return ctx, suppressions, nil 58} 59 60func (ctx *fuchsia) ContainsCrash(output []byte) bool { 61 return bytes.Contains(output, zirconPanic) || 62 bytes.Contains(output, zirconKernelHang) 63} 64 65func (ctx *fuchsia) Parse(output []byte) *Report { 66 rep := &Report{ 67 Output: output, 68 EndPos: len(output), 69 } 70 wantLocation := true 71 if pos := bytes.Index(output, zirconPanic); pos != -1 { 72 rep.Title = string(zirconPanicShort) 73 rep.StartPos = pos 74 } else if pos := bytes.Index(output, zirconKernelHang); pos != -1 { 75 rep.Title = string(zirconKernelHang) 76 rep.StartPos = pos 77 wantLocation = false // these tend to produce random locations 78 } else { 79 return nil 80 } 81 symb := symbolizer.NewSymbolizer() 82 defer symb.Close() 83 where := "" 84 for s := bufio.NewScanner(bytes.NewReader(output[rep.StartPos:])); s.Scan(); { 85 line := s.Bytes() 86 if len(line) == 0 || matchesAny(line, zirconUnrelated) { 87 continue 88 } 89 if bytes.Equal(line, zirconReportEnd) { 90 break 91 } 92 if bytes.Contains(line, []byte("DEBUG ASSERT FAILED")) { 93 rep.Title = "ASSERT FAILED" 94 } 95 if bytes.Contains(line, []byte("Supervisor Page Fault exception")) { 96 rep.Title = "Supervisor fault" 97 } 98 if bytes.Contains(line, []byte("recursion in interrupt handler")) { 99 rep.Title = "recursion in interrupt handler" 100 } 101 if bytes.Contains(line, []byte("double fault")) { 102 rep.Title = "double fault" 103 } 104 if match := zirconRIP.FindSubmatchIndex(line); match != nil { 105 ctx.processPC(rep, symb, line, match, false, &where) 106 } else if match := zirconBT.FindSubmatchIndex(line); match != nil { 107 if ctx.processPC(rep, symb, line, match, true, &where) { 108 continue 109 } 110 } 111 rep.Report = append(rep.Report, line...) 112 rep.Report = append(rep.Report, '\n') 113 } 114 if wantLocation && where != "" { 115 rep.Title = fmt.Sprintf("%v in %v", rep.Title, where) 116 } 117 return rep 118} 119 120func (ctx *fuchsia) processPC(rep *Report, symb *symbolizer.Symbolizer, 121 line []byte, match []int, call bool, where *string) bool { 122 if ctx.obj == "" { 123 return false 124 } 125 prefix := line[match[0]:match[1]] 126 pcStart := match[2] - match[0] 127 pcEnd := match[3] - match[0] 128 pcStr := prefix[pcStart:pcEnd] 129 pc, err := strconv.ParseUint(string(pcStr), 0, 64) 130 if err != nil { 131 return false 132 } 133 shortPC := pc & 0xfffffff 134 pc = 0xffffffff80000000 | shortPC 135 if call { 136 pc-- 137 } 138 frames, err := symb.Symbolize(ctx.obj, pc) 139 if err != nil || len(frames) == 0 { 140 return false 141 } 142 for _, frame := range frames { 143 file := ctx.trimFile(frame.File) 144 name := demangle.Filter(frame.Func, demangle.NoParams, demangle.NoTemplateParams) 145 if strings.Contains(name, "<lambda(") { 146 // demangle produces super long (full) names for lambdas. 147 name = "lambda" 148 } 149 if *where == "" && !matchesAny([]byte(name), zirconSkip) { 150 *where = name 151 } 152 id := "[ inline ]" 153 if !frame.Inline { 154 id = fmt.Sprintf("0x%08x", shortPC) 155 } 156 start := replace(append([]byte{}, prefix...), pcStart, pcEnd, []byte(id)) 157 frameLine := fmt.Sprintf("%s %v %v:%v\n", start, name, file, frame.Line) 158 rep.Report = append(rep.Report, frameLine...) 159 } 160 return true 161} 162 163func (ctx *fuchsia) trimFile(file string) string { 164 const ( 165 prefix1 = "zircon/kernel/" 166 prefix2 = "zircon/" 167 ) 168 if pos := strings.LastIndex(file, prefix1); pos != -1 { 169 return file[pos+len(prefix1):] 170 } 171 if pos := strings.LastIndex(file, prefix2); pos != -1 { 172 return file[pos+len(prefix2):] 173 } 174 return file 175} 176 177func (ctx *fuchsia) Symbolize(rep *Report) error { 178 return nil 179} 180