:: commit beee378f069cb759a27f691a566700c1a622327e

Kamila Szewczyk <k@iczelia.net> — 2026-05-01 17:00

parents: b7bf7f12f5

lib/elf: protect against crafted ELF files.

On 32-bit builds loading ELF64, a crafted large RELR table could overflow the count, underallocate the pointer array, then write past it during RELR expansion. Found via fuzzing.
diff --git a/common/lib/elf.c b/common/lib/elf.c
index 66fc1e60..b7859811 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -303,6 +303,14 @@ static bool elf64_translate_vaddr(uint8_t *elf, size_t file_size,
     return false;
 }
 
+static void elf64_add_relocation_count(size_t *count, uint64_t add) {
+    if (add > SIZE_MAX - *count) {
+        panic(true, "elf: relocation count overflow");
+    }
+
+    *count += (size_t)add;
+}
+
 static bool elf64_apply_relocations(uint8_t *elf, size_t file_size, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
     if (hdr->phdr_size < sizeof(struct elf64_phdr)) {
         panic(true, "elf: phdr_size < sizeof(struct elf64_phdr)");
@@ -437,9 +445,9 @@ end_of_pt_segment:
             uint64_t entry = *((uint64_t *)(elf + relr_offset + i * relr_ent));
 
             if ((entry & 1) == 0) {
-                relocs_i++;
+                elf64_add_relocation_count(&relocs_i, 1);
             } else {
-                relocs_i += __builtin_popcountll(entry) - 1;
+                elf64_add_relocation_count(&relocs_i, __builtin_popcountll(entry) - 1);
             }
         }
     }
@@ -451,7 +459,7 @@ end_of_pt_segment:
         if (rela_size % rela_ent != 0) {
             panic(true, "elf: rela_size not a multiple of rela_ent");
         }
-        relocs_i += rela_size / rela_ent;
+        elf64_add_relocation_count(&relocs_i, rela_size / rela_ent);
     }
     if (dt_pltrelsz != 0) {
         if (dt_pltrel != DT_RELA) {
@@ -463,7 +471,7 @@ end_of_pt_segment:
         if (dt_pltrelsz % rela_ent != 0) {
             panic(true, "elf: dt_pltrelsz not a multiple of rela_ent");
         }
-        relocs_i += dt_pltrelsz / rela_ent;
+        elf64_add_relocation_count(&relocs_i, dt_pltrelsz / rela_ent);
     }
     struct elf64_rela **relocs = ext_mem_alloc_counted(relocs_i, sizeof(struct elf64_rela *));
 
tab: 248 wrap: offon