From 69a06ca2b532ff4021a43fdead4e2ac1452a44c0 Mon Sep 17 00:00:00 2001 From: Joey Jiao Date: Tue, 2 Mar 2021 09:31:32 +0800 Subject: all: add KernelModule cfg to show DLKM coverage PC returned for dynamic loaded module (DLKM) is not parsed in coverage page. So the commit is to use DLKM modules' load address to restore the PC and show coverage data of DLKM. As the load address is written in cfg file, so kaslr needs to be disabled. And for linux target, load address is getting from /proc/modules during instance setup. For either manual or auto address setting case, name and path are needed in config kernel_modules, where name is module name on target. path is module unstripped object path on host. addr is decimal value of module load address on target. Example of config: "kernel_modules": [ { "name": "nf_nat", "path": "/usr/src/linux-source/net/netfilter/nf_nat.ko", "addr": 18446744072637911040 } ] --- pkg/cover/backend/backend.go | 19 +- pkg/cover/backend/elf.go | 360 +++++++++++++++++++++++++++----------- pkg/cover/report.go | 34 +++- pkg/cover/report_test.go | 2 +- pkg/mgrconfig/config.go | 18 ++ pkg/symbolizer/symbolizer.go | 14 +- pkg/symbolizer/symbolizer_test.go | 13 +- syz-manager/cover.go | 2 +- syz-manager/manager.go | 42 +++++ tools/syz-cover/syz-cover.go | 2 +- vm/qemu/qemu.go | 1 + 11 files changed, 384 insertions(+), 123 deletions(-) diff --git a/pkg/cover/backend/backend.go b/pkg/cover/backend/backend.go index 22823021d..bbb526865 100644 --- a/pkg/cover/backend/backend.go +++ b/pkg/cover/backend/backend.go @@ -9,11 +9,17 @@ import ( "github.com/google/syzkaller/sys/targets" ) +type KernelModule struct { + Name string `json:"name"` + Path string `json:"path"` + Addr uint64 `json:"addr"` +} + type Impl struct { Units []*CompileUnit Symbols []*Symbol Frames []Frame - Symbolize func(pcs []uint64) ([]Frame, error) + Symbolize func(pcs []uint64, modules []KernelModule) ([]Frame, error) RestorePC func(pc uint32) uint64 } @@ -38,9 +44,10 @@ type ObjectUnit struct { } type Frame struct { - PC uint64 - Name string - Path string + Module string + PC uint64 + Name string + Path string Range } @@ -53,12 +60,12 @@ type Range struct { const LineEnd = 1 << 30 -func Make(target *targets.Target, vm, objDir, srcDir, buildDir string) (*Impl, error) { +func Make(target *targets.Target, vm, objDir, srcDir, buildDir string, modules []KernelModule) (*Impl, error) { if objDir == "" { return nil, fmt.Errorf("kernel obj directory is not specified") } if vm == "gvisor" { return makeGvisor(target, objDir, srcDir, buildDir) } - return makeELF(target, objDir, srcDir, buildDir) + return makeELF(target, objDir, srcDir, buildDir, modules) } diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go index eba6fd7ca..c1eca5211 100644 --- a/pkg/cover/backend/elf.go +++ b/pkg/cover/backend/elf.go @@ -16,74 +16,115 @@ import ( "sort" "strconv" "strings" + "unsafe" "github.com/google/syzkaller/pkg/osutil" "github.com/google/syzkaller/pkg/symbolizer" "github.com/google/syzkaller/sys/targets" ) -func makeELF(target *targets.Target, objDir, srcDir, buildDir string) (*Impl, error) { - kernelObject := filepath.Join(objDir, target.KernelObject) - file, err := elf.Open(kernelObject) - if err != nil { - return nil, err - } - // Here and below index 0 refers to coverage callbacks (__sanitizer_cov_trace_pc) - // and index 1 refers to comparison callbacks (__sanitizer_cov_trace_cmp*). - var coverPoints [2][]uint64 - var symbols []*Symbol - var textAddr uint64 - errc := make(chan error, 1) - go func() { - symbols1, textAddr1, tracePC, traceCmp, err := readSymbols(file) +const KERNEL string = "kernel" + +func makeELF(target *targets.Target, objDir, srcDir, buildDir string, modules []KernelModule) (*Impl, error) { + var allCoverPoints [2][]uint64 + var allSymbols []*Symbol + var allRanges []pcRange + var allUnits []*CompileUnit + var kernelTextAddr uint64 + + for i := len(modules) - 1; i >= 0; i-- { + module := modules[i] + file, err := elf.Open(module.Path) if err != nil { + return nil, err + } + + // Here and below index 0 refers to coverage callbacks (__sanitizer_cov_trace_pc) + // and index 1 refers to comparison callbacks (__sanitizer_cov_trace_cmp*). + var coverPoints [2][]uint64 + var symbols []*Symbol + var textAddr uint64 + errc := make(chan error, 1) + go func() { + symbols1, textAddr1, tracePC, traceCmp, err := readSymbols(file, module) + if err != nil { + errc <- err + return + } + symbols, textAddr = symbols1, textAddr1 + if target.OS == targets.FreeBSD { + // On FreeBSD .text address in ELF is 0, but .text is actually mapped at 0xffffffff. + textAddr = ^uint64(0) + } + if module.Name == KERNEL { + kernelTextAddr = textAddr + } + if target.Arch == targets.AMD64 { + if module.Name == KERNEL { + coverPoints, err = readCoverPoints(file, tracePC, traceCmp, module) + } else { + coverPoints, err = readCoverPoints(file, 0, nil, module) + } + } else { + coverPoints, err = objdump(target, module.Path) + if module.Name != KERNEL { + for i, pcs := range coverPoints { + for j, pc := range pcs { + coverPoints[i][j] = module.Addr + pc + } + } + } + } errc <- err - return + }() + ranges, units, err := readTextRanges(file, module) + if err != nil { + return nil, err } - symbols, textAddr = symbols1, textAddr1 - if target.OS == targets.FreeBSD { - // On FreeBSD .text address in ELF is 0, but .text is actually mapped at 0xffffffff. - textAddr = ^uint64(0) + if err := <-errc; err != nil { + return nil, err } - if target.Arch == targets.AMD64 { - coverPoints, err = readCoverPoints(file, tracePC, traceCmp) - } else { - coverPoints, err = objdump(target, kernelObject) + + if module.Name == KERNEL && len(coverPoints[0]) == 0 { + return nil, fmt.Errorf("%v doesn't contain coverage callbacks (set CONFIG_KCOV=y)", module.Path) } - errc <- err - }() - ranges, units, err := readTextRanges(file) - if err != nil { - return nil, err - } - if err := <-errc; err != nil { - return nil, err - } - if len(coverPoints[0]) == 0 { - return nil, fmt.Errorf("%v doesn't contain coverage callbacks (set CONFIG_KCOV=y)", kernelObject) + + allCoverPoints[0] = append(allCoverPoints[0], coverPoints[0]...) + allCoverPoints[1] = append(allCoverPoints[1], coverPoints[1]...) + allSymbols = append(allSymbols, symbols...) + allRanges = append(allRanges, ranges...) + allUnits = append(allUnits, units...) } - symbols = buildSymbols(symbols, ranges, coverPoints) + + sort.Slice(allSymbols, func(i, j int) bool { + return allSymbols[i].Start < allSymbols[j].Start + }) + sort.Slice(allRanges, func(i, j int) bool { + return allRanges[i].start < allRanges[j].start + }) + + allSymbols = buildSymbols(allSymbols, allRanges, allCoverPoints) nunit := 0 - for _, unit := range units { + for _, unit := range allUnits { if len(unit.PCs) == 0 { continue // drop the unit } unit.Name, unit.Path = cleanPath(unit.Name, objDir, srcDir, buildDir) - units[nunit] = unit + allUnits[nunit] = unit nunit++ } - units = units[:nunit] - if len(symbols) == 0 || len(units) == 0 { + allUnits = allUnits[:nunit] + if len(allSymbols) == 0 || len(allUnits) == 0 { return nil, fmt.Errorf("failed to parse DWARF (set CONFIG_DEBUG_INFO=y?)") } impl := &Impl{ - Units: units, - Symbols: symbols, - Symbolize: func(pcs []uint64) ([]Frame, error) { - return symbolize(target, objDir, srcDir, buildDir, kernelObject, pcs) + Units: allUnits, + Symbols: allSymbols, + Symbolize: func(pcs []uint64, modules []KernelModule) ([]Frame, error) { + return symbolize(target, objDir, srcDir, buildDir, modules, pcs) }, RestorePC: func(pc uint32) uint64 { - return PreviousInstructionPC(target, RestorePC(pc, uint32(textAddr>>32))) + return PreviousInstructionPC(target, RestorePC(pc, uint32(kernelTextAddr>>32))) }, } return impl, nil @@ -155,7 +196,7 @@ func buildSymbols(symbols []*Symbol, ranges []pcRange, coverPoints [2][]uint64) return symbols } -func readSymbols(file *elf.File) ([]*Symbol, uint64, uint64, map[uint64]bool, error) { +func readSymbols(file *elf.File, module KernelModule) ([]*Symbol, uint64, uint64, map[uint64]bool, error) { text := file.Section(".text") if text == nil { return nil, 0, 0, nil, fmt.Errorf("no .text section in the object file") @@ -171,14 +212,20 @@ func readSymbols(file *elf.File) ([]*Symbol, uint64, uint64, map[uint64]bool, er if symb.Value < text.Addr || symb.Value+symb.Size > text.Addr+text.Size { continue } + var start uint64 + if module.Name == KERNEL { + start = symb.Value + } else { + start = symb.Value + module.Addr + } symbols = append(symbols, &Symbol{ ObjectUnit: ObjectUnit{ Name: symb.Name, }, - Start: symb.Value, - End: symb.Value + symb.Size, + Start: start, + End: start + symb.Size, }) - if strings.HasPrefix(symb.Name, "__sanitizer_cov_trace_") { + if module.Name == KERNEL && strings.HasPrefix(symb.Name, "__sanitizer_cov_trace_") { if symb.Name == "__sanitizer_cov_trace_pc" { tracePC = symb.Value } else { @@ -186,7 +233,7 @@ func readSymbols(file *elf.File) ([]*Symbol, uint64, uint64, map[uint64]bool, er } } } - if tracePC == 0 { + if module.Name == KERNEL && tracePC == 0 { return nil, 0, 0, nil, fmt.Errorf("no __sanitizer_cov_trace_pc symbol in the object file") } sort.Slice(symbols, func(i, j int) bool { @@ -195,7 +242,7 @@ func readSymbols(file *elf.File) ([]*Symbol, uint64, uint64, map[uint64]bool, er return symbols, text.Addr, tracePC, traceCmp, nil } -func readTextRanges(file *elf.File) ([]pcRange, []*CompileUnit, error) { +func readTextRanges(file *elf.File, module KernelModule) ([]pcRange, []*CompileUnit, error) { text := file.Section(".text") if text == nil { return nil, nil, fmt.Errorf("no .text section in the object file") @@ -249,7 +296,11 @@ func readTextRanges(file *elf.File) ([]pcRange, []*CompileUnit, error) { } } } - ranges = append(ranges, pcRange{r[0], r[1], unit}) + if module.Name == KERNEL { + ranges = append(ranges, pcRange{r[0], r[1], unit}) + } else { + ranges = append(ranges, pcRange{r[0] + module.Addr, r[1] + module.Addr, unit}) + } } r.SkipChildren() } @@ -259,7 +310,36 @@ func readTextRanges(file *elf.File) ([]pcRange, []*CompileUnit, error) { return ranges, units, nil } -func symbolize(target *targets.Target, objDir, srcDir, buildDir, obj string, pcs []uint64) ([]Frame, error) { +func groupPCsByModule(pcs []uint64, modules []KernelModule) map[string][]uint64 { + groupPCs := make(map[string][]uint64) + for _, module := range modules { + groupPCs[module.Name] = make([]uint64, 0) + } + for _, pc := range pcs { + if pc > modules[0].Addr { + if modules[0].Name == KERNEL { + groupPCs[modules[0].Name] = append(groupPCs[modules[0].Name], pc) + } else { + groupPCs[modules[0].Name] = append(groupPCs[modules[0].Name], pc-modules[0].Addr) + } + } else { + for i := 0; i < len(modules)-1; i++ { + if pc < modules[i].Addr && pc >= modules[i+1].Addr { + if modules[i+1].Name == KERNEL { + groupPCs[modules[i+1].Name] = append(groupPCs[modules[i+1].Name], pc) + } else { + groupPCs[modules[i+1].Name] = append(groupPCs[modules[i+1].Name], pc-modules[i+1].Addr) + } + break + } + } + } + } + return groupPCs +} + +func symbolize(target *targets.Target, objDir, srcDir, buildDir string, + modules []KernelModule, pcs []uint64) ([]Frame, error) { procs := runtime.GOMAXPROCS(0) / 2 if need := len(pcs) / 1000; procs > need { procs = need @@ -280,31 +360,46 @@ func symbolize(target *targets.Target, objDir, srcDir, buildDir, obj string, pcs err error } symbolizerC := make(chan symbolizerResult, procs) - pcchan := make(chan []uint64, procs) - for p := 0; p < procs; p++ { - go func() { - symb := symbolizer.NewSymbolizer(target) - defer symb.Close() - var res symbolizerResult - for pcs := range pcchan { - frames, err := symb.SymbolizeArray(obj, pcs) - if err != nil { - res.err = fmt.Errorf("failed to symbolize: %v", err) + + groupPCs := groupPCsByModule(pcs, modules) + for name, pcs := range groupPCs { + if len(pcs) == 0 { + continue + } + var module KernelModule + for _, m := range modules { + if name == m.Name { + module = m + break + } + } + pcchan := make(chan []uint64, procs) + for p := 0; p < procs; p++ { + go func() { + symb := symbolizer.NewSymbolizer(target) + defer symb.Close() + var res symbolizerResult + for pcs := range pcchan { + frames, err := symb.SymbolizeArray(module.Name, module.Path, pcs) + if err != nil { + res.err = fmt.Errorf("failed to symbolize: %v", err) + } + res.frames = append(res.frames, frames...) } - res.frames = append(res.frames, frames...) + symbolizerC <- res + }() + } + for i := 0; i < len(pcs); { + end := i + 100 + if end > len(pcs) { + end = len(pcs) } - symbolizerC <- res - }() - } - for i := 0; i < len(pcs); { - end := i + 100 - if end > len(pcs) { - end = len(pcs) + pcchan <- pcs[i:end] + i = end } - pcchan <- pcs[i:end] - i = end + close(pcchan) } - close(pcchan) + var err0 error var frames []Frame for p := 0; p < procs; p++ { @@ -315,9 +410,10 @@ func symbolize(target *targets.Target, objDir, srcDir, buildDir, obj string, pcs for _, frame := range res.frames { name, path := cleanPath(frame.File, objDir, srcDir, buildDir) frames = append(frames, Frame{ - PC: frame.PC, - Name: name, - Path: path, + Module: frame.Module, + PC: frame.PC, + Name: name, + Path: path, Range: Range{ StartLine: frame.Line, StartCol: 0, @@ -333,35 +429,99 @@ func symbolize(target *targets.Target, objDir, srcDir, buildDir, obj string, pcs return frames, nil } -// readCoverPoints finds all coverage points (calls of __sanitizer_cov_trace_pc) in the object file. -// Currently it is amd64-specific: looks for e8 opcode and correct offset. -// Running objdump on the whole object file is too slow. -func readCoverPoints(file *elf.File, tracePC uint64, traceCmp map[uint64]bool) ([2][]uint64, error) { - var pcs [2][]uint64 - text := file.Section(".text") - if text == nil { - return pcs, fmt.Errorf("no .text section in the object file") +func getRelocations(file *elf.File) ([]elf.Rela64, error) { + var allRels []elf.Rela64 + + for _, s := range file.Sections { + if s.Type == 4 { + var rel elf.Rela64 + r := s.Open() + rels := make([]elf.Rela64, s.Size/uint64(unsafe.Sizeof(rel))) + if err := binary.Read(r, binary.LittleEndian, rels); err != nil { + return allRels, err + } + allRels = append(allRels, rels...) + } + } + return allRels, nil +} + +func getRelSymbolName(file *elf.File, index uint32) (string, error) { + symbols, err := file.Symbols() + if err != nil { + return "", err } - data, err := text.Data() + if uint64(index-1) < uint64(len(symbols)) { + return symbols[index-1].Name, nil + } + return "", fmt.Errorf("out of array access index") +} + +func getCovRels(file *elf.File) ([2][]elf.Rela64, error) { + var rRels [2][]elf.Rela64 + rels, err := getRelocations(file) if err != nil { - return pcs, fmt.Errorf("failed to read .text: %v", err) + return rRels, err } + for _, rel := range rels { + name, err := getRelSymbolName(file, elf.R_SYM64(rel.Info)) + if err != nil { + return rRels, err + } + if strings.Contains(name, "__sanitizer_cov_trace_pc") { + rRels[0] = append(rRels[0], rel) + } else if strings.Contains(name, "__sanitizer_cov_trace_") { + rRels[1] = append(rRels[1], rel) + } + } + return rRels, nil +} + +// readCoverPoints finds all coverage points (calls of __sanitizer_cov_trace_*) in the object file. +// Currently it is amd64-specific: looks for e8 opcode and correct offset. +// Running objdump on the whole object file is too slow. +func readCoverPoints(file *elf.File, tracePC uint64, traceCmp map[uint64]bool, + module KernelModule) ([2][]uint64, error) { + var pcs [2][]uint64 const callLen = 5 - end := len(data) - callLen + 1 - for i := 0; i < end; i++ { - pos := bytes.IndexByte(data[i:end], 0xe8) - if pos == -1 { - break + if module.Name == KERNEL { + text := file.Section(".text") + if text == nil { + return pcs, fmt.Errorf("no .text section in the object file") } - pos += i - i = pos - off := uint64(int64(int32(binary.LittleEndian.Uint32(data[pos+1:])))) - pc := text.Addr + uint64(pos) - target := pc + off + callLen - if target == tracePC { - pcs[0] = append(pcs[0], pc) - } else if traceCmp[target] { - pcs[1] = append(pcs[1], pc) + data, err := text.Data() + if err != nil { + return pcs, fmt.Errorf("failed to read .text: %v", err) + } + end := len(data) - callLen + 1 + for i := 0; i < end; i++ { + pos := bytes.IndexByte(data[i:end], 0xe8) + if pos == -1 { + break + } + pos += i + i = pos + off := uint64(int64(int32(binary.LittleEndian.Uint32(data[pos+1:])))) + pc := text.Addr + uint64(pos) + target := pc + off + callLen + if target == tracePC { + pcs[0] = append(pcs[0], pc) + } else if traceCmp[target] { + pcs[1] = append(pcs[1], pc) + } + } + } else { + _ = tracePC + _ = traceCmp + rels, err := getCovRels(file) + if err != nil { + return pcs, err + } + for _, rel := range rels[0] { + pcs[0] = append(pcs[0], module.Addr+rel.Off-1) + } + for _, rel := range rels[1] { + pcs[1] = append(pcs[1], module.Addr+rel.Off-1) } } return pcs, nil diff --git a/pkg/cover/report.go b/pkg/cover/report.go index d8f2115e3..bd2c25e66 100644 --- a/pkg/cover/report.go +++ b/pkg/cover/report.go @@ -5,6 +5,7 @@ package cover import ( "fmt" + "path/filepath" "sort" "github.com/google/syzkaller/pkg/cover/backend" @@ -17,6 +18,7 @@ type ReportGenerator struct { objDir string buildDir string subsystem []Subsystem + modules []backend.KernelModule *backend.Impl } @@ -28,8 +30,18 @@ type Prog struct { var RestorePC = backend.RestorePC func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir string, - subsystem []Subsystem) (*ReportGenerator, error) { - impl, err := backend.Make(target, vm, objDir, srcDir, buildDir) + subsystem []Subsystem, modules []backend.KernelModule) (*ReportGenerator, error) { + kernelObject := filepath.Join(objDir, target.KernelObject) + modules = append(modules, backend.KernelModule{ + Name: "kernel", + Addr: 0, + Path: kernelObject, + }) + sort.Slice(modules, func(i, j int) bool { + return modules[i].Addr > modules[j].Addr + }) + + impl, err := backend.Make(target, vm, objDir, srcDir, buildDir, modules) if err != nil { return nil, err } @@ -43,6 +55,7 @@ func MakeReportGenerator(target *targets.Target, vm, objDir, srcDir, buildDir st objDir: objDir, buildDir: buildDir, subsystem: subsystem, + modules: modules, Impl: impl, } return rg, nil @@ -94,7 +107,20 @@ func (rg *ReportGenerator) prepareFileMap(progs []Prog) (map[string]*file, error for _, frame := range rg.Frames { f := getFile(files, frame.Name, frame.Path) ln := f.lines[frame.StartLine] - coveredBy := progPCs[frame.PC] + var pc uint64 + if frame.Module == backend.KERNEL { + pc = frame.PC + } else { + idx := 0 + for i, module := range rg.modules { + if module.Name == frame.Module { + idx = i + break + } + } + pc = frame.PC + rg.modules[idx].Addr + } + coveredBy := progPCs[pc] if len(coveredBy) == 0 { f.uncovered = append(f.uncovered, frame.Range) continue @@ -175,7 +201,7 @@ func (rg *ReportGenerator) lazySymbolize(progs []Prog) error { if len(pcs) == 0 { return nil } - frames, err := rg.Symbolize(pcs) + frames, err := rg.Symbolize(pcs, rg.modules) if err != nil { return err } diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go index 0707eab64..b1962c599 100644 --- a/pkg/cover/report_test.go +++ b/pkg/cover/report_test.go @@ -190,7 +190,7 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, [] }, } - rg, err := MakeReportGenerator(target, "", dir, dir, dir, subsystem) + rg, err := MakeReportGenerator(target, "", dir, dir, dir, subsystem, nil) if err != nil { return nil, nil, err } diff --git a/pkg/mgrconfig/config.go b/pkg/mgrconfig/config.go index e4bfc46f8..a5ab47590 100644 --- a/pkg/mgrconfig/config.go +++ b/pkg/mgrconfig/config.go @@ -5,7 +5,9 @@ package mgrconfig import ( "encoding/json" + "github.com/google/syzkaller/pkg/cover" + "github.com/google/syzkaller/pkg/cover/backend" ) type Config struct { @@ -40,6 +42,22 @@ type Config struct { // Directory with kernel object files (e.g. `vmlinux` for linux) // (used for report symbolization and coverage reports, optional). KernelObj string `json:"kernel_obj"` + // Map of kernel modules' symbol files and load address. + // path is unstripped module obj path and + // addr (uint64) is the module load address on target, + // like 'button 16384 0 - Live 0xffffffffc0163000', + // addr is 18446744072637263872 of 0xffffffffc0163000 above. + // For linux target, the addr is getting from /proc/modules + // automatically during instance run. + // Example config: + // "kernel_modules": [ + // { + // "name": "wlan", + // "path": "../../wlan.ko.unstripped", + // "addr": 18446744072637911040 + // } + // ] + KernelModules []backend.KernelModule `json:"kernel_modules"` // Kernel source directory (if not set defaults to KernelObj). KernelSrc string `json:"kernel_src,omitempty"` // Location of the driectory where the kernel was built (if not set defaults to KernelSrc) diff --git a/pkg/symbolizer/symbolizer.go b/pkg/symbolizer/symbolizer.go index 96b62fb95..87342e750 100644 --- a/pkg/symbolizer/symbolizer.go +++ b/pkg/symbolizer/symbolizer.go @@ -23,6 +23,7 @@ type Symbolizer struct { } type Frame struct { + Module string PC uint64 Func string File string @@ -43,15 +44,15 @@ func NewSymbolizer(target *targets.Target) *Symbolizer { } func (s *Symbolizer) Symbolize(bin string, pc uint64) ([]Frame, error) { - return s.SymbolizeArray(bin, []uint64{pc}) + return s.SymbolizeArray("kernel", bin, []uint64{pc}) } -func (s *Symbolizer) SymbolizeArray(bin string, pcs []uint64) ([]Frame, error) { +func (s *Symbolizer) SymbolizeArray(module, bin string, pcs []uint64) ([]Frame, error) { sub, err := s.getSubprocess(bin) if err != nil { return nil, err } - return symbolize(sub.input, sub.scanner, pcs) + return symbolize(sub.input, sub.scanner, pcs, module) } func (s *Symbolizer) Close() { @@ -100,7 +101,7 @@ func (s *Symbolizer) getSubprocess(bin string) (*subprocess, error) { return sub, nil } -func symbolize(input *bufio.Writer, scanner *bufio.Scanner, pcs []uint64) ([]Frame, error) { +func symbolize(input *bufio.Writer, scanner *bufio.Scanner, pcs []uint64, module string) ([]Frame, error) { var frames []Frame done := make(chan error, 1) go func() { @@ -116,7 +117,7 @@ func symbolize(input *bufio.Writer, scanner *bufio.Scanner, pcs []uint64) ([]Fra } for range pcs { var frames1 []Frame - frames1, err = parse(scanner) + frames1, err = parse(scanner, module) if err != nil { return } @@ -145,7 +146,7 @@ func symbolize(input *bufio.Writer, scanner *bufio.Scanner, pcs []uint64) ([]Fra return frames, nil } -func parse(s *bufio.Scanner) ([]Frame, error) { +func parse(s *bufio.Scanner, module string) ([]Frame, error) { pc, err := strconv.ParseUint(s.Text(), 0, 64) if err != nil { return nil, fmt.Errorf("failed to parse pc '%v' in addr2line output: %v", s.Text(), err) @@ -182,6 +183,7 @@ func parse(s *bufio.Scanner) ([]Frame, error) { continue } frames = append(frames, Frame{ + Module: module, PC: pc, Func: fn, File: file, diff --git a/pkg/symbolizer/symbolizer_test.go b/pkg/symbolizer/symbolizer_test.go index 0bddb2f71..cdbef8e6c 100644 --- a/pkg/symbolizer/symbolizer_test.go +++ b/pkg/symbolizer/symbolizer_test.go @@ -25,6 +25,7 @@ func TestParse(t *testing.T) { "mm/kasan/report.c:320\n", []Frame{ { + Module: "kernel", PC: 0xffffffff8180a42e, Func: "__asan_report_load2_noabort", File: "mm/kasan/report.c", @@ -42,6 +43,7 @@ func TestParse(t *testing.T) { "mm/kasan/report.c:320\n", []Frame{ { + Module: "kernel", PC: 0xffffffff8180a42d, Func: "kasan_report", File: "mm/kasan/report.c", @@ -49,6 +51,7 @@ func TestParse(t *testing.T) { Inline: true, }, { + Module: "kernel", PC: 0xffffffff8180a42d, Func: "__asan_report_load2_noabort", File: "mm/kasan/report.c", @@ -64,6 +67,7 @@ func TestParse(t *testing.T) { "drivers/video/console/fbcon.c:2750\n", []Frame{ { + Module: "kernel", PC: 0xffffffff82fdbe0b, Func: "fbcon_invert_region", File: "drivers/video/console/fbcon.c", @@ -93,6 +97,7 @@ func TestParse(t *testing.T) { "fs/devpts/inode.c:588 (discriminator 3)\n", []Frame{ { + Module: "kernel", PC: 0xffffffff81a2aff9, Func: "devpts_get_priv", File: "fs/devpts/inode.c", @@ -153,7 +158,7 @@ func TestParse(t *testing.T) { var allPCs []uint64 var allFrames []Frame for _, addr := range addresses { - frames, err := symbolize(input, scanner, []uint64{addr.pc}) + frames, err := symbolize(input, scanner, []uint64{addr.pc}, "kernel") if err != nil { t.Fatalf("got error: %v", err) } @@ -166,11 +171,11 @@ func TestParse(t *testing.T) { // Symbolize PCs in 2 groups. for i := 0; i <= len(addresses); i++ { - frames, err := symbolize(input, scanner, allPCs[:i]) + frames, err := symbolize(input, scanner, allPCs[:i], "kernel") if err != nil { t.Fatalf("got error: %v", err) } - frames2, err := symbolize(input, scanner, allPCs[i:]) + frames2, err := symbolize(input, scanner, allPCs[i:], "kernel") if err != nil { t.Fatalf("got error: %v", err) } @@ -185,7 +190,7 @@ func TestParse(t *testing.T) { for i := range lots { lots[i] = addresses[0].pc } - frames, err := symbolize(input, scanner, lots) + frames, err := symbolize(input, scanner, lots, "kernel") if err != nil { t.Fatalf("got error: %v", err) } diff --git a/syz-manager/cover.go b/syz-manager/cover.go index 1d33b4d91..27def6f5d 100644 --- a/syz-manager/cover.go +++ b/syz-manager/cover.go @@ -19,7 +19,7 @@ var getReportGenerator = func() func(cfg *mgrconfig.Config) (*cover.ReportGenera once.Do(func() { log.Logf(0, "initializing coverage information...") rg, err = cover.MakeReportGenerator(cfg.SysTarget, cfg.Type, cfg.KernelObj, cfg.KernelSrc, - cfg.KernelBuildSrc, cfg.KernelSubsystem) + cfg.KernelBuildSrc, cfg.KernelSubsystem, cfg.KernelModules) }) return rg, err } diff --git a/syz-manager/manager.go b/syz-manager/manager.go index ca28efdac..d870f7bb0 100644 --- a/syz-manager/manager.go +++ b/syz-manager/manager.go @@ -14,6 +14,7 @@ import ( "os" "os/exec" "path/filepath" + "strings" "sync" "sync/atomic" "time" @@ -622,6 +623,47 @@ func (mgr *Manager) runInstanceInner(index int, instanceName string) (*report.Re procs = 1 } + if mgr.sysTarget.OS == "linux" { + var rawBytes []byte + waitForOutput := func(outc <-chan []byte) { + timer := time.NewTimer(10 * time.Second).C + for { + select { + case out, ok := <-outc: + if !ok { + return + } + rawBytes = append(rawBytes, out...) + case <-timer: + return + } + } + } + outC, _, _ := inst.Run(time.Hour, mgr.vmStop, "cat /proc/modules") + waitForOutput(outC) + lines := strings.Split(string(rawBytes), "\n") + + for _, line := range lines { + if line == "" { + continue + } + tokens := strings.Fields(line) + if len(tokens) >= 6 { + var addr uint64 + _, err := fmt.Sscanf(strings.TrimPrefix(tokens[5], "0x"), "%x", &addr) + if err != nil { + continue + } + for i := 0; i < len(mgr.cfg.KernelModules); i++ { + if mgr.cfg.KernelModules[i].Name == tokens[0] { + mgr.cfg.KernelModules[i].Addr = addr + break + } + } + } + } + } + // Run the fuzzer binary. start := time.Now() atomic.AddUint32(&mgr.numFuzzing, 1) diff --git a/tools/syz-cover/syz-cover.go b/tools/syz-cover/syz-cover.go index 4c69b6c09..6e16b828d 100644 --- a/tools/syz-cover/syz-cover.go +++ b/tools/syz-cover/syz-cover.go @@ -67,7 +67,7 @@ func main() { if err != nil { tool.Fail(err) } - rg, err := cover.MakeReportGenerator(target, *flagVM, *flagKernelObj, *flagKernelSrc, *flagKernelBuildSrc, nil) + rg, err := cover.MakeReportGenerator(target, *flagVM, *flagKernelObj, *flagKernelSrc, *flagKernelBuildSrc, nil, nil) if err != nil { tool.Fail(err) } diff --git a/vm/qemu/qemu.go b/vm/qemu/qemu.go index 5ada86950..a7ee82926 100644 --- a/vm/qemu/qemu.go +++ b/vm/qemu/qemu.go @@ -598,6 +598,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin } } else { args = []string{"ssh"} + args = append(args, "-q") args = append(args, sshArgs...) args = append(args, inst.sshuser+"@localhost", "cd "+inst.targetDir()+" && "+command) } -- cgit mrf-deployment