unittests: Adds a test to check for partial decode

Known failure so added to the known failure list.
This commit is contained in:
Ryan Houdek committed 2025-10-16 13:17:16 -07:00
1 parent eb41cb2261
commit 79d90f3c7f
4 files changed
+111 -38

No files matched your search

+3
View File
@@ -23,3 +23,6 @@ sigtest_sigmask.64
# Disabled since FEX's FaultSafeMemcpy is intentionally stub-implemented
syscalls_efault.32
syscalls_efault.64
# partial instruction decode is known to fail
noexec_protect.64
@@ -31,6 +31,7 @@ foreach(TEST ${TESTS})
add_executable(${TEST_NAME}.${BITNESS} ${TEST})
target_link_libraries(${TEST_NAME}.${BITNESS} PRIVATE Catch2::Catch2WithMain)
target_include_directories(${TEST_NAME}.${BITNESS} PRIVATE include/)
endforeach()
target_link_libraries(pthread_cancel.${BITNESS} PRIVATE pthread)
@@ -0,0 +1,54 @@
#pragma once
#include <cstdint>
class SimpleX86Emit final {
public:
enum Reg {
RAX = 0,
RCX = 1,
RDX = 2,
RBX = 3,
RSP = 4,
RBP = 5,
RSI = 6,
RDI = 7,
// r8 and higher not implemented.
};
SimpleX86Emit(void* Ptr, std::size_t size)
: Ptr {static_cast<uint8_t*>(Ptr)}
, EndPtr {static_cast<uint8_t*>(Ptr) + size} {}
void ret() {
db<uint8_t>(0xc3);
}
void mov(Reg reg, uint32_t val) {
db<uint8_t>(0xB8 + reg);
db(val);
}
void dd(uint32_t val) {
db(val);
}
bool HadError() const {
return _HadError;
}
private:
uint8_t* Ptr;
uint8_t* EndPtr;
bool _HadError {};
template<typename T>
void db(T v) {
static_assert(sizeof(uint32_t) == 4);
std::size_t i {};
for (i = 0; i < sizeof(T) && Ptr != EndPtr; ++i) {
*Ptr = v >> (i * 8);
++Ptr;
}
_HadError = i != sizeof(T);
}
};
@@ -1,3 +1,4 @@
#include "simple_x86.h"
#include <catch2/catch_test_macros.hpp>
#include <signal.h>
@@ -7,6 +8,7 @@
#include <csetjmp>
bool Caught = false;
uint64_t CaughtAddr {};
static jmp_buf LongJump {};
static void SIGSEGV_Handler(int signal, siginfo_t* siginfo, void* context) {
@@ -16,47 +18,13 @@ static void SIGSEGV_Handler(int signal, siginfo_t* siginfo, void* context) {
// Page fault
REQUIRE(_context->uc_mcontext.gregs[REG_TRAPNO] == 14);
CaughtAddr = reinterpret_cast<uint64_t>(siginfo->si_addr);
Caught = true;
longjmp(LongJump, 1);
}
class SimpleX86Emit final {
public:
enum Reg {
RAX = 0,
RCX = 1,
RDX = 2,
RBX = 3,
RSP = 4,
RBP = 5,
RSI = 6,
RDI = 7,
// r8 and higher not implemented.
};
SimpleX86Emit(void* Ptr)
: Ptr {static_cast<uint8_t*>(Ptr)} {}
void ret() {
db<uint8_t>(0xc3);
}
void mov(Reg reg, uint32_t val) {
db<uint8_t>(0xB8 + reg);
db(val);
}
private:
uint8_t* Ptr;
template<typename T>
void db(T v) {
static_assert(sizeof(uint32_t) == 4);
for (size_t i = 0; i < sizeof(T); ++i) {
*Ptr = v >> (i * 8);
++Ptr;
}
}
};
TEST_CASE("Signals: Test No-Exec") {
struct sigaction act {};
act.sa_sigaction = SIGSEGV_Handler;
@@ -69,7 +37,7 @@ TEST_CASE("Signals: Test No-Exec") {
void* Ptr = mmap(nullptr, PageSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
REQUIRE(Ptr != MAP_FAILED);
SimpleX86Emit emit(Ptr);
SimpleX86Emit emit(Ptr, PageSize);
emit.mov(SimpleX86Emit::Reg::RAX, 1);
emit.ret();
@@ -99,3 +67,50 @@ TEST_CASE("Signals: Test No-Exec") {
REQUIRE(Caught == true);
}
}
TEST_CASE("Signals: Partial decode") {
struct sigaction act {};
act.sa_sigaction = SIGSEGV_Handler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
auto PageSize = sysconf(_SC_PAGESIZE);
PageSize = PageSize > 0 ? PageSize : 0x1000;
void* Ptr = mmap(nullptr, PageSize * 2, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
REQUIRE(Ptr != MAP_FAILED);
SimpleX86Emit emit(static_cast<uint8_t*>(Ptr) + PageSize - 1, PageSize + 1);
// MOV <uint32_t> hits the end of the page, writing only the opcode.
emit.mov(SimpleX86Emit::Reg::RAX, 0x42424242);
emit.ret();
// Protect second page
REQUIRE(mprotect(static_cast<uint8_t*>(Ptr) + PageSize, PageSize, PROT_NONE) == 0);
using func_ptr = uint32_t (*)();
func_ptr func = reinterpret_cast<func_ptr>(static_cast<uint8_t*>(Ptr) + PageSize - 1);
Caught = false;
if (setjmp(LongJump) == 0) {
REQUIRE(func() != 0x42424242);
} else {
REQUIRE(Caught == true);
CHECK(CaughtAddr == (reinterpret_cast<uint64_t>(Ptr) + PageSize));
}
Caught = false;
CaughtAddr = 0;
// Protect second page
REQUIRE(mprotect(static_cast<uint8_t*>(Ptr) + PageSize, PageSize, PROT_READ | PROT_WRITE | PROT_EXEC) == 0);
if (setjmp(LongJump) == 0) {
CHECK(func() == 0x42424242);
} else {
REQUIRE(false);
}
CHECK(Caught == false);
CHECK(CaughtAddr == 0);
}