fix(elf2image): validate ELF section types and addresses before processing

This commit is contained in:
Jaroslav Burian
2025-07-09 18:18:38 +02:00
committed by Radim Karniš
parent ec84a75777
commit 97a1546166
2 changed files with 115 additions and 36 deletions

View File

@@ -1281,20 +1281,6 @@ ESP32H21ROM.BOOTLOADER_IMAGE = ESP32H21FirmwareImage
class ELFFile(object):
SEC_TYPE_PROGBITS = 0x01
SEC_TYPE_STRTAB = 0x03
SEC_TYPE_NOBITS = 0x08 # e.g. .bss section
SEC_TYPE_INITARRAY = 0x0E
SEC_TYPE_FINIARRAY = 0x0F
SEC_TYPE_PREINITARRAY = 0x10
PROG_SEC_TYPES = (
SEC_TYPE_PROGBITS,
SEC_TYPE_INITARRAY,
SEC_TYPE_FINIARRAY,
SEC_TYPE_PREINITARRAY,
)
LEN_SEC_HEADER = 0x28
SEG_TYPE_LOAD = 0x01
@@ -1353,6 +1339,22 @@ class ELFFile(object):
self._read_segments(f, _phoff, _phnum, shstrndx)
def _read_sections(self, f, section_header_offs, section_header_count, shstrndx):
SEC_TYPE_PROGBITS = 0x01
SEC_TYPE_STRTAB = 0x03
SEC_TYPE_NOBITS = 0x08 # e.g. .bss section
SEC_TYPE_INITARRAY = 0x0E
SEC_TYPE_FINIARRAY = 0x0F
SEC_TYPE_PREINITARRAY = 0x10
PROG_SEC_TYPES = (
SEC_TYPE_PROGBITS,
SEC_TYPE_INITARRAY,
SEC_TYPE_FINIARRAY,
SEC_TYPE_PREINITARRAY,
)
KNOWN_SEC_TYPES = PROG_SEC_TYPES + (SEC_TYPE_NOBITS, SEC_TYPE_STRTAB)
f.seek(section_header_offs)
len_bytes = section_header_count * self.LEN_SEC_HEADER
section_header = f.read(len_bytes)
@@ -1384,17 +1386,13 @@ class ELFFile(object):
) = struct.unpack_from("<LLLLLLLLL", section_header[offs:])
return (name_offs, sec_type, lma, size, sec_offs, _flags, align)
all_sections = [read_section_header(offs) for offs in section_header_offsets]
prog_sections = [s for s in all_sections if s[1] in ELFFile.PROG_SEC_TYPES]
nobits_secitons = [s for s in all_sections if s[1] == ELFFile.SEC_TYPE_NOBITS]
# search for the string table section
if (shstrndx * self.LEN_SEC_HEADER) not in section_header_offsets:
raise FatalError(f"ELF file has no STRTAB section at shstrndx {shstrndx}")
_, sec_type, _, sec_size, sec_offs, _flags, align = read_section_header(
shstrndx * self.LEN_SEC_HEADER
)
if sec_type != ELFFile.SEC_TYPE_STRTAB:
if sec_type != SEC_TYPE_STRTAB:
log.warning(f"ELF file has incorrect STRTAB section type {sec_type:#04x}")
f.seek(sec_offs)
string_table = f.read(sec_size)
@@ -1410,23 +1408,42 @@ class ELFFile(object):
f.seek(offs)
return f.read(size)
prog_sections = [
ELFSection(
lookup_string(n_offs),
lma,
read_data(offs, size),
flags=_flags,
align=align,
)
for (n_offs, _type, lma, size, offs, _flags, align) in prog_sections
if lma != 0 and size > 0
]
self.sections = prog_sections
self.nobits_sections = [
ELFSection(lookup_string(n_offs), lma, b"", flags=_flags, align=align)
for (n_offs, _type, lma, size, offs, _flags, align) in nobits_secitons
if lma != 0 and size > 0
]
all_sections = [read_section_header(offs) for offs in section_header_offsets]
self.sections = []
self.nobits_sections = []
# Process all sections and raise an error if an unknown section type is found
for section in all_sections:
n_offs, sec_type, lma, size, offs, _flags, align = section
# Skip sections with lma == 0 or size == 0
if lma == 0 or size == 0:
continue
if sec_type not in KNOWN_SEC_TYPES:
log.warning(f"Unknown section type {sec_type:#04x} in ELF file")
continue
if sec_type in PROG_SEC_TYPES:
self.sections.append(
ELFSection(
lookup_string(n_offs),
lma,
read_data(offs, size),
flags=_flags,
align=align,
)
)
elif sec_type == SEC_TYPE_NOBITS:
self.nobits_sections.append(
ELFSection(
lookup_string(n_offs),
lma,
b"",
flags=_flags,
align=align,
)
)
def _read_segments(self, f, segment_header_offs, segment_header_count, shstrndx):
f.seek(segment_header_offs)

View File

@@ -539,6 +539,68 @@ class TestHashAppend(BaseTestCase):
assert bytes(expected_bin_without_hash) == bin_without_hash
class TestELFSectionHandling(BaseTestCase):
"""Test ELF section type handling and related functionality."""
@staticmethod
def _modify_section_type(elf_path, section_name, new_type):
"""
Modify the type of a specific section in the ELF file.
"""
with open(elf_path, "rb+") as f:
elf = ELFFile(f)
section = elf.get_section_by_name(section_name)
index = elf.get_section_index(section_name)
# Calculate the section header's position in the file (using section index,
# the section header table's offset and section header entry size)
sh_entry_offset = elf.header["e_shoff"] + index * elf.header["e_shentsize"]
# Modify the section type in the header
section.header.sh_type = new_type
f.seek(sh_entry_offset)
f.write(elf.structs.Elf_Shdr.build(section.header))
@staticmethod
def _get_section_type(elf_path, section_name):
"""
Get the current type of a specific section in the ELF file.
"""
with open(elf_path, "rb") as f:
elf = ELFFile(f)
section = elf.get_section_by_name(section_name)
return section.header.sh_type
def test_unknown_section_type_warning(self, capsys):
"""Test that unknown section types generate the expected warning message."""
ELF = "esp32c6-appdesc.elf"
BIN = "esp32c6-appdesc.bin"
SECTION_NAME = ".flash.appdesc"
UNKNOWN_TYPE = 0x99
original_sec_type = self._get_section_type(ELF, SECTION_NAME)
# Modify the section to have an unknown type
self._modify_section_type(ELF, SECTION_NAME, UNKNOWN_TYPE)
# Verify the section was actually modified
modified_type = self._get_section_type(ELF, SECTION_NAME)
assert modified_type == UNKNOWN_TYPE
try:
self.run_elf2image("esp32c6", ELF, allow_warnings=True)
output = capsys.readouterr().out
print(output)
expected_warning = f"Unknown section type {UNKNOWN_TYPE:#04x} in ELF file"
assert expected_warning in output
finally:
self._modify_section_type(ELF, SECTION_NAME, original_sec_type)
try_delete(BIN)
class TestMMUPageSize(BaseTestCase):
def test_appdesc_aligned(self, capsys):
ELF = "esp32c6-appdesc.elf"