Serenity Operating System
at master 329 lines 14 kB view raw
1/* 2 * Copyright (c) 2020, Andrew Kaster <akaster@serenityos.org> 3 * Copyright (c) 2021, Andreas Kling <kling@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <AK/Assertions.h> 9#include <AK/Checked.h> 10#include <Kernel/API/serenity_limits.h> 11#include <LibC/elf.h> 12#include <LibELF/Validation.h> 13 14#ifndef KERNEL 15# include <limits.h> 16# include <pthread.h> 17#endif 18 19namespace ELF { 20 21bool validate_elf_header(ElfW(Ehdr) const& elf_header, size_t file_size, bool verbose) 22{ 23 if (!IS_ELF(elf_header)) { 24 if (verbose) 25 dbgln("File is not an ELF file."); 26 return false; 27 } 28 29 auto expected_class = ELFCLASS64; 30 auto expected_bitness = 64; 31 if (expected_class != elf_header.e_ident[EI_CLASS]) { 32 if (verbose) 33 dbgln("File is not a {}-bit ELF file.", expected_bitness); 34 return false; 35 } 36 37 if (ELFDATA2LSB != elf_header.e_ident[EI_DATA]) { 38 if (verbose) 39 dbgln("File is not a little endian ELF file."); 40 return false; 41 } 42 43 if (EV_CURRENT != elf_header.e_ident[EI_VERSION]) { 44 if (verbose) 45 dbgln("File has unrecognized ELF version ({}), expected ({})!", elf_header.e_ident[EI_VERSION], EV_CURRENT); 46 return false; 47 } 48 49 // NOTE: With Clang, -fprofile-instr-generate -fcoverage-mapping sets our ELF ABI Version to 3 b/c of SHF_GNU_RETAIN 50 if (ELFOSABI_SYSV != elf_header.e_ident[EI_OSABI] && ELFOSABI_LINUX != elf_header.e_ident[EI_OSABI]) { 51 if (verbose) 52 dbgln("File has unknown OS ABI ({}), expected SYSV(0) or GNU/Linux(3)!", elf_header.e_ident[EI_OSABI]); 53 return false; 54 } 55 56 if (0 != elf_header.e_ident[EI_ABIVERSION]) { 57 if (verbose) 58 dbgln("File has unknown SYSV ABI version ({})!", elf_header.e_ident[EI_ABIVERSION]); 59 return false; 60 } 61 62 auto expected_machines = Array { EM_X86_64, EM_AARCH64 }; 63 auto expected_machine_names = Array { "x86-64"sv, "aarch64"sv }; 64 65 if (!expected_machines.span().contains_slow(elf_header.e_machine)) { 66 if (verbose) 67 dbgln("File has unknown machine ({}), expected {} ({})!", elf_header.e_machine, expected_machine_names.span(), expected_machines.span()); 68 return false; 69 } 70 71 if (ET_EXEC != elf_header.e_type && ET_DYN != elf_header.e_type && ET_REL != elf_header.e_type && ET_CORE != elf_header.e_type) { 72 if (verbose) 73 dbgln("File has unloadable ELF type ({}), expected REL (1), EXEC (2), DYN (3) or CORE(4)!", elf_header.e_type); 74 return false; 75 } 76 77 if (EV_CURRENT != elf_header.e_version) { 78 if (verbose) 79 dbgln("File has unrecognized ELF version ({}), expected ({})!", elf_header.e_version, EV_CURRENT); 80 return false; 81 } 82 83 if (sizeof(ElfW(Ehdr)) != elf_header.e_ehsize) { 84 if (verbose) 85 dbgln("File has incorrect ELF header size..? ({}), expected ({})!", elf_header.e_ehsize, sizeof(ElfW(Ehdr))); 86 return false; 87 } 88 89 if ((elf_header.e_phnum != 0 && elf_header.e_phoff < elf_header.e_ehsize) || (elf_header.e_shnum != SHN_UNDEF && elf_header.e_shoff < elf_header.e_ehsize)) { 90 if (verbose) { 91 dbgln("SHENANIGANS! program header offset ({}) or section header offset ({}) overlap with ELF header!", 92 elf_header.e_phoff, elf_header.e_shoff); 93 } 94 return false; 95 } 96 97 if (elf_header.e_phoff > file_size || elf_header.e_shoff > file_size) { 98 if (verbose) { 99 dbgln("SHENANIGANS! program header offset ({}) or section header offset ({}) are past the end of the file!", 100 elf_header.e_phoff, elf_header.e_shoff); 101 } 102 return false; 103 } 104 105 if (elf_header.e_phnum == 0 && elf_header.e_phoff != 0) { 106 if (verbose) 107 dbgln("SHENANIGANS! File has no program headers, but it does have a program header offset ({})!", elf_header.e_phoff); 108 return false; 109 } 110 111 if (elf_header.e_phnum != 0 && elf_header.e_phoff != elf_header.e_ehsize) { 112 if (verbose) { 113 dbgln("File does not have program headers directly after the ELF header? program header offset ({}), expected ({}).", 114 elf_header.e_phoff, elf_header.e_ehsize); 115 } 116 return false; 117 } 118 119 if (0 != elf_header.e_flags) { 120 if (verbose) 121 dbgln("File has incorrect ELF header flags...? ({}), expected ({}).", elf_header.e_flags, 0); 122 return false; 123 } 124 125 if (0 != elf_header.e_phnum && sizeof(ElfW(Phdr)) != elf_header.e_phentsize) { 126 if (verbose) 127 dbgln("File has incorrect program header size..? ({}), expected ({}).", elf_header.e_phentsize, sizeof(ElfW(Phdr))); 128 return false; 129 } 130 131 if (sizeof(ElfW(Shdr)) != elf_header.e_shentsize) { 132 if (verbose) 133 dbgln("File has incorrect section header size..? ({}), expected ({}).", elf_header.e_shentsize, sizeof(ElfW(Shdr))); 134 return false; 135 } 136 137 Checked<size_t> total_size_of_program_headers = elf_header.e_phnum; 138 total_size_of_program_headers *= elf_header.e_phentsize; 139 140 Checked<size_t> end_of_last_program_header = elf_header.e_phoff; 141 end_of_last_program_header += total_size_of_program_headers; 142 143 if (end_of_last_program_header.has_overflow()) { 144 if (verbose) 145 dbgln("SHENANIGANS! Integer overflow in program header validation"); 146 return false; 147 } 148 149 if (end_of_last_program_header > file_size) { 150 if (verbose) 151 dbgln("SHENANIGANS! End of last program header ({}) is past the end of the file!", end_of_last_program_header.value()); 152 return false; 153 } 154 155 if (elf_header.e_shoff != SHN_UNDEF && elf_header.e_shoff < end_of_last_program_header.value()) { 156 if (verbose) { 157 dbgln("SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]!", 158 elf_header.e_shoff, elf_header.e_phoff, end_of_last_program_header.value()); 159 } 160 return false; 161 } 162 163 Checked<size_t> total_size_of_section_headers = elf_header.e_shnum; 164 total_size_of_section_headers *= elf_header.e_shentsize; 165 166 Checked<size_t> end_of_last_section_header = elf_header.e_shoff; 167 end_of_last_section_header += total_size_of_section_headers; 168 169 if (end_of_last_section_header.has_overflow()) { 170 if (verbose) 171 dbgln("SHENANIGANS! Integer overflow in section header validation"); 172 return false; 173 } 174 175 if (end_of_last_section_header > file_size) { 176 if (verbose) 177 dbgln("SHENANIGANS! End of last section header ({}) is past the end of the file!", end_of_last_section_header.value()); 178 return false; 179 } 180 181 if (elf_header.e_shstrndx != SHN_UNDEF && elf_header.e_shstrndx >= elf_header.e_shnum) { 182 if (verbose) 183 dbgln("SHENANIGANS! Section header string table index ({}) is not a valid index given we have {} section headers!", elf_header.e_shstrndx, elf_header.e_shnum); 184 return false; 185 } 186 187 return true; 188} 189 190ErrorOr<bool> validate_program_headers(ElfW(Ehdr) const& elf_header, size_t file_size, ReadonlyBytes buffer, StringBuilder* interpreter_path_builder, bool verbose) 191{ 192 Checked<size_t> total_size_of_program_headers = elf_header.e_phnum; 193 total_size_of_program_headers *= elf_header.e_phentsize; 194 195 Checked<size_t> end_of_last_program_header = elf_header.e_phoff; 196 end_of_last_program_header += total_size_of_program_headers; 197 198 if (end_of_last_program_header.has_overflow()) { 199 if (verbose) 200 dbgln("SHENANIGANS! Integer overflow in program header validation"); 201 return false; 202 } 203 204 // Can we actually parse all the program headers in the given buffer? 205 if (end_of_last_program_header > buffer.size()) { 206 if (verbose) 207 dbgln("Unable to parse program headers from buffer, buffer too small! Buffer size: {}, End of program headers {}", buffer.size(), end_of_last_program_header.value()); 208 return false; 209 } 210 211 if (file_size < buffer.size()) { 212 dbgln("We somehow read more from a file than was in the file in the first place!"); 213 VERIFY_NOT_REACHED(); 214 } 215 216 size_t num_program_headers = elf_header.e_phnum; 217 auto program_header_begin = (const ElfW(Phdr)*)buffer.offset(elf_header.e_phoff); 218 219 for (size_t header_index = 0; header_index < num_program_headers; ++header_index) { 220 auto& program_header = program_header_begin[header_index]; 221 222 if (program_header.p_filesz > program_header.p_memsz) { 223 if (verbose) 224 dbgln("Program header ({}) has p_filesz ({}) larger than p_memsz ({})", header_index, program_header.p_filesz, program_header.p_memsz); 225 return false; 226 } 227 228 if (elf_header.e_type != ET_CORE) { 229 if (program_header.p_type == PT_LOAD && program_header.p_align == 0) { 230 if (verbose) 231 dbgln("Program header ({}) with p_type PT_LOAD missing p_align (p_align == 0)", header_index); 232 return false; 233 } 234 235 if (program_header.p_type == PT_LOAD && program_header.p_align % (size_t)PAGE_SIZE != 0) { 236 if (verbose) 237 dbgln("Program header ({}) with p_type PT_LOAD has p_align ({}) not divisible by page size ({})", header_index, program_header.p_align, PAGE_SIZE); 238 return false; 239 } 240 241 if (program_header.p_type == PT_LOAD && program_header.p_vaddr % program_header.p_align != program_header.p_offset % program_header.p_align) { 242 if (verbose) 243 dbgln("Program header ({}) with p_type PT_LOAD has mis-aligned p_vaddr ({:x})", header_index, program_header.p_vaddr); 244 return false; 245 } 246 } 247 248 switch (program_header.p_type) { 249 case PT_INTERP: 250 // We checked above that file_size was >= buffer size. We only care about buffer size anyway, we're trying to read this! 251 if (Checked<size_t>::addition_would_overflow(program_header.p_offset, program_header.p_filesz)) { 252 if (verbose) 253 dbgln("Integer overflow while validating PT_INTERP header"); 254 return false; 255 } 256 if (program_header.p_offset + program_header.p_filesz > buffer.size()) { 257 if (verbose) 258 dbgln("Found PT_INTERP header ({}), but the .interp section was not within the buffer :(", header_index); 259 return false; 260 } 261 if (program_header.p_filesz <= 1) { 262 if (verbose) 263 dbgln("Found PT_INTERP header ({}), but p_filesz is invalid ({})", header_index, program_header.p_filesz); 264 return false; 265 } 266 if (interpreter_path_builder) 267 TRY(interpreter_path_builder->try_append({ buffer.offset(program_header.p_offset), program_header.p_filesz - 1 })); 268 break; 269 case PT_LOAD: 270 case PT_DYNAMIC: 271 case PT_GNU_EH_FRAME: 272 case PT_NOTE: 273 case PT_PHDR: 274 case PT_TLS: 275 if (Checked<size_t>::addition_would_overflow(program_header.p_offset, program_header.p_filesz)) { 276 if (verbose) 277 dbgln("Integer overflow while validating a program header"); 278 return false; 279 } 280 if (program_header.p_offset + program_header.p_filesz > file_size) { 281 if (verbose) 282 dbgln("SHENANIGANS! Program header {} segment leaks beyond end of file!", header_index); 283 return false; 284 } 285 if ((program_header.p_flags & PF_X) && (program_header.p_flags & PF_W)) { 286 if (verbose) 287 dbgln("SHENANIGANS! Program header {} segment is marked write and execute", header_index); 288 return false; 289 } 290 break; 291 case PT_GNU_STACK: 292 if (program_header.p_flags & PF_X) { 293 if (verbose) 294 dbgln("Possible shenanigans! Validating an ELF with executable stack."); 295 } 296 297 if (program_header.p_memsz != 0) { 298 if (program_header.p_memsz < static_cast<unsigned>(PTHREAD_STACK_MIN) || program_header.p_memsz > static_cast<unsigned>(PTHREAD_STACK_MAX)) { 299 if (verbose) 300 dbgln("PT_GNU_STACK defines an unacceptable stack size."); 301 return false; 302 } 303 304 if (program_header.p_memsz % PAGE_SIZE != 0) { 305 if (verbose) 306 dbgln("PT_GNU_STACK size is not page-aligned."); 307 return false; 308 } 309 } 310 311 break; 312 case PT_GNU_RELRO: 313 if ((program_header.p_flags & PF_X) && (program_header.p_flags & PF_W)) { 314 if (verbose) 315 dbgln("SHENANIGANS! Program header {} segment is marked write and execute", header_index); 316 return false; 317 } 318 break; 319 default: 320 // Not handling other program header types in other code so... let's not surprise them 321 if (verbose) 322 dbgln("Found program header ({}) of unrecognized type {}!", header_index, program_header.p_type); 323 return false; 324 } 325 } 326 return true; 327} 328 329} // end namespace ELF