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)
|
parseDebugLinePrologue(dbl, buf)
|
||||||
if dbl.Prologue.Version >= 5 {
|
if dbl.Prologue.Version >= 5 {
|
||||||
parseIncludeDirs5(dbl, buf)
|
if !parseIncludeDirs5(dbl, buf) {
|
||||||
parseFileEntries5(dbl, buf)
|
return nil
|
||||||
|
}
|
||||||
|
if !parseFileEntries5(dbl, buf) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
parseIncludeDirs2(dbl, buf)
|
if !parseIncludeDirs2(dbl, buf) {
|
||||||
parseFileEntries2(dbl, buf)
|
return nil
|
||||||
|
}
|
||||||
|
if !parseFileEntries2(dbl, buf) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instructions size calculation breakdown:
|
// 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.
|
// 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 {
|
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 == "" {
|
if str == "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
info.IncludeDirs = append(info.IncludeDirs, str)
|
info.IncludeDirs = append(info.IncludeDirs, str)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseIncludeDirs5 parses the directory table for DWARF version 5.
|
// 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)
|
dirEntryFormReader := readEntryFormat(buf, info.Logf)
|
||||||
|
if dirEntryFormReader == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
dirCount, _ := util.DecodeULEB128(buf)
|
dirCount, _ := util.DecodeULEB128(buf)
|
||||||
info.IncludeDirs = make([]string, 0, dirCount)
|
info.IncludeDirs = make([]string, 0, dirCount)
|
||||||
for i := uint64(0); i < dirCount; i++ {
|
for i := uint64(0); i < dirCount; i++ {
|
||||||
@ -168,13 +186,23 @@ func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
|
|||||||
case _DW_LNCT_MD5:
|
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
|
// 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 {
|
for {
|
||||||
entry := readFileEntry(info, buf, true)
|
entry := readFileEntry(info, buf, true)
|
||||||
|
if entry == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if entry.Path == "" {
|
if entry.Path == "" {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -182,12 +210,20 @@ func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
|
|||||||
info.FileNames = append(info.FileNames, entry)
|
info.FileNames = append(info.FileNames, entry)
|
||||||
info.Lookup[entry.Path] = entry
|
info.Lookup[entry.Path] = entry
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
|
func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
|
||||||
entry := new(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 {
|
if entry.Path == "" && exitOnEmptyPath {
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
@ -225,8 +261,11 @@ func pathIsAbs(s string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parseFileEntries5 parses the file table for DWARF 5
|
// 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)
|
fileEntryFormReader := readEntryFormat(buf, info.Logf)
|
||||||
|
if fileEntryFormReader == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
fileCount, _ := util.DecodeULEB128(buf)
|
fileCount, _ := util.DecodeULEB128(buf)
|
||||||
info.FileNames = make([]*FileEntry, 0, fileCount)
|
info.FileNames = make([]*FileEntry, 0, fileCount)
|
||||||
for i := 0; i < int(fileCount); i++ {
|
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.FileNames = append(info.FileNames, entry)
|
||||||
info.Lookup[entry.Path] = 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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/go-delve/delve/pkg/dwarf/util"
|
"github.com/go-delve/delve/pkg/dwarf/util"
|
||||||
)
|
)
|
||||||
@ -39,6 +40,8 @@ const (
|
|||||||
_DW_LNCT_MD5
|
_DW_LNCT_MD5
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrBufferUnderflow = errors.New("buffer underflow")
|
||||||
|
|
||||||
type formReader struct {
|
type formReader struct {
|
||||||
logf func(string, ...interface{})
|
logf func(string, ...interface{})
|
||||||
contentTypes []uint64
|
contentTypes []uint64
|
||||||
@ -51,11 +54,15 @@ type formReader struct {
|
|||||||
u64 uint64
|
u64 uint64
|
||||||
i64 int64
|
i64 int64
|
||||||
str string
|
str string
|
||||||
|
err error
|
||||||
|
|
||||||
nexti int
|
nexti int
|
||||||
}
|
}
|
||||||
|
|
||||||
func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
|
func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
|
||||||
|
if buf.Len() < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
count := buf.Next(1)[0]
|
count := buf.Next(1)[0]
|
||||||
r := &formReader{
|
r := &formReader{
|
||||||
logf: logf,
|
logf: logf,
|
||||||
@ -70,10 +77,14 @@ func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *form
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rdr *formReader) reset() {
|
func (rdr *formReader) reset() {
|
||||||
|
rdr.err = nil
|
||||||
rdr.nexti = 0
|
rdr.nexti = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
||||||
|
if rdr.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
if rdr.nexti >= len(rdr.contentTypes) {
|
if rdr.nexti >= len(rdr.contentTypes) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -87,24 +98,52 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
|||||||
rdr.readBlock(buf, n)
|
rdr.readBlock(buf, n)
|
||||||
|
|
||||||
case _DW_FORM_block1:
|
case _DW_FORM_block1:
|
||||||
|
if buf.Len() < 1 {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return false
|
||||||
|
}
|
||||||
rdr.readBlock(buf, uint64(buf.Next(1)[0]))
|
rdr.readBlock(buf, uint64(buf.Next(1)[0]))
|
||||||
|
|
||||||
case _DW_FORM_block2:
|
case _DW_FORM_block2:
|
||||||
|
if buf.Len() < 2 {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return false
|
||||||
|
}
|
||||||
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
|
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
|
||||||
|
|
||||||
case _DW_FORM_block4:
|
case _DW_FORM_block4:
|
||||||
|
if buf.Len() < 4 {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return false
|
||||||
|
}
|
||||||
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
|
rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
|
||||||
|
|
||||||
case _DW_FORM_data1, _DW_FORM_flag, _DW_FORM_strx1:
|
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])
|
rdr.u64 = uint64(buf.Next(1)[0])
|
||||||
|
|
||||||
case _DW_FORM_data2, _DW_FORM_strx2:
|
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)))
|
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:
|
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)))
|
rdr.u64 = uint64(binary.LittleEndian.Uint32(buf.Next(4)))
|
||||||
|
|
||||||
case _DW_FORM_data8:
|
case _DW_FORM_data8:
|
||||||
|
if buf.Len() < 8 {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return false
|
||||||
|
}
|
||||||
rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
|
rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
|
||||||
|
|
||||||
case _DW_FORM_data16:
|
case _DW_FORM_data16:
|
||||||
@ -120,6 +159,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
|||||||
rdr.str, _ = util.ParseString(buf)
|
rdr.str, _ = util.ParseString(buf)
|
||||||
|
|
||||||
case _DW_FORM_strx3:
|
case _DW_FORM_strx3:
|
||||||
|
if buf.Len() < 3 {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return false
|
||||||
|
}
|
||||||
rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
|
rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -136,6 +179,10 @@ func (rdr *formReader) next(buf *bytes.Buffer) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
|
func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
|
||||||
|
if uint64(buf.Len()) < n {
|
||||||
|
rdr.err = ErrBufferUnderflow
|
||||||
|
return
|
||||||
|
}
|
||||||
if cap(rdr.block) < int(n) {
|
if cap(rdr.block) < int(n) {
|
||||||
rdr.block = make([]byte, 0, 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) {
|
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)
|
sm.definedFiles = append(sm.definedFiles, entry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
|
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.
|
// 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)
|
str, err := data.ReadString(0x0)
|
||||||
if err != nil {
|
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.
|
// ReadUintRaw reads an integer of ptrSize bytes, with the specified byte order, from reader.
|
||||||
|
Loading…
Reference in New Issue
Block a user