From 2381e7fc644e54f53a75f6bc638f20d7adf0785d Mon Sep 17 00:00:00 2001 From: Zinebyte Date: Thu, 8 Oct 2026 20:34:40 -0400 Subject: [PATCH] fix(relinker): reject prefixed system-call instructions Use the decoded opcode offset when checking forbidden system calls. Add synthetic regressions for prefixed forms, immediate decoys, and the skip option. AI-assisted: Codex. The submitting author is responsible for every changed line. --- .../relinker/src/analysis/SyscallScanner.cpp | 10 ++++----- .../tests/test_executable_segments.py | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/core/relinker/relinker/src/analysis/SyscallScanner.cpp b/core/relinker/relinker/src/analysis/SyscallScanner.cpp index f57cede70..8476e026f 100644 --- a/core/relinker/relinker/src/analysis/SyscallScanner.cpp +++ b/core/relinker/relinker/src/analysis/SyscallScanner.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -26,17 +27,16 @@ void SyscallScanner::ScanCodeSectionForSyscalls( const std::vector& codeSection, const FileByteOffset codeSectionOffset, const FileByteOffset codeSectionSize) { - const std::size_t limit = std::min(codeSection.size(), static_cast(codeSectionSize)); - const auto scanner = Codegen::MakeInstructionScanner(); const auto matches = scanner->ScanCodeSection(codeSection, codeSectionOffset, codeSectionSize); for (const auto& [Offset, Length] : matches) { const std::size_t i = Offset - codeSectionOffset; + const auto opcodeOffset = Codegen::DecodedInstruction{codeSection.data() + i, Length}.OpcodeOffset(); - if (i + 1 < limit) { - const std::uint8_t b0 = codeSection[i]; - const std::uint8_t b1 = codeSection[i + 1]; + if (opcodeOffset + 1 < Length) { + const std::uint8_t b0 = codeSection[i + opcodeOffset]; + const std::uint8_t b1 = codeSection[i + opcodeOffset + 1]; const bool isSyscall = (b0 == SYSCALL_BYTE0 && b1 == SYSCALL_BYTE1); const bool isInt80 = (b0 == INT80_BYTE0 && b1 == INT80_BYTE1); diff --git a/core/relinker/relinker/tests/test_executable_segments.py b/core/relinker/relinker/tests/test_executable_segments.py index ac3eb83d4..1da5c85e4 100644 --- a/core/relinker/relinker/tests/test_executable_segments.py +++ b/core/relinker/relinker/tests/test_executable_segments.py @@ -57,6 +57,28 @@ def main(): result, output = run_case(relinker, work, "syscall-filter", ["unused-filter=1"], b"\x0f\x05\xc3") assert result.returncode == 2 and "Forbidden syscall instruction" in result.stderr and not output.exists(), result + forbidden = ( + ("rex-syscall", b"\x48\x0f\x05"), + ("operand-syscall", b"\x66\x0f\x05"), + ("address-sysenter", b"\x67\x0f\x34"), + ("segment-sysenter", b"\x2e\x0f\x34"), + ("rex-sysret", b"\x48\x0f\x07"), + ("mixed-sysret", b"\x66\x67\x48\x0f\x07"), + ("operand-int80", b"\x66\xcd\x80"), + ("segment-int80", b"\x64\xcd\x80"), + ) + for name, instruction in forbidden: + result, output = run_case(relinker, work, name, [], b"\x90" + instruction + b"\xc3") + assert result.returncode == 2 and not output.exists(), (name, result.stdout, result.stderr) + assert "Forbidden syscall instruction at code offset 0x1801" in result.stderr, (name, result.stderr) + + result, output = run_case(relinker, work, "immediate-decoys", [], + b"\x48\xb8\x0f\x05\xcd\x80\x0f\x34\x0f\x07\xc3") + assert result.returncode == 0 and output.exists(), (result.stdout, result.stderr) + + result, output = run_case(relinker, work, "skip-prefixed", ["--skip-syscall-check"], b"\x48\x0f\x05\xc3") + assert result.returncode == 0 and output.exists(), (result.stdout, result.stderr) + result, output = run_case(relinker, work, "skip", ["--skip-syscall-check"], b"\x0f\x05\xc3") assert result.returncode == 0 and output.exists(), (result.stdout, result.stderr) second_code = b"\x90" * 16 + b"\xb8\x2a\x00\x00\x00\xc3"