aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkg/cover/backend/dwarf.go24
-rw-r--r--pkg/cover/backend/elf.go14
-rw-r--r--pkg/cover/html.go100
-rw-r--r--pkg/cover/report_test.go8
-rw-r--r--syz-manager/html.go105
-rw-r--r--tools/syz-cover/syz-cover.go4
6 files changed, 153 insertions, 102 deletions
diff --git a/pkg/cover/backend/dwarf.go b/pkg/cover/backend/dwarf.go
index 23a1e8ce9..7a9abd221 100644
--- a/pkg/cover/backend/dwarf.go
+++ b/pkg/cover/backend/dwarf.go
@@ -7,6 +7,7 @@ import (
"bufio"
"bytes"
"debug/dwarf"
+ "debug/elf"
"encoding/binary"
"fmt"
"io/ioutil"
@@ -30,25 +31,30 @@ type containerFns struct {
}
type Arch struct {
- callLen int
- opcodeOffset int
- opcodes [2]byte
- target func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64
+ callLen int
+ relaOffset uint64
+ opcodeOffset int
+ opcodes [2]byte
+ callRelocType uint64
+ target func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64
}
var arches = map[string]Arch{
targets.AMD64: {
- callLen: 5,
- opcodes: [2]byte{0xe8, 0xe8},
+ callLen: 5,
+ relaOffset: 1,
+ opcodes: [2]byte{0xe8, 0xe8},
+ callRelocType: uint64(elf.R_X86_64_PLT32),
target: func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 {
off := uint64(int64(int32(binary.LittleEndian.Uint32(insn[1:]))))
return pc + off + uint64(arch.callLen)
},
},
targets.ARM64: {
- callLen: 4,
- opcodeOffset: 3,
- opcodes: [2]byte{0x94, 0x97},
+ callLen: 4,
+ opcodeOffset: 3,
+ opcodes: [2]byte{0x94, 0x97},
+ callRelocType: uint64(elf.R_AARCH64_CALL26),
target: func(arch *Arch, insn []byte, pc uint64, opcode byte) uint64 {
off := uint64(binary.LittleEndian.Uint32(insn)) & ((1 << 24) - 1)
if opcode == arch.opcodes[1] {
diff --git a/pkg/cover/backend/elf.go b/pkg/cover/backend/elf.go
index 94e6fdd8c..227356eef 100644
--- a/pkg/cover/backend/elf.go
+++ b/pkg/cover/backend/elf.go
@@ -40,7 +40,9 @@ func elfReadSymbols(module *Module, info *symbolInfo) ([]*Symbol, error) {
if err != nil {
return nil, fmt.Errorf("failed to read ELF symbols: %v", err)
}
- info.textAddr = text.Addr
+ if module.Name == "" {
+ info.textAddr = text.Addr
+ }
var symbols []*Symbol
for i, symb := range allSymbols {
text := symb.Value >= text.Addr && symb.Value+symb.Size <= text.Addr+text.Size
@@ -133,7 +135,8 @@ func elfReadModuleCoverPoints(target *targets.Target, module *Module, info *symb
if err != nil {
return pcs, err
}
- offset := uint64(arches[target.Arch].opcodeOffset)
+ callRelocType := arches[target.Arch].callRelocType
+ relaOffset := arches[target.Arch].relaOffset
for _, s := range file.Sections {
if s.Type != elf.SHT_RELA { // nolint: misspell
continue
@@ -146,10 +149,11 @@ func elfReadModuleCoverPoints(target *targets.Target, module *Module, info *symb
}
return pcs, err
}
- // Note: this assumes that call instruction is 1 byte.
- pc := module.Addr + rel.Off - 1
+ if (rel.Info & 0xffffffff) != callRelocType {
+ continue
+ }
+ pc := module.Addr + rel.Off - relaOffset
index := int(elf.R_SYM64(rel.Info)) - 1
- pc -= offset
if info.tracePCIdx[index] {
pcs[0] = append(pcs[0], pc)
} else if info.traceCmpIdx[index] {
diff --git a/pkg/cover/html.go b/pkg/cover/html.go
index 63b1506c1..7afe8b54c 100644
--- a/pkg/cover/html.go
+++ b/pkg/cover/html.go
@@ -21,9 +21,43 @@ import (
"github.com/google/syzkaller/pkg/cover/backend"
"github.com/google/syzkaller/pkg/mgrconfig"
+ "github.com/google/syzkaller/sys/targets"
)
-func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error {
+func fixUpPCs(target string, progs []Prog, coverFilter map[uint32]uint32) []Prog {
+ if coverFilter != nil {
+ for _, prog := range progs {
+ var nPCs []uint64
+ for _, pc := range prog.PCs {
+ if coverFilter[uint32(pc)] != 0 {
+ nPCs = append(nPCs, pc)
+ }
+ }
+ prog.PCs = nPCs
+ }
+ }
+
+ // On arm64 as PLT is enabled by default, .text section is loaded after .plt section,
+ // so there is 0x18 bytes offset from module load address for .text section
+ // we need to remove the 0x18 bytes offset in order to correct module symbol address
+ if target == targets.ARM64 {
+ for _, prog := range progs {
+ var nPCs []uint64
+ for _, pc := range prog.PCs {
+ // TODO: avoid to hardcode the address
+ if pc < 0xffffffd010000000 {
+ pc -= 0x18
+ }
+ nPCs = append(nPCs, pc)
+ }
+ prog.PCs = nPCs
+ }
+ }
+ return progs
+}
+
+func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
files, err := rg.prepareFileMap(progs)
if err != nil {
return err
@@ -106,7 +140,8 @@ func (rg *ReportGenerator) DoHTML(w io.Writer, progs []Prog) error {
return coverTemplate.Execute(w, d)
}
-func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog) error {
+func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) error {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
if err := rg.lazySymbolize(progs); err != nil {
return err
}
@@ -125,6 +160,58 @@ func (rg *ReportGenerator) DoRawCoverFiles(w http.ResponseWriter, progs []Prog)
return nil
}
+func (rg *ReportGenerator) DoRawCover(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
+ var pcs []uint64
+ uniquePCs := make(map[uint64]bool)
+ for _, prog := range progs {
+ for _, pc := range prog.PCs {
+ if uniquePCs[pc] {
+ continue
+ }
+ uniquePCs[pc] = true
+ pcs = append(pcs, pc)
+ }
+ }
+ sort.Slice(pcs, func(i, j int) bool {
+ return pcs[i] < pcs[j]
+ })
+
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ buf := bufio.NewWriter(w)
+ for _, pc := range pcs {
+ fmt.Fprintf(buf, "0x%x\n", pc)
+ }
+ buf.Flush()
+}
+
+func (rg *ReportGenerator) DoFilterPCs(w http.ResponseWriter, progs []Prog, coverFilter map[uint32]uint32) {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
+ var pcs []uint64
+ uniquePCs := make(map[uint64]bool)
+ for _, prog := range progs {
+ for _, pc := range prog.PCs {
+ if uniquePCs[pc] {
+ continue
+ }
+ uniquePCs[pc] = true
+ if coverFilter[uint32(pc)] != 0 {
+ pcs = append(pcs, pc)
+ }
+ }
+ }
+ sort.Slice(pcs, func(i, j int) bool {
+ return pcs[i] < pcs[j]
+ })
+
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ buf := bufio.NewWriter(w)
+ for _, pc := range pcs {
+ fmt.Fprintf(buf, "0x%x\n", pc)
+ }
+ buf.Flush()
+}
+
type fileStats struct {
Name string
CoveredLines int
@@ -200,7 +287,8 @@ func (rg *ReportGenerator) convertToStats(progs []Prog) ([]fileStats, error) {
return data, nil
}
-func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog) error {
+func (rg *ReportGenerator) DoCSVFiles(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
data, err := rg.convertToStats(progs)
if err != nil {
return err
@@ -298,7 +386,8 @@ func groupCoverByFilePrefixes(datas []fileStats, subsystems []mgrconfig.Subsyste
return d
}
-func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog) error {
+func (rg *ReportGenerator) DoHTMLTable(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
data, err := rg.convertToStats(progs)
if err != nil {
return err
@@ -316,7 +405,8 @@ var csvHeader = []string{
"Total PCs",
}
-func (rg *ReportGenerator) DoCSV(w io.Writer, progs []Prog) error {
+func (rg *ReportGenerator) DoCSV(w io.Writer, progs []Prog, coverFilter map[uint32]uint32) error {
+ progs = fixUpPCs(rg.target.Arch, progs, coverFilter)
files, err := rg.prepareFileMap(progs)
if err != nil {
return err
diff --git a/pkg/cover/report_test.go b/pkg/cover/report_test.go
index 9b6b9e56c..3461fcf58 100644
--- a/pkg/cover/report_test.go
+++ b/pkg/cover/report_test.go
@@ -224,20 +224,20 @@ func generateReport(t *testing.T, target *targets.Target, test Test) ([]byte, []
test.Progs = append(test.Progs, Prog{Data: "main", PCs: pcs})
}
html := new(bytes.Buffer)
- if err := rg.DoHTML(html, test.Progs); err != nil {
+ if err := rg.DoHTML(html, test.Progs, nil); err != nil {
return nil, nil, err
}
htmlTable := new(bytes.Buffer)
- if err := rg.DoHTMLTable(htmlTable, test.Progs); err != nil {
+ if err := rg.DoHTMLTable(htmlTable, test.Progs, nil); err != nil {
return nil, nil, err
}
_ = htmlTable
csv := new(bytes.Buffer)
- if err := rg.DoCSV(csv, test.Progs); err != nil {
+ if err := rg.DoCSV(csv, test.Progs, nil); err != nil {
return nil, nil, err
}
csvFiles := new(bytes.Buffer)
- if err := rg.DoCSVFiles(csvFiles, test.Progs); err != nil {
+ if err := rg.DoCSVFiles(csvFiles, test.Progs, nil); err != nil {
return nil, nil, err
}
_ = csvFiles
diff --git a/syz-manager/html.go b/syz-manager/html.go
index 26b611aa1..46c062752 100644
--- a/syz-manager/html.go
+++ b/syz-manager/html.go
@@ -4,7 +4,6 @@
package main
import (
- "bufio"
"bytes"
"encoding/json"
"fmt"
@@ -242,6 +241,8 @@ const (
DoCSV
DoCSVFiles
DoRawCoverFiles
+ DoRawCover
+ DoFilterPCs
)
func (mgr *Manager) httpCover(w http.ResponseWriter, r *http.Request) {
@@ -279,23 +280,12 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
}
mgr.mu.Lock()
- convert := coverToPCs
- if r.FormValue("filter") != "" && mgr.coverFilter != nil {
- convert = func(rg *cover.ReportGenerator, cover []uint32) (ret []uint64) {
- for _, pc := range coverToPCs(rg, cover) {
- if mgr.coverFilter[uint32(pc)] != 0 {
- ret = append(ret, pc)
- }
- }
- return ret
- }
- }
var progs []cover.Prog
if sig := r.FormValue("input"); sig != "" {
inp := mgr.corpus[sig]
progs = append(progs, cover.Prog{
Data: string(inp.Prog),
- PCs: convert(rg, inp.Cover),
+ PCs: coverToPCs(rg, inp.Cover),
})
} else {
call := r.FormValue("call")
@@ -305,12 +295,32 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
}
progs = append(progs, cover.Prog{
Data: string(inp.Prog),
- PCs: convert(rg, inp.Cover),
+ PCs: coverToPCs(rg, inp.Cover),
})
}
}
mgr.mu.Unlock()
+ var coverFilter map[uint32]uint32
+ if r.FormValue("filter") != "" {
+ coverFilter = mgr.coverFilter
+ }
+
+ if funcFlag == DoRawCoverFiles {
+ if err := rg.DoRawCoverFiles(w, progs, coverFilter); err != nil {
+ http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
+ return
+ }
+ runtime.GC()
+ return
+ } else if funcFlag == DoRawCover {
+ rg.DoRawCover(w, progs, coverFilter)
+ return
+ } else if funcFlag == DoFilterPCs {
+ rg.DoFilterPCs(w, progs, coverFilter)
+ return
+ }
+
do := rg.DoHTML
if funcFlag == DoHTMLTable {
do = rg.DoHTMLTable
@@ -318,15 +328,9 @@ func (mgr *Manager) httpCoverCover(w http.ResponseWriter, r *http.Request, funcF
do = rg.DoCSV
} else if funcFlag == DoCSVFiles {
do = rg.DoCSVFiles
- } else if funcFlag == DoRawCoverFiles {
- if err := rg.DoRawCoverFiles(w, progs); err != nil {
- http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
- return
- }
- runtime.GC()
- return
}
- if err := do(w, progs); err != nil {
+
+ if err := do(w, progs, coverFilter); err != nil {
http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
return
}
@@ -469,31 +473,7 @@ func (mgr *Manager) httpReport(w http.ResponseWriter, r *http.Request) {
}
func (mgr *Manager) httpRawCover(w http.ResponseWriter, r *http.Request) {
- // Note: initCover is executed without mgr.mu because it takes very long time
- // (but it only reads config and it protected by initCoverOnce).
- rg, err := getReportGenerator(mgr.cfg, mgr.modules)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
-
- var cov cover.Cover
- for _, inp := range mgr.corpus {
- cov.Merge(inp.Cover)
- }
- pcs := coverToPCs(rg, cov.Serialize())
- sort.Slice(pcs, func(i, j int) bool {
- return pcs[i] < pcs[j]
- })
-
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- buf := bufio.NewWriter(w)
- for _, pc := range pcs {
- fmt.Fprintf(buf, "0x%x\n", pc)
- }
- buf.Flush()
+ mgr.httpCoverCover(w, r, DoRawCover, false)
}
func (mgr *Manager) httpRawCoverFiles(w http.ResponseWriter, r *http.Request) {
@@ -505,36 +485,7 @@ func (mgr *Manager) httpFilterPCs(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "cover is not filtered in config.\n")
return
}
- // Note: initCover is executed without mgr.mu because it takes very long time
- // (but it only reads config and it protected by initCoverOnce).
- rg, err := getReportGenerator(mgr.cfg, mgr.modules)
- if err != nil {
- http.Error(w, fmt.Sprintf("failed to generate coverage profile: %v", err), http.StatusInternalServerError)
- return
- }
- mgr.mu.Lock()
- defer mgr.mu.Unlock()
-
- var cov cover.Cover
- for _, inp := range mgr.corpus {
- cov.Merge(inp.Cover)
- }
- pcs := make([]uint64, 0, len(cov))
- for _, pc := range coverToPCs(rg, cov.Serialize()) {
- if mgr.coverFilter[uint32(pc)] != 0 {
- pcs = append(pcs, pc)
- }
- }
- sort.Slice(pcs, func(i, j int) bool {
- return pcs[i] < pcs[j]
- })
-
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- buf := bufio.NewWriter(w)
- for _, pc := range pcs {
- fmt.Fprintf(buf, "0x%x\n", pc)
- }
- buf.Flush()
+ mgr.httpCoverCover(w, r, DoFilterPCs, false)
}
func (mgr *Manager) collectCrashes(workdir string) ([]*UICrashType, error) {
diff --git a/tools/syz-cover/syz-cover.go b/tools/syz-cover/syz-cover.go
index 5c7a27ecb..42e592bd9 100644
--- a/tools/syz-cover/syz-cover.go
+++ b/tools/syz-cover/syz-cover.go
@@ -75,7 +75,7 @@ func main() {
progs := []cover.Prog{{PCs: pcs}}
buf := new(bytes.Buffer)
if *flagExport != "" {
- if err := rg.DoCSV(buf, progs); err != nil {
+ if err := rg.DoCSV(buf, progs, nil); err != nil {
tool.Fail(err)
}
if err := osutil.WriteFile(*flagExport, buf.Bytes()); err != nil {
@@ -83,7 +83,7 @@ func main() {
}
return
}
- if err := rg.DoHTML(buf, progs); err != nil {
+ if err := rg.DoHTML(buf, progs, nil); err != nil {
tool.Fail(err)
}
fn, err := osutil.TempFile("syz-cover")