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:
Alessandro Arzilli 2021-05-04 21:36:22 +02:00 committed by GitHub
parent 6a25ee09f5
commit c6e52ecf5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 109 additions and 15 deletions

@ -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,9 +552,10 @@ func setdiscriminator(sm *StateMachine, buf *bytes.Buffer) {
}
func definefile(sm *StateMachine, buf *bytes.Buffer) {
entry := readFileEntry(sm.dbl, sm.buf, false)
if entry := readFileEntry(sm.dbl, sm.buf, false); entry != nil {
sm.definedFiles = append(sm.definedFiles, entry)
}
}
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
sm.prologueEnd = true

@ -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.