2020-04-11 12:32:38 -06:00
/*
* Copyright ( c ) 2020 , Andrew Kaster < andrewdkaster @ gmail . com >
2021-01-30 13:54:24 +01:00
* Copyright ( c ) 2021 , Andreas Kling < kling @ serenityos . org >
2020-04-11 12:32:38 -06:00
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
*
* 1. Redistributions of source code must retain the above copyright notice , this
* list of conditions and the following disclaimer .
*
* 2. Redistributions in binary form must reproduce the above copyright notice ,
* this list of conditions and the following disclaimer in the documentation
* and / or other materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS "
* AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY ,
* OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <AK/Assertions.h>
2021-01-30 13:54:24 +01:00
# include <AK/Checked.h>
2020-04-11 12:32:38 -06:00
# include <AK/String.h>
# include <LibELF/Validation.h>
2020-09-18 09:49:51 +02:00
# include <LibELF/exec_elf.h>
2020-04-11 12:32:38 -06:00
namespace ELF {
2020-08-10 09:55:17 -04:00
bool validate_elf_header ( const Elf32_Ehdr & elf_header , size_t file_size , bool verbose )
2020-04-11 12:32:38 -06:00
{
if ( ! IS_ELF ( elf_header ) ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File is not an ELF file. " ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( ELFCLASS32 ! = elf_header . e_ident [ EI_CLASS ] ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File is not a 32 bit ELF file. " ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( ELFDATA2LSB ! = elf_header . e_ident [ EI_DATA ] ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File is not a little endian ELF file. " ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( EV_CURRENT ! = elf_header . e_ident [ EI_VERSION ] ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unrecognized ELF version ({}), expected ({})! " , elf_header . e_ident [ EI_VERSION ] , EV_CURRENT ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( ELFOSABI_SYSV ! = elf_header . e_ident [ EI_OSABI ] ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unknown OS ABI ({}), expected SYSV(0)! " , elf_header . e_ident [ EI_OSABI ] ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( 0 ! = elf_header . e_ident [ EI_ABIVERSION ] ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unknown SYSV ABI version ({})! " , elf_header . e_ident [ EI_ABIVERSION ] ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( EM_386 ! = elf_header . e_machine ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unknown machine ({}), expected i386 (3)! " , elf_header . e_machine ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
2020-11-30 23:18:49 -07: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 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unloadable ELF type ({}), expected REL (1), EXEC (2), DYN (3) or CORE(4)! " , elf_header . e_type ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( EV_CURRENT ! = elf_header . e_version ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has unrecognized ELF version ({}), expected ({})! " , elf_header . e_version , EV_CURRENT ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( sizeof ( Elf32_Ehdr ) ! = elf_header . e_ehsize ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has incorrect ELF header size..? ({}), expected ({})! " , elf_header . e_ehsize , sizeof ( Elf32_Ehdr ) ) ;
2020-04-11 12:32:38 -06: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-11-30 23:18:49 -07:00
if ( verbose ) {
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! program header offset ({}) or section header offset ({}) overlap with ELF header! " ,
2020-11-30 23:18:49 -07:00
elf_header . e_phoff , elf_header . e_shoff ) ;
}
return false ;
}
2020-04-11 12:32:38 -06:00
if ( elf_header . e_phoff > file_size | | elf_header . e_shoff > file_size ) {
2020-08-10 09:55:17 -04:00
if ( verbose ) {
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! program header offset ({}) or section header offset ({}) are past the end of the file! " ,
2020-08-10 09:55:17 -04:00
elf_header . e_phoff , elf_header . e_shoff ) ;
}
2020-04-11 12:32:38 -06:00
return false ;
}
2020-11-30 23:18:49 -07:00
if ( elf_header . e_phnum = = 0 & & elf_header . e_phoff ! = 0 ) {
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! File has no program headers, but it does have a program header offset ({})! " , elf_header . e_phoff ) ;
2020-11-30 23:18:49 -07:00
return false ;
}
2020-04-11 12:32:38 -06:00
if ( elf_header . e_phnum ! = 0 & & elf_header . e_phoff ! = elf_header . e_ehsize ) {
2020-08-10 09:55:17 -04:00
if ( verbose ) {
2021-01-10 09:33:56 +01:00
dbgln ( " File does not have program headers directly after the ELF header? program header offset ({}), expected ({}). " ,
2020-08-10 09:55:17 -04:00
elf_header . e_phoff , elf_header . e_ehsize ) ;
}
2020-04-11 12:32:38 -06:00
return false ;
}
if ( 0 ! = elf_header . e_flags ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has incorrect ELF header flags...? ({}), expected ({}). " , elf_header . e_flags , 0 ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( 0 ! = elf_header . e_phnum & & sizeof ( Elf32_Phdr ) ! = elf_header . e_phentsize ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has incorrect program header size..? ({}), expected ({}). " , elf_header . e_phentsize , sizeof ( Elf32_Phdr ) ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( sizeof ( Elf32_Shdr ) ! = elf_header . e_shentsize ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " File has incorrect section header size..? ({}), expected ({}). " , elf_header . e_shentsize , sizeof ( Elf32_Shdr ) ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
2021-01-30 13:54:24 +01: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 12:32:38 -06:00
if ( end_of_last_program_header > file_size ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-30 13:54:24 +01:00
dbgln ( " SHENANIGANS! End of last program header ({}) is past the end of the file! " , end_of_last_program_header . value ( ) ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
2021-01-30 13:54:24 +01:00
if ( elf_header . e_shoff ! = SHN_UNDEF & & elf_header . e_shoff < end_of_last_program_header . value ( ) ) {
2020-11-30 23:18:49 -07:00
if ( verbose ) {
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]! " ,
2021-01-30 13:54:24 +01:00
elf_header . e_shoff , elf_header . e_phoff , end_of_last_program_header . value ( ) ) ;
2020-11-30 23:18:49 -07:00
}
return false ;
}
2021-01-30 13:54:24 +01: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 12:32:38 -06:00
if ( end_of_last_section_header > file_size ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-30 13:54:24 +01:00
dbgln ( " SHENANIGANS! End of last section header ({}) is past the end of the file! " , end_of_last_section_header . value ( ) ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
2020-11-11 22:04:33 +02:00
if ( elf_header . e_shstrndx ! = SHN_UNDEF & & elf_header . e_shstrndx > = elf_header . e_shnum ) {
2020-08-10 09:55:17 -04:00
if ( verbose )
2021-01-10 09:33:56 +01: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 12:32:38 -06:00
return false ;
}
return true ;
}
2020-11-30 23:21:55 -07:00
bool validate_program_headers ( const Elf32_Ehdr & elf_header , size_t file_size , const u8 * buffer , size_t buffer_size , String * interpreter_path , bool verbose )
2020-04-11 12:32:38 -06:00
{
2021-01-30 13:54:24 +01: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 12:32:38 -06:00
// Can we actually parse all the program headers in the given buffer?
if ( end_of_last_program_header > buffer_size ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-30 13:54:24 +01: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 12:32:38 -06:00
return false ;
}
if ( file_size < buffer_size ) {
2021-01-10 09:33:56 +01:00
dbgln ( " We somehow read more from a file than was in the file in the first place! " ) ;
2021-02-23 20:42:32 +01:00
VERIFY_NOT_REACHED ( ) ;
2020-04-11 12:32:38 -06:00
}
size_t num_program_headers = elf_header . e_phnum ;
auto program_header_begin = ( const Elf32_Phdr * ) & ( buffer [ elf_header . e_phoff ] ) ;
for ( size_t header_index = 0 ; header_index < num_program_headers ; + + header_index ) {
auto & program_header = program_header_begin [ header_index ] ;
2020-12-27 22:01:56 +00: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 02:36:55 +00: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 ;
}
if ( program_header . p_type = = PT_LOAD & & program_header . p_align ! = PAGE_SIZE ) {
if ( elf_header . e_type ! = ET_CORE ) {
if ( verbose )
dbgln ( " Program header ({}) with p_type PT_LOAD has p_align ({}) not equal to page size ({}) " , header_index , program_header . p_align , PAGE_SIZE ) ;
return false ;
}
}
2020-04-11 12:32:38 -06: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 13:54:24 +01: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 12:32:38 -06:00
if ( program_header . p_offset + program_header . p_filesz > buffer_size ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " Found PT_INTERP header ({}), but the .interp section was not within the buffer :( " , header_index ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
2021-01-16 17:35:46 +00: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-11-30 23:21:55 -07:00
if ( interpreter_path )
* interpreter_path = String ( ( const char * ) & buffer [ program_header . p_offset ] , program_header . p_filesz - 1 ) ;
2020-04-11 12:32:38 -06:00
break ;
case PT_LOAD :
case PT_DYNAMIC :
case PT_NOTE :
case PT_PHDR :
case PT_TLS :
2021-01-30 13:54:24 +01: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 12:32:38 -06:00
if ( program_header . p_offset + program_header . p_filesz > file_size ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! Program header {} segment leaks beyond end of file! " , header_index ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
if ( ( program_header . p_flags & PF_X ) & & ( program_header . p_flags & PF_W ) ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! Program header {} segment is marked write and execute " , header_index ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
break ;
2020-08-20 10:08:02 +02:00
case PT_GNU_STACK :
if ( program_header . p_flags & PF_X ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " Possible shenanigans! Validating an ELF with executable stack. " ) ;
2020-08-20 10:08:02 +02:00
}
break ;
2020-09-09 08:40:17 +01:00
case PT_GNU_RELRO :
if ( ( program_header . p_flags & PF_X ) & & ( program_header . p_flags & PF_W ) ) {
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " SHENANIGANS! Program header {} segment is marked write and execute " , header_index ) ;
2020-09-09 08:40:17 +01:00
return false ;
}
break ;
2020-04-11 12:32:38 -06:00
default :
// Not handling other program header types in other code so... let's not surprise them
2020-11-30 23:21:55 -07:00
if ( verbose )
2021-01-10 09:33:56 +01:00
dbgln ( " Found program header ({}) of unrecognized type {}! " , header_index , program_header . p_type ) ;
2020-04-11 12:32:38 -06:00
return false ;
}
}
return true ;
}
} // end namespace ELF