Marek Benc
2025-Apr-21 19:33 UTC
[syslinux] [PATCH] Permit zero-sized ELF program sections
Hello,
On Debian, I've hit a bit of an issue with the 32-bit EFI support where
the ELF loader failed to load the ldlinux.e32 file.
The issue occurs in com32/lib/sys/module/i386/elf_module.c in
the load_segments() function, particularly when iterating through
the module's program headers and loading the PT_LOAD ones.
The code hits a program section which has a zero on-disk size,
the cr_pht->p_filesz field is 0, and looking at the cr_pht->p_vaddr
and comparing that with an objdump of the ldlinux.e32 file, that was
the .BSS section, and it's reasonable for it to not be stored
in the file, the memory should be initialized to zero, and
the System V ABI document, chapter 5 [0] specifies that if the on-file
size of a section is smaller than its in-memory size, the remaining
portion of the section should be set to zero.
This behavior is already implemented in the ELF loader, and a BSS
section is supposed to be initialized to all zero, so all it needs
are some checks to be able to handle when a program section has
a zero on-disk size.
Here's a patch with the necessary checks:
--- a/com32/lib/sys/module/i386/elf_module.c
+++ b/com32/lib/sys/module/i386/elf_module.c
@@ -112,6 +112,14 @@ int load_segments(struct elf_module *mod
for (i = 0; i < elf_hdr->e_phnum; i++) {
cr_pht = (Elf32_Phdr*)(pht + i * elf_hdr->e_phentsize);
+ if (cr_pht->p_filesz == 0)
+ {
+ DBG_PRINT("Skipping loadable segment of zero size at vaddr 0x%08x at
0x%08x.\n"
+ cr_pht->p_vaddr,
+ (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+ continue;
+ }
+
if (cr_pht->p_type == PT_LOAD) {
// Copy the segment at its destination
if (cr_pht->p_offset < module->u.l._cr_offset) {
@@ -120,6 +128,16 @@ int load_segments(struct elf_module *mod
// headers
Elf32_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset;
+ if (cr_pht->p_filesz <= aux_off)
+ {
+ DBG_PRINT("Skipping segment of size 0x%08x at vaddr 0x%08x at 0x%08x,
"
+ "all data is before current offset.\n",
+ cr_pht->p_filesz,
+ cr_pht->p_vaddr,
+ (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+ continue;
+ }
+
if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) +
aux_off,
cr_pht->p_filesz - aux_off, module) < 0) {
res = -1;
---
syslinux-6.04~git20190206.bf6db5b4+dfsg1.orig/com32/lib/sys/module/x86_64/elf_module.c
+++
syslinux-6.04~git20190206.bf6db5b4+dfsg1/com32/lib/sys/module/x86_64/elf_module.c
@@ -112,6 +112,14 @@ int load_segments(struct elf_module *mod
for (i = 0; i < elf_hdr->e_phnum; i++) {
cr_pht = (Elf64_Phdr*)(pht + i * elf_hdr->e_phentsize);
+ if (cr_pht->p_filesz == 0)
+ {
+ DBG_PRINT("Skipping loadable segment of zero size at vaddr 0x%08x at
0x%08x.\n"
+ cr_pht->p_vaddr,
+ (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+ continue;
+ }
+
if (cr_pht->p_type == PT_LOAD) {
// Copy the segment at its destination
if (cr_pht->p_offset < module->u.l._cr_offset) {
@@ -120,6 +128,16 @@ int load_segments(struct elf_module *mod
// headers
Elf64_Off aux_off = module->u.l._cr_offset - cr_pht->p_offset;
+ if (cr_pht->p_filesz <= aux_off)
+ {
+ DBG_PRINT("Skipping segment of size 0x%08x at vaddr 0x%08x at 0x%08x,
"
+ "all data is before current offset.\n",
+ cr_pht->p_filesz,
+ cr_pht->p_vaddr,
+ (Elf32_Addr)module_get_absolute(cr_pht->p_vaddr, module));
+ continue;
+ }
+
if (image_read((char *)module_get_absolute(cr_pht->p_vaddr, module) +
aux_off,
cr_pht->p_filesz - aux_off, module) < 0) {
res = -1;
With this patch, 32-bit EFI mode works again for me. The patch adds
the checks to both the 32-bit and 64-bit loading code, since there's
always the possibility that a similar situation might happen on 64-bit
as well.
I've also opened a debian bug report for this issue [1], it contains
the exact steps on how to reproduce the issue on Debian.
Best Regards,
Marek
[0] - https://refspecs.linuxbase.org/elf/gabi4+/ch5.pheader.html
[1] - https://bugs.debian.org/1103692