fix(relinker): validate complete allocated section ranges
This commit is contained in:
parent
a11c8f7f70
commit
653eb704c2
|
|
@ -191,6 +191,22 @@ if(BUILD_TESTING)
|
|||
add_test(NAME linux_desktop_entry COMMAND linux_desktop_entry_tests)
|
||||
endif()
|
||||
|
||||
add_test_executable(section_header_ranges_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/tests/SectionRangeTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/general/SectionHeaderTableBuilder.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/io/src/ByteWriter.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/io/src/BufferUtils.cpp
|
||||
)
|
||||
target_include_directories(section_header_ranges_tests PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/domain/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/relinker/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/io/include
|
||||
)
|
||||
add_test(NAME section_header_ranges COMMAND section_header_ranges_tests)
|
||||
set_tests_properties(section_header_ranges PROPERTIES TIMEOUT 20)
|
||||
|
||||
add_test_executable(amd64_only_converter_tests
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/tests/Amd64OnlyConverterTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/Amd64OnlyConverter.cpp
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <elfpatcher/general/ElfConstants.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
|
||||
namespace Elfpatcher {
|
||||
|
||||
|
|
@ -43,18 +44,33 @@ void SectionHeaderTableBuilder::WriteTable(
|
|||
const auto phOffset = Io::ReadU64(buf, kEhdrPhOffOffset);
|
||||
const auto phCount = Io::ReadU16(buf, kEhdrPhNumOffset);
|
||||
const auto phStride = Io::ReadU16(buf, kEhdrPhEntSizeOffset);
|
||||
const auto mappedAddress = [&](std::uint64_t offset) {
|
||||
if (phStride < 56 || phOffset > buf.size() || phCount > (buf.size() - phOffset) / phStride)
|
||||
throw std::runtime_error("Output program header table is out of bounds");
|
||||
const auto mappedAddress = [&](std::uint64_t offset, std::uint64_t sectionSize) {
|
||||
if (offset > buf.size() || sectionSize > buf.size() - offset)
|
||||
throw std::runtime_error("Allocated output section exceeds file");
|
||||
for (std::uint16_t index = 0; index < phCount; ++index) {
|
||||
const auto header = phOffset + index * phStride;
|
||||
if (Io::ReadU32(buf, header) != PT_LOAD) continue;
|
||||
const auto start = Io::ReadU64(buf, header + 8);
|
||||
const auto size = Io::ReadU64(buf, header + 32);
|
||||
if (offset >= start && offset - start < size)
|
||||
return Io::ReadU64(buf, header + 16) + offset - start;
|
||||
if (offset >= start && offset - start <= size && sectionSize <= size - (offset - start)) {
|
||||
const auto relative = offset - start;
|
||||
const auto address = Io::ReadU64(buf, header + 16);
|
||||
if (relative > std::numeric_limits<std::uint64_t>::max() - address ||
|
||||
sectionSize > std::numeric_limits<std::uint64_t>::max() - (address + relative))
|
||||
throw std::runtime_error("Allocated output section address overflowed");
|
||||
return address + relative;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Allocated output section is outside load segments");
|
||||
};
|
||||
|
||||
const auto dynStrAddress = mappedAddress(request.DynStrOffset, request.DynStrSize);
|
||||
const auto dynSymAddress = mappedAddress(request.DynSymOffset, request.DynSymSize);
|
||||
const auto dynamicAddress = mappedAddress(request.DynamicSegmentOffset, request.DynamicSegmentSize);
|
||||
const auto stubAddress = mappedAddress(request.StubOffset, request.StubSize);
|
||||
|
||||
const auto shStrTabOff = static_cast<std::uint64_t>(buf.size());
|
||||
constexpr std::uint64_t shStrTabSize = sizeof(kShStrTabBlob);
|
||||
for (const char i : kShStrTabBlob)
|
||||
|
|
@ -65,10 +81,10 @@ void SectionHeaderTableBuilder::WriteTable(
|
|||
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 0, 0, SHT_NULL, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 1, kShStrTabNameOffset, SHT_STRTAB, 0, shStrTabOff, shStrTabOff, shStrTabSize, 0, 0, kNoSpecialAlignment, 0);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 2, kDynStrNameOffset, SHT_STRTAB, SHF_ALLOC, mappedAddress(request.DynStrOffset), request.DynStrOffset, request.DynStrSize, 0, 0, kNoSpecialAlignment, 0);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 3, kDynSymNameOffset, SHT_DYNSYM, SHF_ALLOC, mappedAddress(request.DynSymOffset), request.DynSymOffset, request.DynSymSize, kDynSymLinkToStrTab, kDynSymInfoFirstGlobal, kDynSymAlign, kSymEntrySize);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 4, kDynamicNameOffset, SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE, mappedAddress(request.DynamicSegmentOffset), request.DynamicSegmentOffset, request.DynamicSegmentSize, kDynamicLinkToStrTab, 0, kDynSymAlign, kDynEntrySize);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 5, kTextNameOffset, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, mappedAddress(request.StubOffset), request.StubOffset, request.StubSize, 0, 0, kNoSpecialAlignment, 0);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 2, kDynStrNameOffset, SHT_STRTAB, SHF_ALLOC, dynStrAddress, request.DynStrOffset, request.DynStrSize, 0, 0, kNoSpecialAlignment, 0);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 3, kDynSymNameOffset, SHT_DYNSYM, SHF_ALLOC, dynSymAddress, request.DynSymOffset, request.DynSymSize, kDynSymLinkToStrTab, kDynSymInfoFirstGlobal, kDynSymAlign, kSymEntrySize);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 4, kDynamicNameOffset, SHT_DYNAMIC, SHF_ALLOC | SHF_WRITE, dynamicAddress, request.DynamicSegmentOffset, request.DynamicSegmentSize, kDynamicLinkToStrTab, 0, kDynSymAlign, kDynEntrySize);
|
||||
_writeSectionHeader(buf, shOff + kShdrEntrySize * 5, kTextNameOffset, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, stubAddress, request.StubOffset, request.StubSize, 0, 0, kNoSpecialAlignment, 0);
|
||||
|
||||
_byteWriter->WriteU64(buf, kEhdrShOffOffset, shOff);
|
||||
_byteWriter->WriteU16(buf, kEhdrShEntSizeOffset, static_cast<std::uint16_t>(kShdrEntrySize));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
#include <elfpatcher/general/SectionHeaderTableBuilder.hpp>
|
||||
#include <io/ByteWriter.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
static void Require(bool ok) { if (!ok) throw std::runtime_error("Section range validation failed"); }
|
||||
|
||||
static std::vector<std::uint8_t> Fixture() {
|
||||
std::vector<std::uint8_t> bytes(512, 0);
|
||||
Io::WriteU64(bytes, 32, 64); Io::WriteU16(bytes, 54, 56); Io::WriteU16(bytes, 56, 1);
|
||||
Io::WriteU32(bytes, 64, 1); Io::WriteU64(bytes, 72, 128);
|
||||
Io::WriteU64(bytes, 80, 0x1000); Io::WriteU64(bytes, 96, 256);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
Elfpatcher::SectionHeaderTableBuilder builder(std::make_shared<Io::ByteWriter>());
|
||||
const Elfpatcher::SectionHeaderTableRequest valid{128, 8, 136, 24, 160, 16, 176, 16};
|
||||
auto bytes = Fixture(); builder.WriteTable(bytes, valid);
|
||||
const auto table = Io::ReadU64(bytes, 40);
|
||||
Require(Io::ReadU64(bytes, table + 2 * 64 + 16) == 0x1000);
|
||||
auto empty = valid; empty.StubOffset = 384; empty.StubSize = 0;
|
||||
bytes = Fixture(); builder.WriteTable(bytes, empty);
|
||||
Require(Io::ReadU64(bytes, Io::ReadU64(bytes, 40) + 5 * 64 + 16) == 0x1100);
|
||||
for (int kind = 0; kind < 8; ++kind) {
|
||||
bytes = Fixture(); auto request = valid;
|
||||
switch (kind) {
|
||||
case 0: request.DynStrSize = 257; break;
|
||||
case 1: request.DynSymSize = std::numeric_limits<std::uint64_t>::max(); break;
|
||||
case 2: request.DynamicSegmentOffset = 383; request.DynamicSegmentSize = 2; break;
|
||||
case 3: request.StubOffset = 384; request.StubSize = 1; break;
|
||||
case 4: Io::WriteU16(bytes, 54, 1); break;
|
||||
case 5: Io::WriteU64(bytes, 32, std::numeric_limits<std::uint64_t>::max()); break;
|
||||
case 6: Io::WriteU64(bytes, 80, std::numeric_limits<std::uint64_t>::max() - 4); break;
|
||||
case 7: Io::WriteU16(bytes, 56, 20); break;
|
||||
}
|
||||
const auto before = bytes;
|
||||
bool rejected = false;
|
||||
try { builder.WriteTable(bytes, request); } catch (const std::exception&) { rejected = true; }
|
||||
Require(rejected && bytes == before);
|
||||
}
|
||||
std::cout << "10 section range cases passed\n";
|
||||
} catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 1; }
|
||||
}
|
||||
Loading…
Reference in New Issue