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>
2022-10-01 22:29:59 +03:00
# include <Kernel/API/serenity_limits.h>
2023-04-30 18:07:21 +03:00
# include <LibELF/ELFABI.h>
2020-04-11 21:32:38 +03:00
# include <LibELF/Validation.h>
2023-01-07 23:57:33 +03:00
# ifndef KERNEL
# include <limits.h>
# include <pthread.h>
# endif
2020-04-11 21:32:38 +03:00
namespace ELF {
2022-01-13 21:54:53 +03:00
bool validate_elf_header ( ElfW ( Ehdr ) const & 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
auto expected_class = ELFCLASS64 ;
auto expected_bitness = 64 ;
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 ;
}
2022-03-05 04:02:23 +03:00
// NOTE: With Clang, -fprofile-instr-generate -fcoverage-mapping sets our ELF ABI Version to 3 b/c of SHF_GNU_RETAIN
if ( ELFOSABI_SYSV ! = elf_header . e_ident [ EI_OSABI ] & & ELFOSABI_LINUX ! = elf_header . e_ident [ EI_OSABI ] ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2022-03-05 04:02:23 +03:00
dbgln ( " File has unknown OS ABI ({}), expected SYSV(0) or GNU/Linux(3)! " , 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 ;
}
2023-10-13 21:10:33 +03:00
auto expected_machines = Array { EM_X86_64 , EM_AARCH64 , EM_RISCV } ;
auto expected_machine_names = Array { " x86-64 " sv , " aarch64 " sv , " riscv64 " sv } ;
2021-06-28 18:24:08 +03:00
2023-01-29 15:11:30 +03:00
if ( ! expected_machines . span ( ) . contains_slow ( elf_header . e_machine ) ) {
2020-08-10 16:55:17 +03:00
if ( verbose )
2023-01-29 15:11:30 +03:00
dbgln ( " File has unknown machine ({}), expected {} ({})! " , elf_header . e_machine , expected_machine_names . span ( ) , expected_machines . span ( ) ) ;
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 ) {
2023-10-13 21:10:33 +03:00
// TODO: Refuse to run C ABI binaries on system without the C extension.
// TODO: Refuse to run TSO ABI binaries on system without the Ztso extension.
if ( elf_header . e_machine = = EM_RISCV ) {
auto float_abi = elf_header . e_flags & EF_RISCV_FLOAT_ABI ;
// TODO: Support 32-bit hardware float ABI somehow?
if ( float_abi ! = EF_RISCV_FLOAT_ABI_DOUBLE ) {
if ( verbose )
dbgln ( " File has unsupported float ABI ({}), only double ({}) is supported. " , float_abi , EF_RISCV_FLOAT_ABI_DOUBLE ) ;
return false ;
}
} else {
if ( verbose )
dbgln ( " File has incorrect ELF header flags...? ({}), expected ({}). " , elf_header . e_flags , 0 ) ;
return false ;
}
2020-04-11 21:32:38 +03:00
}
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 ;
}
2023-07-10 12:13:13 +03:00
ErrorOr < bool > validate_program_headers ( ElfW ( Ehdr ) const & elf_header , size_t file_size , ReadonlyBytes buffer , StringBuilder * interpreter_path_builder , Optional < size_t > * requested_stack_size , 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?
2022-01-13 21:54:53 +03:00
if ( end_of_last_program_header > buffer . size ( ) ) {
2020-12-01 09:21:55 +03:00
if ( verbose )
2022-01-13 21:54:53 +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 ;
}
2022-01-13 21:54:53 +03:00
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 ;
2022-01-13 21:54:53 +03:00
auto program_header_begin = ( const ElfW ( Phdr ) * ) buffer . offset ( 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 ) ;
2021-01-01 05:36:55 +03:00
return false ;
}
2021-06-29 11:43:58 +03:00
if ( elf_header . e_type ! = ET_CORE ) {
2021-06-30 19:59:03 +03:00
if ( program_header . p_type = = PT_LOAD & & program_header . p_align = = 0 ) {
if ( verbose )
dbgln ( " Program header ({}) with p_type PT_LOAD missing p_align (p_align == 0) " , header_index ) ;
return false ;
}
2021-06-29 11:43:58 +03:00
if ( program_header . p_type = = PT_LOAD & & program_header . p_align % ( size_t ) PAGE_SIZE ! = 0 ) {
2021-06-28 18:24:08 +03:00
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 ;
}
2021-06-29 11:43:58 +03:00
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 ( 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 ;
}
2022-01-13 21:54:53 +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 ;
}
2022-01-13 19:50:17 +03:00
if ( interpreter_path_builder )
2023-09-03 22:11:39 +03:00
TRY ( interpreter_path_builder - > try_append ( { buffer . offset ( program_header . p_offset ) , static_cast < size_t > ( 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
}
2022-10-01 22:29:59 +03:00
if ( program_header . p_memsz ! = 0 ) {
2023-09-03 22:11:39 +03:00
if (
# ifdef PTHREAD_STACK_MIN
program_header . p_memsz < static_cast < unsigned > ( PTHREAD_STACK_MIN ) | |
# endif
program_header . p_memsz > static_cast < unsigned > ( PTHREAD_STACK_MAX ) ) {
2022-10-01 22:29:59 +03:00
if ( verbose )
dbgln ( " PT_GNU_STACK defines an unacceptable stack size. " ) ;
return false ;
}
if ( program_header . p_memsz % PAGE_SIZE ! = 0 ) {
if ( verbose )
dbgln ( " PT_GNU_STACK size is not page-aligned. " ) ;
return false ;
}
2023-07-10 12:13:13 +03:00
if ( requested_stack_size )
* requested_stack_size = program_header . p_memsz ;
2022-10-01 22:29:59 +03:00
}
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