diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 4561113eb..c47fc2c5a 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -144,6 +144,22 @@ if(BUILD_TESTING) target_include_directories(autorun_exit_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cli/include ${CMAKE_CURRENT_SOURCE_DIR}/io/include ${CMAKE_CURRENT_SOURCE_DIR}/domain/include) add_test(NAME autorun_exit_status COMMAND autorun_exit_tests) set_tests_properties(autorun_exit_status PROPERTIES TIMEOUT 20) + add_test_executable(rewrite_boundaries_tests + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/tests/RewriteBoundaryTests.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionRewriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/InstructionScanner.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionDecoder.cpp + ) + target_include_directories(rewrite_boundaries_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 rewrite_boundaries COMMAND rewrite_boundaries_tests) + set_tests_properties(rewrite_boundaries PROPERTIES TIMEOUT 20) + add_test_executable(cli_args_tests ${CMAKE_CURRENT_SOURCE_DIR}/cli/tests/CliArgsTests.cpp ${CMAKE_CURRENT_SOURCE_DIR}/cli/src/CliArgs.cpp diff --git a/core/relinker/codegen/src/x86/X64InstructionRewriter.cpp b/core/relinker/codegen/src/x86/X64InstructionRewriter.cpp index a3899c3dd..212bdaee2 100644 --- a/core/relinker/codegen/src/x86/X64InstructionRewriter.cpp +++ b/core/relinker/codegen/src/x86/X64InstructionRewriter.cpp @@ -74,6 +74,18 @@ RewriteResult X64InstructionRewriter::Rewrite( throw CodegenException("Rewrite offset is out of bounds", request.Offset); } + const auto scanner = MakeInstructionScanner(); + const auto matches = scanner->ScanCodeSection(codeSection, 0, codeSection.size()); + bool instructionStart = false; + for (const auto& match : matches) { + if (match.Offset == request.Offset) { + instructionStart = true; + break; + } + } + if (!instructionStart) + throw CodegenException("Rewrite offset is not an instruction boundary", request.Offset); + const X64InstructionDecoder decoder; const std::size_t decodedLength = decoder.Decode(codeSection.data() + offset, codeSection.size() - offset); @@ -90,9 +102,6 @@ RewriteResult X64InstructionRewriter::Rewrite( rewritten.insert(rewritten.end(), request.NewBytes.begin(), request.NewBytes.end()); rewritten.insert(rewritten.end(), codeSection.begin() + offset + decodedLength, codeSection.end()); - const auto scanner = MakeInstructionScanner(); - const auto matches = scanner->ScanCodeSection(codeSection, 0, codeSection.size()); - std::vector adjustments; for (const auto& match : matches) { diff --git a/core/relinker/codegen/tests/RewriteBoundaryTests.cpp b/core/relinker/codegen/tests/RewriteBoundaryTests.cpp new file mode 100644 index 000000000..fa9ee0055 --- /dev/null +++ b/core/relinker/codegen/tests/RewriteBoundaryTests.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +static void Require(bool ok) { if (!ok) throw std::runtime_error("Invalid rewrite boundary accepted"); } + +int main() { + try { + const Codegen::X64InstructionRewriter rewriter; + for (const auto& code : {std::vector{0xB8, 0x90, 0x90, 0x90, 0x90, 0xC3}, + std::vector{0x48, 0x8B, 0x05, 0x90, 0x90, 0x90, 0x90, 0xC3}, + std::vector{0xE8, 0x90, 0x90, 0x90, 0x90, 0xC3}}) { + const auto original = code; + for (std::size_t offset = 1; offset + 1 < code.size(); ++offset) { + bool rejected = false; + try { (void)rewriter.Rewrite(code, {offset, {0x90}}); } + catch (const std::exception&) { rejected = true; } + Require(rejected && code == original); + } + const auto result = rewriter.Rewrite(code, {0, {0x90}}); + Require(result.Bytes == std::vector{0x90, 0xC3}); + } + const auto result = rewriter.Rewrite({0x90, 0x90, 0xC3}, {1, {0x90, 0x90}}); + Require(result.Bytes == std::vector{0x90, 0x90, 0x90, 0xC3}); + std::cout << "18 rewrite boundary cases passed\n"; + } catch (const std::exception& error) { std::cerr << error.what() << '\n'; return 1; } +}