2020-04-11 21:32:38 +03:00
/*
2021-05-30 16:28:59 +03:00
* Copyright ( c ) 2020 , Andrew Kaster < akaster @ serenityos . org >
2021-01-30 15:54:24 +03:00
* Copyright ( c ) 2021 , Andreas Kling < kling @ serenityos . org >
2020-04-11 21:32:38 +03:00
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-04-11 21:32:38 +03:00
*/
# include <AK/Assertions.h>
2021-01-30 15:54:24 +03:00
# include <AK/Checked.h>
2020-04-11 21:32:38 +03:00
# include <AK/String.h>
2021-04-16 22:53:43 +03:00
# include <LibC/elf.h>
2020-04-11 21:32:38 +03:00
# include <LibELF/Validation.h>
2021-06-23 23:54:41 +03:00
# include <limits.h>
2020-04-11 21:32:38 +03:00
namespace ELF {
2021-06-28 18:24:08 +03:00
bool validate_elf_header ( const ElfW ( Ehdr ) & elf_header , size_t file_size , bool verbose )
2020-04-11 21:32:38 +03:00
{
if ( ! IS_ELF ( elf_header ) ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File is not an ELF file. " ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-06-28 18:24:08 +03:00
# if ARCH(I386)
auto expected_class = ELFCLASS32 ;
auto expected_bitness = 32 ;
# else
auto expected_class = ELFCLASS64 ;
auto expected_bitness = 64 ;
# endif
if ( expected_class ! = elf_header . e_ident [ EI_CLASS ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " File is not a {}-bit ELF file. " , expected_bitness ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( ELFDATA2LSB ! = elf_header . e_ident [ EI_DATA ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File is not a little endian ELF file. " ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( EV_CURRENT ! = elf_header . e_ident [ EI_VERSION ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has unrecognized ELF version ({}), expected ({})! " , elf_header . e_ident [ EI_VERSION ] , EV_CURRENT ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( ELFOSABI_SYSV ! = elf_header . e_ident [ EI_OSABI ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has unknown OS ABI ({}), expected SYSV(0)! " , elf_header . e_ident [ EI_OSABI ] ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( 0 ! = elf_header . e_ident [ EI_ABIVERSION ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has unknown SYSV ABI version ({})! " , elf_header . e_ident [ EI_ABIVERSION ] ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-06-28 18:24:08 +03:00
# if ARCH(I386)
auto expected_machine = EM_386 ;
auto expected_machine_name = " i386 " ;
# else
auto expected_machine = EM_X86_64 ;
auto expected_machine_name = " x86-64 " ;
# endif
if ( expected_machine ! = elf_header . e_machine ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " File has unknown machine ({}), expected {} ({})! " , elf_header . e_machine , expected_machine_name , expected_machine ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2020-12-01 09:18:49 +03:00
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 ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has unloadable ELF type ({}), expected REL (1), EXEC (2), DYN (3) or CORE(4)! " , elf_header . e_type ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( EV_CURRENT ! = elf_header . e_version ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has unrecognized ELF version ({}), expected ({})! " , elf_header . e_version , EV_CURRENT ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-06-28 18:24:08 +03:00
if ( sizeof ( ElfW ( Ehdr ) ) ! = elf_header . e_ehsize ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " File has incorrect ELF header size..? ({}), expected ({})! " , elf_header . e_ehsize , sizeof ( ElfW ( Ehdr ) ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-03-29 15:14:41 +03:00
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 ) ) {
2020-12-01 09:18:49 +03:00
if ( verbose ) {
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! program header offset ({}) or section header offset ({}) overlap with ELF header! " ,
2020-12-01 09:18:49 +03:00
elf_header . e_phoff , elf_header . e_shoff ) ;
}
return false ;
}
2020-04-11 21:32:38 +03:00
if ( elf_header . e_phoff > file_size | | elf_header . e_shoff > file_size ) {
2020-08-10 16:55:17 +03:00
if ( verbose ) {
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! program header offset ({}) or section header offset ({}) are past the end of the file! " ,
2020-08-10 16:55:17 +03:00
elf_header . e_phoff , elf_header . e_shoff ) ;
}
2020-04-11 21:32:38 +03:00
return false ;
}
2020-12-01 09:18:49 +03:00
if ( elf_header . e_phnum = = 0 & & elf_header . e_phoff ! = 0 ) {
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! File has no program headers, but it does have a program header offset ({})! " , elf_header . e_phoff ) ;
2020-12-01 09:18:49 +03:00
return false ;
}
2020-04-11 21:32:38 +03:00
if ( elf_header . e_phnum ! = 0 & & elf_header . e_phoff ! = elf_header . e_ehsize ) {
2020-08-10 16:55:17 +03:00
if ( verbose ) {
2021-01-10 11:33:56 +03:00
dbgln ( " File does not have program headers directly after the ELF header? program header offset ({}), expected ({}). " ,
2020-08-10 16:55:17 +03:00
elf_header . e_phoff , elf_header . e_ehsize ) ;
}
2020-04-11 21:32:38 +03:00
return false ;
}
if ( 0 ! = elf_header . e_flags ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " File has incorrect ELF header flags...? ({}), expected ({}). " , elf_header . e_flags , 0 ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-06-28 18:24:08 +03:00
if ( 0 ! = elf_header . e_phnum & & sizeof ( ElfW ( Phdr ) ) ! = elf_header . e_phentsize ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " File has incorrect program header size..? ({}), expected ({}). " , elf_header . e_phentsize , sizeof ( ElfW ( Phdr ) ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-06-28 18:24:08 +03:00
if ( sizeof ( ElfW ( Shdr ) ) ! = elf_header . e_shentsize ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " File has incorrect section header size..? ({}), expected ({}). " , elf_header . e_shentsize , sizeof ( ElfW ( Shdr ) ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-01-30 15:54:24 +03:00
Checked < size_t > total_size_of_program_headers = elf_header . e_phnum ;
total_size_of_program_headers * = elf_header . e_phentsize ;
Checked < size_t > end_of_last_program_header = elf_header . e_phoff ;
end_of_last_program_header + = total_size_of_program_headers ;
if ( end_of_last_program_header . has_overflow ( ) ) {
if ( verbose )
dbgln ( " SHENANIGANS! Integer overflow in program header validation " ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
if ( end_of_last_program_header > file_size ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-30 15:54:24 +03:00
dbgln ( " SHENANIGANS! End of last program header ({}) is past the end of the file! " , end_of_last_program_header . value ( ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-01-30 15:54:24 +03:00
if ( elf_header . e_shoff ! = SHN_UNDEF & & elf_header . e_shoff < end_of_last_program_header . value ( ) ) {
2020-12-01 09:18:49 +03:00
if ( verbose ) {
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]! " ,
2021-01-30 15:54:24 +03:00
elf_header . e_shoff , elf_header . e_phoff , end_of_last_program_header . value ( ) ) ;
2020-12-01 09:18:49 +03:00
}
return false ;
}
2021-01-30 15:54:24 +03:00
Checked < size_t > total_size_of_section_headers = elf_header . e_shnum ;
total_size_of_section_headers * = elf_header . e_shentsize ;
Checked < size_t > end_of_last_section_header = elf_header . e_shoff ;
end_of_last_section_header + = total_size_of_section_headers ;
if ( end_of_last_section_header . has_overflow ( ) ) {
if ( verbose )
dbgln ( " SHENANIGANS! Integer overflow in section header validation " ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
if ( end_of_last_section_header > file_size ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-30 15:54:24 +03:00
dbgln ( " SHENANIGANS! End of last section header ({}) is past the end of the file! " , end_of_last_section_header . value ( ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2020-11-11 23:04:33 +03:00
if ( elf_header . e_shstrndx ! = SHN_UNDEF & & elf_header . e_shstrndx > = elf_header . e_shnum ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
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 ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
return true ;
}
2021-06-28 18:24:08 +03:00
bool validate_program_headers ( const ElfW ( Ehdr ) & elf_header , size_t file_size , const u8 * buffer , size_t buffer_size , String * interpreter_path , bool verbose )
2020-04-11 21:32:38 +03:00
{
2021-01-30 15:54:24 +03:00
Checked < size_t > total_size_of_program_headers = elf_header . e_phnum ;
total_size_of_program_headers * = elf_header . e_phentsize ;
Checked < size_t > end_of_last_program_header = elf_header . e_phoff ;
end_of_last_program_header + = total_size_of_program_headers ;
if ( end_of_last_program_header . has_overflow ( ) ) {
if ( verbose )
dbgln ( " SHENANIGANS! Integer overflow in program header validation " ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
// Can we actually parse all the program headers in the given buffer?
if ( end_of_last_program_header > buffer_size ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-30 15:54:24 +03:00
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 ( ) ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( file_size < buffer_size ) {
2021-01-10 11:33:56 +03:00
dbgln ( " We somehow read more from a file than was in the file in the first place! " ) ;
2021-02-23 22:42:32 +03:00
VERIFY_NOT_REACHED ( ) ;
2020-04-11 21:32:38 +03:00
}
size_t num_program_headers = elf_header . e_phnum ;
2021-06-28 18:24:08 +03:00
auto program_header_begin = ( const ElfW ( Phdr ) * ) & ( buffer [ elf_header . e_phoff ] ) ;
2020-04-11 21:32:38 +03:00
for ( size_t header_index = 0 ; header_index < num_program_headers ; + + header_index ) {
auto & program_header = program_header_begin [ header_index ] ;
2020-12-28 01:01:56 +03:00
if ( program_header . p_filesz > program_header . p_memsz ) {
if ( verbose )
dbgln ( " Program header ({}) has p_filesz ({}) larger than p_memsz ({}) " , header_index , program_header . p_filesz , program_header . p_memsz ) ;
return false ;
}
2021-01-01 05:36:55 +03:00
if ( program_header . p_memsz < = 0 & & ( program_header . p_type = = PT_TLS | | program_header . p_type = = PT_LOAD ) ) {
if ( verbose )
dbgln ( " Program header ({}) has invalid size in memory ({}) " , header_index , program_header . p_memsz ) ;
return false ;
}
2021-06-28 18:24:08 +03:00
if ( program_header . p_type = = PT_LOAD & & program_header . p_align % ( size_t ) PAGE_SIZE ! = 0 ) {
if ( elf_header . e_type ! = ET_CORE ) {
if ( verbose )
dbgln ( " Program header ({}) with p_type PT_LOAD has p_align ({}) not divisible by page size ({}) " , header_index , program_header . p_align , PAGE_SIZE ) ;
return false ;
}
}
if ( program_header . p_type = = PT_LOAD & & program_header . p_vaddr % program_header . p_align ! = program_header . p_offset % program_header . p_align ) {
2021-01-01 05:36:55 +03:00
if ( elf_header . e_type ! = ET_CORE ) {
if ( verbose )
2021-06-28 18:24:08 +03:00
dbgln ( " Program header ({}) with p_type PT_LOAD has mis-aligned p_vaddr ({:x}) " , header_index , program_header . p_vaddr ) ;
2021-01-01 05:36:55 +03:00
return false ;
}
}
2020-04-11 21:32:38 +03:00
switch ( program_header . p_type ) {
case PT_INTERP :
// We checked above that file_size was >= buffer size. We only care about buffer size anyway, we're trying to read this!
2021-01-30 15:54:24 +03:00
if ( Checked < size_t > : : addition_would_overflow ( program_header . p_offset , program_header . p_filesz ) ) {
if ( verbose )
dbgln ( " Integer overflow while validating PT_INTERP header " ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
if ( program_header . p_offset + program_header . p_filesz > buffer_size ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " Found PT_INTERP header ({}), but the .interp section was not within the buffer :( " , header_index ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
2021-01-16 20:35:46 +03:00
if ( program_header . p_filesz < = 1 ) {
if ( verbose )
dbgln ( " Found PT_INTERP header ({}), but p_filesz is invalid ({}) " , header_index , program_header . p_filesz ) ;
return false ;
}
2020-12-01 09:21:55 +03:00
if ( interpreter_path )
* interpreter_path = String ( ( const char * ) & buffer [ program_header . p_offset ] , program_header . p_filesz - 1 ) ;
2020-04-11 21:32:38 +03:00
break ;
case PT_LOAD :
case PT_DYNAMIC :
2021-04-16 22:48:57 +03:00
case PT_GNU_EH_FRAME :
2020-04-11 21:32:38 +03:00
case PT_NOTE :
case PT_PHDR :
case PT_TLS :
2021-01-30 15:54:24 +03:00
if ( Checked < size_t > : : addition_would_overflow ( program_header . p_offset , program_header . p_filesz ) ) {
if ( verbose )
dbgln ( " Integer overflow while validating a program header " ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
if ( program_header . p_offset + program_header . p_filesz > file_size ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! Program header {} segment leaks beyond end of file! " , header_index ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
if ( ( program_header . p_flags & PF_X ) & & ( program_header . p_flags & PF_W ) ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! Program header {} segment is marked write and execute " , header_index ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
break ;
2020-08-20 11:08:02 +03:00
case PT_GNU_STACK :
if ( program_header . p_flags & PF_X ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " Possible shenanigans! Validating an ELF with executable stack. " ) ;
2020-08-20 11:08:02 +03:00
}
break ;
2020-09-09 10:40:17 +03:00
case PT_GNU_RELRO :
if ( ( program_header . p_flags & PF_X ) & & ( program_header . p_flags & PF_W ) ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " SHENANIGANS! Program header {} segment is marked write and execute " , header_index ) ;
2020-09-09 10:40:17 +03:00
return false ;
}
break ;
2020-04-11 21:32:38 +03:00
default :
// Not handling other program header types in other code so... let's not surprise them
2020-12-01 09:21:55 +03:00
if ( verbose )
2021-01-10 11:33:56 +03:00
dbgln ( " Found program header ({}) of unrecognized type {}! " , header_index , program_header . p_type ) ;
2020-04-11 21:32:38 +03:00
return false ;
}
}
return true ;
}
} // end namespace ELF