mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 12:07:45 +00:00
LibELF: Add a bunch of overflow checks in ELF validation
This commit is contained in:
parent
90343eeaeb
commit
5bf9999652
1 changed files with 54 additions and 10 deletions
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andrew Kaster <andrewdkaster@gmail.com>
|
* Copyright (c) 2020, Andrew Kaster <andrewdkaster@gmail.com>
|
||||||
|
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
|
#include <AK/Checked.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <LibELF/Validation.h>
|
#include <LibELF/Validation.h>
|
||||||
#include <LibELF/exec_elf.h>
|
#include <LibELF/exec_elf.h>
|
||||||
|
@ -141,25 +143,47 @@ bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool ve
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (end_of_last_program_header > file_size) {
|
if (end_of_last_program_header > file_size) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
dbgln("SHENANIGANS! End of last program header ({}) is past the end of the file!", end_of_last_program_header);
|
dbgln("SHENANIGANS! End of last program header ({}) is past the end of the file!", end_of_last_program_header.value());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elf_header.e_shoff != SHN_UNDEF && elf_header.e_shoff < end_of_last_program_header) {
|
if (elf_header.e_shoff != SHN_UNDEF && elf_header.e_shoff < end_of_last_program_header.value()) {
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
dbgln("SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]!",
|
dbgln("SHENANIGANS! Section header table begins at file offset {}, which is within program headers [ {} - {} ]!",
|
||||||
elf_header.e_shoff, elf_header.e_phoff, end_of_last_program_header);
|
elf_header.e_shoff, elf_header.e_phoff, end_of_last_program_header.value());
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t end_of_last_section_header = elf_header.e_shoff + (elf_header.e_shnum * elf_header.e_shentsize);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
if (end_of_last_section_header > file_size) {
|
if (end_of_last_section_header > file_size) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
dbgln("SHENANIGANS! End of last section header ({}) is past the end of the file!", end_of_last_section_header);
|
dbgln("SHENANIGANS! End of last section header ({}) is past the end of the file!", end_of_last_section_header.value());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,12 +198,22 @@ bool validate_elf_header(const Elf32_Ehdr& elf_header, size_t file_size, bool ve
|
||||||
|
|
||||||
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)
|
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)
|
||||||
{
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
// Can we actually parse all the program headers in the given buffer?
|
// Can we actually parse all the program headers in the given buffer?
|
||||||
size_t end_of_last_program_header = elf_header.e_phoff + (elf_header.e_phnum * elf_header.e_phentsize);
|
|
||||||
if (end_of_last_program_header > buffer_size) {
|
if (end_of_last_program_header > buffer_size) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
dbgln("Unable to parse program headers from buffer, buffer too small! Buffer size: {}, End of program headers {}",
|
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());
|
||||||
buffer_size, end_of_last_program_header);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,6 +251,11 @@ bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, co
|
||||||
switch (program_header.p_type) {
|
switch (program_header.p_type) {
|
||||||
case PT_INTERP:
|
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!
|
// We checked above that file_size was >= buffer size. We only care about buffer size anyway, we're trying to read this!
|
||||||
|
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;
|
||||||
|
}
|
||||||
if (program_header.p_offset + program_header.p_filesz > buffer_size) {
|
if (program_header.p_offset + program_header.p_filesz > buffer_size) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
dbgln("Found PT_INTERP header ({}), but the .interp section was not within the buffer :(", header_index);
|
dbgln("Found PT_INTERP header ({}), but the .interp section was not within the buffer :(", header_index);
|
||||||
|
@ -235,6 +274,11 @@ bool validate_program_headers(const Elf32_Ehdr& elf_header, size_t file_size, co
|
||||||
case PT_NOTE:
|
case PT_NOTE:
|
||||||
case PT_PHDR:
|
case PT_PHDR:
|
||||||
case PT_TLS:
|
case PT_TLS:
|
||||||
|
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;
|
||||||
|
}
|
||||||
if (program_header.p_offset + program_header.p_filesz > file_size) {
|
if (program_header.p_offset + program_header.p_filesz > file_size) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
dbgln("SHENANIGANS! Program header {} segment leaks beyond end of file!", header_index);
|
dbgln("SHENANIGANS! Program header {} segment leaks beyond end of file!", header_index);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue