@@ -1144,8 +1144,11 @@ char* SymbolsLinux::extractBuildIdFromMemory(const void* elf_base, size_t elf_si
11441144 return nullptr ;
11451145 }
11461146
1147- // Verify program header table is within file bounds
1148- if (ehdr->e_phoff + ehdr->e_phnum * sizeof (Elf64_Phdr) > elf_size) {
1147+ // Verify program header table is within file bounds. Written as subtractions
1148+ // so a huge e_phoff cannot wrap the addition and slip past the check, which
1149+ // would leave `phdr` pointing outside the mapped image.
1150+ if (ehdr->e_phoff > elf_size ||
1151+ ehdr->e_phnum * sizeof (Elf64_Phdr) > elf_size - ehdr->e_phoff ) {
11491152 return nullptr ;
11501153 }
11511154
@@ -1160,8 +1163,11 @@ char* SymbolsLinux::extractBuildIdFromMemory(const void* elf_base, size_t elf_si
11601163 // Search for PT_NOTE segments
11611164 for (int i = 0 ; i < ehdr->e_phnum ; i++) {
11621165 if (phdr[i].p_type == PT_NOTE && phdr[i].p_filesz > 0 ) {
1163- // Ensure note segment is within file bounds
1164- if (phdr[i].p_offset + phdr[i].p_filesz > elf_size) {
1166+ // Ensure note segment is within file bounds. Subtraction form avoids
1167+ // a u64 overflow in p_offset + p_filesz that would otherwise yield a
1168+ // wild note_data pointer passed to findBuildIdInNotes().
1169+ if (phdr[i].p_offset > elf_size ||
1170+ phdr[i].p_filesz > elf_size - phdr[i].p_offset ) {
11651171 continue ;
11661172 }
11671173
@@ -1197,11 +1203,19 @@ const uint8_t* SymbolsLinux::findBuildIdInNotes(const void* note_data, size_t no
11971203 break ;
11981204 }
11991205
1200- const Elf64_Nhdr* nhdr = reinterpret_cast <const Elf64_Nhdr*>(data + offset);
1206+ // Copy the note header into an aligned local: note_data is base +
1207+ // p_offset and p_offset is attacker-controlled, so dereferencing an
1208+ // Elf64_Nhdr* in place could be a misaligned load (UB, and a fault on
1209+ // alignment-strict architectures). The size check above guarantees the
1210+ // whole header is in bounds.
1211+ Elf64_Nhdr nhdr;
1212+ memcpy (&nhdr, data + offset, sizeof (nhdr));
12011213
1202- // Calculate aligned sizes (4-byte alignment as per ELF spec)
1203- size_t name_size_aligned = (nhdr->n_namesz + 3 ) & ~static_cast <size_t >(3 );
1204- size_t desc_size_aligned = (nhdr->n_descsz + 3 ) & ~static_cast <size_t >(3 );
1214+ // Calculate aligned sizes (4-byte alignment as per ELF spec). Promote to
1215+ // size_t before the +3 so a near-UINT32_MAX n_namesz/n_descsz cannot wrap
1216+ // to a small value and defeat the bounds checks below.
1217+ size_t name_size_aligned = (static_cast <size_t >(nhdr.n_namesz ) + 3 ) & ~static_cast <size_t >(3 );
1218+ size_t desc_size_aligned = (static_cast <size_t >(nhdr.n_descsz ) + 3 ) & ~static_cast <size_t >(3 );
12051219
12061220 // Check bounds using subtraction to avoid overflow
12071221 size_t remaining = note_size - offset - sizeof (Elf64_Nhdr);
@@ -1214,13 +1228,13 @@ const uint8_t* SymbolsLinux::findBuildIdInNotes(const void* note_data, size_t no
12141228 }
12151229
12161230 // Check if this is a GNU build-id note
1217- if (nhdr-> n_type == NT_GNU_BUILD_ID && nhdr-> n_namesz > 0 && nhdr-> n_descsz > 0 ) {
1231+ if (nhdr. n_type == NT_GNU_BUILD_ID && nhdr. n_namesz > 0 && nhdr. n_descsz > 0 ) {
12181232 const char * name = data + offset + sizeof (Elf64_Nhdr);
12191233
12201234 // Verify GNU build-id name (including null terminator)
1221- if (nhdr-> n_namesz == 4 && strncmp (name, GNU_BUILD_ID_NAME , 3 ) == 0 && name[3 ] == ' \0 ' ) {
1235+ if (nhdr. n_namesz == 4 && strncmp (name, GNU_BUILD_ID_NAME , 3 ) == 0 && name[3 ] == ' \0 ' ) {
12221236 const uint8_t * desc = reinterpret_cast <const uint8_t *>(data + offset + sizeof (Elf64_Nhdr) + name_size_aligned);
1223- *build_id_len = nhdr-> n_descsz ;
1237+ *build_id_len = nhdr. n_descsz ;
12241238 return desc;
12251239 }
12261240 }
0 commit comments