go-perf icon indicating copy to clipboard operation
go-perf copied to clipboard

[bug] - Crashes while trying to decode a recording of perf DWARF data

Open dalehamel opened this issue 3 years ago • 4 comments

Perf recording taking on arm64 (Apple M1, ubuntu VM in VMWare). May affect other platforms.

To reproduce, just take a perf recording with --call-graph=dwarf:

perf record --call-graph dwarf

Then try to dump it:

~/go/bin/perfdump -i perf.data
events:
  0x400012c000={Event:EventSoftwareCPUClock SamplePeriod:0 SampleFreq:4000 SampleFormat:Addr|CPU|Callchain|DataSrc|IP|Period|RegsUser|StackUser|TID|Time ReadFormat:ID Flags:AuxOutput|Comm|CommExec|Disabled|ExcludeCallchainUser|ExcludeGuest|Freq|Inherit|Ksymbol|Mmap|MmapData|MmapInodeData|SampleIDAll|Task Precise:EventPrecisionZeroSkip WakeupEvents:0 WakeupWatermark:0 BranchSampleType:0 SampleRegsUser:8589934591 SampleStackUser:8192 SampleRegsIntr:0 AuxWatermark:0 SampleMaxStack:0}
build IDs:

... 

hostname: ubuntu
OS release: 5.4.0-92-generic
version: 5.4.157
arch: aarch64
CPUs online: 4
CPUs available: 4
CPU desc:
CPUID:
total memory: 2070073344
cmdline: [/usr/lib/linux-tools-5.4.0-92/perf record --call-graph dwarf]
core groups: [0-3]
thread groups: [0 1 2 3]
NUMA nodes: [{0 2070073344 387358720 0-3}]
PMU mappings: map[1:software 2:tracepoint 5:breakpoint 6:kprobe 7:uprobe]
groups: []

panic: runtime error: index out of range [7] with length 0

goroutine 1 [running]:
encoding/binary.littleEndian.Uint64(...)
        /usr/local/go/src/encoding/binary/binary.go:77
github.com/aclements/go-perf/perffile.(*bufDecoder).u64(...)
        /root/go/pkg/mod/github.com/aclements/[email protected]/perffile/bufdecoder.go:48
github.com/aclements/go-perf/perffile.(*Records).parseSample(0x4000154000, 0x4000069998, 0x40001e7db0, 0x90?)
        /root/go/pkg/mod/github.com/aclements/[email protected]/perffile/records.go:610 +0xaa4
github.com/aclements/go-perf/perffile.(*Records).Next(0x4000154000)
        /root/go/pkg/mod/github.com/aclements/[email protected]/perffile/records.go:148 +0x508
github.com/aclements/go-perf/perffile.(*File).Records(0x4000128000, 0x400011a008?)
        /root/go/pkg/mod/github.com/aclements/[email protected]/perffile/reader.go:350 +0x298
main.main()
        /root/go/pkg/mod/github.com/aclements/[email protected]/cmd/perfdump/main.go:77 +0x750

dalehamel avatar Nov 23 '22 20:11 dalehamel

Actual crash is caused by https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/encoding/binary/binary.go;l=77 so I guess that the issue is that it is passed an empty buffer (a bit surprising they don't check boundaries in stdlib...)

Maybe a guard can be placed on https://github.com/aclements/go-perf/blob/cadabe6d386a5472159d9cb2945d2115f6ae77d7/perffile/bufdecoder.go#L48 or in https://github.com/aclements/go-perf/blob/cadabe6d386a5472159d9cb2945d2115f6ae77d7/perffile/records.go#L609-L611

dalehamel avatar Nov 23 '22 20:11 dalehamel

The following patch avoids the crash:

diff --git a/perffile/bufdecoder.go b/perffile/bufdecoder.go
index fcb5966..79425fa 100644
--- a/perffile/bufdecoder.go
+++ b/perffile/bufdecoder.go
@@ -45,6 +45,9 @@ func (b *bufDecoder) i32() int32 {
 }

 func (b *bufDecoder) u64() uint64 {
+       if len(b.buf) < 8 {
+               return 0
+       }
        x := b.order.Uint64(b.buf)
        b.buf = b.buf[8:]
        return x

dalehamel avatar Nov 23 '22 21:11 dalehamel

FYI it is not an ARM specific bug, verified on amd64 as well

dalehamel avatar Nov 23 '22 21:11 dalehamel