From ed787856f258f495445c2267b9cf40d7d4ce320d Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Wed, 27 Apr 2016 17:37:54 +0200 Subject: Use readelf to obtain the upper 32 bits of addresses returned by kcov. When executors send coverage data to the manager, they clamp the addresses of covered blocks to 32 bits. Manager uses RestorePC() to restore the original addresses. Previously, RestorePC() assumed that the upper 4 bytes of a kernel code address were 0xffffffff, which is not so on Android. Instead we now parse `readelf -SW vmlinux` output to obtain the upper bytes of PROGBITS sections VMAs in the case those VMAs are non-zero. We assume that the upper 4 bytes are the same for every section. --- cover/cover.go | 4 ++-- syz-manager/cover.go | 39 ++++++++++++++++++++++++++++++++++++++- tools/syz-execprog/execprog.go | 2 +- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/cover/cover.go b/cover/cover.go index 4c07314af..df2c76a79 100644 --- a/cover/cover.go +++ b/cover/cover.go @@ -20,8 +20,8 @@ func Copy(cov Cover) Cover { return append(Cover{}, cov...) } -func RestorePC(pc uint32) uint64 { - return uint64(0xffffffff)<<32 + uint64(pc) +func RestorePC(pc uint32, base uint32) uint64 { + return uint64(base)<<32 + uint64(pc) } /* Canonicalize sorts and removes duplicates. */ diff --git a/syz-manager/cover.go b/syz-manager/cover.go index c55f75264..7badc31f2 100644 --- a/syz-manager/cover.go +++ b/syz-manager/cover.go @@ -112,7 +112,44 @@ func parseFile(fn string) ([][]byte, error) { return lines, nil } +func getVmOffset(vmlinux string) (uint32, error) { + out, err := exec.Command("readelf", "-SW", vmlinux).CombinedOutput() + if err != nil { + return 0, fmt.Errorf("readelf failed: %v\n%s", err, out) + } + s := bufio.NewScanner(bytes.NewReader(out)) + var addr uint32 + for s.Scan() { + ln := s.Text() + pieces := strings.Fields(ln) + for i := 0; i < len(pieces); i++ { + if pieces[i] != "PROGBITS" { + continue + } + v, err := strconv.ParseUint("0x"+pieces[i+1], 0, 64) + if err != nil { + return 0, fmt.Errorf("failed to parse addr in readelf output: %v", err) + } + if v == 0 { + continue + } + v32 := (uint32)(v >> 32) + if addr == 0 { + addr = v32 + } + if addr != v32 { + return 0, fmt.Errorf("different section offsets in a single binary") + } + } + } + return addr, nil +} + func symbolize(vmlinux string, cov []uint32) ([]LineInfo, string, error) { + base, err := getVmOffset(vmlinux) + if err != nil { + return nil, "", err + } cmd := exec.Command("addr2line", "-a", "-i", "-e", vmlinux) stdin, err := cmd.StdinPipe() if err != nil { @@ -130,7 +167,7 @@ func symbolize(vmlinux string, cov []uint32) ([]LineInfo, string, error) { defer cmd.Wait() go func() { for _, pc := range cov { - fmt.Fprintf(stdin, "0x%x\n", cover.RestorePC(pc)-1) + fmt.Fprintf(stdin, "0x%x\n", cover.RestorePC(pc, base)-1) } stdin.Close() }() diff --git a/tools/syz-execprog/execprog.go b/tools/syz-execprog/execprog.go index e9246766b..fdba0f258 100644 --- a/tools/syz-execprog/execprog.go +++ b/tools/syz-execprog/execprog.go @@ -110,7 +110,7 @@ func main() { buf := new(bytes.Buffer) binary.Write(buf, binary.LittleEndian, uint64(0xC0BFFFFFFFFFFF64)) for _, pc := range c { - binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc)) + binary.Write(buf, binary.LittleEndian, cover.RestorePC(pc, 0xffffffff)) } err := ioutil.WriteFile(fmt.Sprintf("%v.%v", *flagCoverFile, i), buf.Bytes(), 0660) if err != nil { -- cgit mrf-deployment