mirror of
https://github.com/anchore/syft.git
synced 2026-11-02 15:36:41 +01:00
fix(elf): avoid a panic on a truncated dynamic section (#5321)
Signed-off-by: somaz <genius5711@gmail.com>
This commit is contained in:
@@ -156,32 +156,12 @@ func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hasElfDynTag(f *elf.File, tag elf.DynTag) bool {
|
func hasElfDynTag(f *elf.File, tag elf.DynTag) bool {
|
||||||
// source https://github.com/golang/go/blob/9b4b3e5acca2dabe107fa2c3ed963097d78a4562/src/cmd/cgo/internal/testshared/shared_test.go#L280
|
vals, err := f.DynValue(tag)
|
||||||
|
|
||||||
ds := f.SectionByType(elf.SHT_DYNAMIC)
|
|
||||||
if ds == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
d, err := ds.Data()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.WithFields("error", err).Trace("unable to read dynamic section from elf file")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
return len(vals) > 0
|
||||||
for len(d) > 0 {
|
|
||||||
var t elf.DynTag
|
|
||||||
switch f.Class {
|
|
||||||
case elf.ELFCLASS32:
|
|
||||||
t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
|
|
||||||
d = d[8:]
|
|
||||||
case elf.ELFCLASS64:
|
|
||||||
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
|
|
||||||
d = d[16:]
|
|
||||||
}
|
|
||||||
if t == tag {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isELFPIE(f *elf.File) bool {
|
func isELFPIE(f *elf.File) bool {
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package executable
|
package executable
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -226,3 +228,89 @@ func Test_elfHasExports(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_hasElfDynTag(t *testing.T) {
|
||||||
|
dynEntry := func(class elf.Class, tag elf.DynTag) []byte {
|
||||||
|
if class == elf.ELFCLASS32 {
|
||||||
|
return binary.LittleEndian.AppendUint32(binary.LittleEndian.AppendUint32(nil, uint32(tag)), 0)
|
||||||
|
}
|
||||||
|
return binary.LittleEndian.AppendUint64(binary.LittleEndian.AppendUint64(nil, uint64(tag)), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
class elf.Class
|
||||||
|
dynamic []byte
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "32-bit tag present",
|
||||||
|
class: elf.ELFCLASS32,
|
||||||
|
dynamic: dynEntry(elf.ELFCLASS32, elf.DT_BIND_NOW),
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "64-bit tag present",
|
||||||
|
class: elf.ELFCLASS64,
|
||||||
|
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_BIND_NOW),
|
||||||
|
want: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "64-bit tag absent",
|
||||||
|
class: elf.ELFCLASS64,
|
||||||
|
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_NULL),
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "32-bit truncated dynamic section",
|
||||||
|
class: elf.ELFCLASS32,
|
||||||
|
dynamic: dynEntry(elf.ELFCLASS32, elf.DT_BIND_NOW)[:4],
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "64-bit truncated dynamic section",
|
||||||
|
class: elf.ELFCLASS64,
|
||||||
|
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_BIND_NOW)[:12],
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
f, err := elf.NewFile(bytes.NewReader(elfWithDynamicSection(t, tt.class, tt.dynamic)))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.want, hasElfDynTag(f, elf.DT_BIND_NOW))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// elfWithDynamicSection builds a little-endian ELF holding only a null section and a SHT_DYNAMIC section.
|
||||||
|
func elfWithDynamicSection(t *testing.T, class elf.Class, dynamic []byte) []byte {
|
||||||
|
t.Helper()
|
||||||
|
ident := [elf.EI_NIDENT]byte{0x7f, 'E', 'L', 'F', byte(class), byte(elf.ELFDATA2LSB), byte(elf.EV_CURRENT)}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
write := func(v any) {
|
||||||
|
require.NoError(t, binary.Write(&buf, binary.LittleEndian, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
if class == elf.ELFCLASS32 {
|
||||||
|
ehsize, shentsize := binary.Size(elf.Header32{}), binary.Size(elf.Section32{})
|
||||||
|
dataOff := ehsize + 2*shentsize
|
||||||
|
write(elf.Header32{
|
||||||
|
Ident: ident, Type: uint16(elf.ET_DYN), Machine: uint16(elf.EM_386), Version: uint32(elf.EV_CURRENT),
|
||||||
|
Shoff: uint32(ehsize), Ehsize: uint16(ehsize), Shentsize: uint16(shentsize), Shnum: 2,
|
||||||
|
})
|
||||||
|
write(elf.Section32{})
|
||||||
|
write(elf.Section32{Type: uint32(elf.SHT_DYNAMIC), Off: uint32(dataOff), Size: uint32(len(dynamic)), Addralign: 1})
|
||||||
|
} else {
|
||||||
|
ehsize, shentsize := binary.Size(elf.Header64{}), binary.Size(elf.Section64{})
|
||||||
|
dataOff := ehsize + 2*shentsize
|
||||||
|
write(elf.Header64{
|
||||||
|
Ident: ident, Type: uint16(elf.ET_DYN), Machine: uint16(elf.EM_X86_64), Version: uint32(elf.EV_CURRENT),
|
||||||
|
Shoff: uint64(ehsize), Ehsize: uint16(ehsize), Shentsize: uint16(shentsize), Shnum: 2,
|
||||||
|
})
|
||||||
|
write(elf.Section64{})
|
||||||
|
write(elf.Section64{Type: uint32(elf.SHT_DYNAMIC), Off: uint64(dataOff), Size: uint64(len(dynamic)), Addralign: 1})
|
||||||
|
}
|
||||||
|
buf.Write(dynamic)
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user