dwarf: make debug_line header parser more resilient (#2456)
Check for errors, log them and return early, do not try to allocate large chunks of memory that we can never possibly read from the file. Fixes #2449
This commit is contained in:
parent
6a25ee09f5
commit
c6e52ecf5c
@ -89,11 +89,19 @@ func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{})
|
||||
|
||||
parseDebugLinePrologue(dbl, buf)
|
||||
if dbl.Prologue.Version >= 5 {
|
||||
parseIncludeDirs5(dbl, buf)
|
||||
parseFileEntries5(dbl, buf)
|
||||
if !parseIncludeDirs5(dbl, buf) {
|
||||
return nil
|
||||
}
|
||||
if !parseFileEntries5(dbl, buf) {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
parseIncludeDirs2(dbl, buf)
|
||||
parseFileEntries2(dbl, buf)
|
||||
if !parseIncludeDirs2(dbl, buf) {
|
||||
return nil
|
||||
}
|
||||
if !parseFileEntries2(dbl, buf) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Instructions size calculation breakdown:
|
||||
@ -135,20 +143,30 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||
}
|
||||
|
||||
// parseIncludeDirs2 parses the directory table for DWARF version 2 through 4.
|
||||
func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) bool {
|
||||
for {
|
||||
str, _ := util.ParseString(buf)
|
||||
str, err := util.ParseString(buf)
|
||||
if err != nil {
|
||||
if info.Logf != nil {
|
||||
info.Logf("error reading string: %v", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if str == "" {
|
||||
break
|
||||
}
|
||||
|
||||
info.IncludeDirs = append(info.IncludeDirs, str)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parseIncludeDirs5 parses the directory table for DWARF version 5.
|
||||
func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) bool {
|
||||
dirEntryFormReader := readEntryFormat(buf, info.Logf)
|
||||
if dirEntryFormReader == nil {
|
||||
return false
|
||||
}
|
||||
dirCount, _ := util.DecodeULEB128(buf)
|
||||
info.IncludeDirs = make([]string, 0, dirCount)
|
||||
for i := uint64(0); i < dirCount; i++ {
|
||||
@ -168,13 +186,23 @@ func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
case _DW_LNCT_MD5:
|
||||
}
|
||||
}
|
||||
if dirEntryFormReader.err != nil {
|
||||
if info.Logf != nil {
|
||||
info.Logf("error reading directory entries table: %v", dirEntryFormReader.err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// parseFileEntries2 parses the file table for DWARF 2 through 4
|
||||
func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) bool {
|
||||
for {
|
||||
entry := readFileEntry(info, buf, true)
|
||||
if entry == nil {
|
||||
return false
|
||||
}
|
||||
if entry.Path == "" {
|
||||
break
|
||||
}
|
||||
@ -182,12 +210,20 @@ func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
info.FileNames = append(info.FileNames, entry)
|
||||
info.Lookup[entry.Path] = entry
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
|
||||
entry := new(FileEntry)
|
||||
|
||||
entry.Path, _ = util.ParseString(buf)
|
||||
var err error
|
||||
entry.Path, err = util.ParseString(buf)
|
||||
if err != nil {
|
||||
if info.Logf != nil {
|
||||
info.Logf("error reading file entry: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if entry.Path == "" && exitOnEmptyPath {
|
||||
return entry
|
||||
}
|
||||
@ -225,8 +261,11 @@ func pathIsAbs(s string) bool {
|
||||
}
|
||||
|
||||
// parseFileEntries5 parses the file table for DWARF 5
|
||||
func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) bool {
|
||||
fileEntryFormReader := readEntryFormat(buf, info.Logf)
|
||||
if fileEntryFormReader == nil {
|
||||
return false
|
||||
}
|
||||
fileCount, _ := util.DecodeULEB128(buf)
|
||||
info.FileNames = make([]*FileEntry, 0, fileCount)
|
||||
for i := 0; i < int(fileCount); i++ {
|
||||
@ -265,5 +304,12 @@ func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
info.FileNames = append(info.FileNames, entry)
|
||||
info.Lookup[entry.Path] = entry
|
||||
}
|
||||
if fileEntryFormReader.err != nil {
|
||||
if info.Logf != nil {
|
||||
info.Logf("error reading file entries table: %v", fileEntryFormReader.err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package line
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/util"
|
||||
)
|
||||
@ -39,6 +40,8 @@ const (
|
||||
_DW_LNCT_MD5
|
||||
)
|
||||
|
||||
var ErrBufferUnderflow = errors.New("buffer underflow")
|
||||
|
||||
type formReader struct {
|
||||
logf func(string, ...interface{})
|
||||
contentTypes []uint64
|
||||
@ -51,11 +54,15 @@ type formReader struct {
|
||||
u64 uint64
|
||||
i64 int64
|
||||
str string
|
||||
err error
|
||||
|
||||
nexti int
|
||||
}
|
||||
|
||||
func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
|
||||
if buf.Len() < 1 {
|
||||
return nil
|
||||
}
|
||||
count := buf.Next(1)[0]
|
||||
r := &formReader{
|
||||
logf: logf,
|
||||
@ -70,10 +77,14 @@ func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *form
|
||||
}
|
||||
|
||||
func (rdr *formReader) reset() {
|
||||
rdr.err = nil
|
||||
rdr.nexti = 0
|
||||
}
|
||||
|
||||
func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
||||
if rdr.err != nil {
|
||||
return false
|
||||
}
|
||||
if rdr.nexti >= len(rdr.contentTypes) {
|
||||
return false
|
||||
}
|
||||
@ -87,24 +98,52 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
||||
rdr.readBlock(buf, n)
|
||||
|
||||
case _DW_FORM_block1:
|
||||
if buf.Len() < 1 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.readBlock(buf, uint64(buf.Next(1)[0]))
|
||||
|
||||
case _DW_FORM_block2:
|
||||
if buf.Len() < 2 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
|
||||
|
||||
case _DW_FORM_block4:
|
||||
if buf.Len() < 4 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
|
||||
|
||||
case _DW_FORM_data1, _DW_FORM_flag, _DW_FORM_strx1:
|
||||
if buf.Len() < 1 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.u64 = uint64(buf.Next(1)[0])
|
||||
|
||||
case _DW_FORM_data2, _DW_FORM_strx2:
|
||||
if buf.Len() < 2 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.u64 = uint64(binary.LittleEndian.Uint16(buf.Next(2)))
|
||||
|
||||
case _DW_FORM_data4, _DW_FORM_line_strp, _DW_FORM_sec_offset, _DW_FORM_strp, _DW_FORM_strx4:
|
||||
if buf.Len() < 4 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.u64 = uint64(binary.LittleEndian.Uint32(buf.Next(4)))
|
||||
|
||||
case _DW_FORM_data8:
|
||||
if buf.Len() < 8 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
|
||||
|
||||
case _DW_FORM_data16:
|
||||
@ -120,6 +159,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
||||
rdr.str, _ = util.ParseString(buf)
|
||||
|
||||
case _DW_FORM_strx3:
|
||||
if buf.Len() < 3 {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return false
|
||||
}
|
||||
rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
|
||||
|
||||
default:
|
||||
@ -136,6 +179,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
||||
}
|
||||
|
||||
func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
|
||||
if uint64(buf.Len()) < n {
|
||||
rdr.err = ErrBufferUnderflow
|
||||
return
|
||||
}
|
||||
if cap(rdr.block) < int(n) {
|
||||
rdr.block = make([]byte, 0, n)
|
||||
}
|
||||
|
@ -552,8 +552,9 @@ func setdiscriminator(sm *StateMachine, buf *bytes.Buffer) {
|
||||
}
|
||||
|
||||
func definefile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
entry := readFileEntry(sm.dbl, sm.buf, false)
|
||||
sm.definedFiles = append(sm.definedFiles, entry)
|
||||
if entry := readFileEntry(sm.dbl, sm.buf, false); entry != nil {
|
||||
sm.definedFiles = append(sm.definedFiles, entry)
|
||||
}
|
||||
}
|
||||
|
||||
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
|
||||
|
@ -128,13 +128,13 @@ func EncodeSLEB128(out io.ByteWriter, x int64) {
|
||||
}
|
||||
|
||||
// ParseString reads a null-terminated string from data.
|
||||
func ParseString(data *bytes.Buffer) (string, uint32) {
|
||||
func ParseString(data *bytes.Buffer) (string, error) {
|
||||
str, err := data.ReadString(0x0)
|
||||
if err != nil {
|
||||
panic("Could not parse string")
|
||||
return "", err
|
||||
}
|
||||
|
||||
return str[:len(str)-1], uint32(len(str))
|
||||
return str[:len(str)-1], nil
|
||||
}
|
||||
|
||||
// ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
|
||||
|
Loading…
Reference in New Issue
Block a user