fuzz_with_unicorn: Avoid self-modifying code

* Don't immediately terminate when unicorn raises an interrupt
* Detect self-modifying code
This commit is contained in:
MerryMage 2018-07-15 11:58:53 +01:00
parent 9f8c6f60f5
commit 9c4f234417
3 changed files with 22 additions and 4 deletions

View file

@ -119,7 +119,7 @@ static u32 GenFloatInst(u64 pc, bool is_last_inst) {
// Approximation. Produces incorrect results. // Approximation. Produces incorrect results.
"FMADD_float", "FMSUB_float", "FNMADD_float", "FNMSUB_float", "FMADD_float", "FMSUB_float", "FNMADD_float", "FNMSUB_float",
// Requires investigation (temporarily disabled). // Requires investigation (temporarily disabled).
"FDIV_2", "FDIV_1", "FDIV_2",
}; };
std::vector<InstructionGenerator> result; std::vector<InstructionGenerator> result;
@ -150,8 +150,8 @@ static u32 GenFloatInst(u64 pc, bool is_last_inst) {
static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::VectorArray& vecs, const size_t instructions_offset, static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::VectorArray& vecs, const size_t instructions_offset,
const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) { const std::vector<u32>& instructions, const u32 pstate, const u32 fpcr) {
static TestEnv jit_env; static TestEnv jit_env{};
static TestEnv uni_env; static TestEnv uni_env{};
std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + instructions_offset); std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + instructions_offset);
std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + instructions_offset); std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + instructions_offset);
@ -159,6 +159,8 @@ static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::V
uni_env.code_mem[instructions.size() + instructions_offset] = 0x14000000; // B . uni_env.code_mem[instructions.size() + instructions_offset] = 0x14000000; // B .
jit_env.modified_memory.clear(); jit_env.modified_memory.clear();
uni_env.modified_memory.clear(); uni_env.modified_memory.clear();
jit_env.interrupts.clear();
uni_env.interrupts.clear();
Dynarmic::A64::UserConfig jit_user_config{&jit_env}; Dynarmic::A64::UserConfig jit_user_config{&jit_env};
// The below corresponds to the settings for qemu's aarch64_max_initfn // The below corresponds to the settings for qemu's aarch64_max_initfn
@ -238,14 +240,25 @@ static void RunTestInstance(const Unicorn::RegisterArray& regs, const Unicorn::V
fmt::print("x86_64:\n"); fmt::print("x86_64:\n");
fmt::print("{}\n", jit.Disassemble()); fmt::print("{}\n", jit.Disassemble());
fmt::print("Interrupts:\n");
for (auto& i : uni_env.interrupts) {
puts(i.c_str());
}
}; };
REQUIRE(uni_env.code_mem_modified_by_guest == jit_env.code_mem_modified_by_guest);
if (uni_env.code_mem_modified_by_guest) {
return;
}
REQUIRE(uni.GetPC() == jit.GetPC()); REQUIRE(uni.GetPC() == jit.GetPC());
REQUIRE(uni.GetRegisters() == jit.GetRegisters()); REQUIRE(uni.GetRegisters() == jit.GetRegisters());
REQUIRE(uni.GetVectors() == jit.GetVectors()); REQUIRE(uni.GetVectors() == jit.GetVectors());
REQUIRE(uni.GetSP() == jit.GetSP()); REQUIRE(uni.GetSP() == jit.GetSP());
REQUIRE((uni.GetPstate() & 0xF0000000) == (jit.GetPstate() & 0xF0000000)); REQUIRE((uni.GetPstate() & 0xF0000000) == (jit.GetPstate() & 0xF0000000));
REQUIRE(uni_env.modified_memory == jit_env.modified_memory); REQUIRE(uni_env.modified_memory == jit_env.modified_memory);
REQUIRE(uni_env.interrupts.empty());
} }
TEST_CASE("A64: Single random instruction", "[a64]") { TEST_CASE("A64: Single random instruction", "[a64]") {

View file

@ -22,6 +22,7 @@ public:
bool code_mem_modified_by_guest = false; bool code_mem_modified_by_guest = false;
std::array<u32, 1024> code_mem{}; std::array<u32, 1024> code_mem{};
std::map<u64, u8> modified_memory; std::map<u64, u8> modified_memory;
std::vector<std::string> interrupts;
std::uint32_t MemoryReadCode(u64 vaddr) override { std::uint32_t MemoryReadCode(u64 vaddr) override {
if (vaddr < code_mem.size() * sizeof(u32)) { if (vaddr < code_mem.size() * sizeof(u32)) {

View file

@ -38,6 +38,9 @@ void Unicorn::Run() {
while (testenv.ticks_left > 0) { while (testenv.ticks_left > 0) {
CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1)); CHECKED(uc_emu_start(uc, GetPC(), END_ADDRESS, 0, 1));
testenv.ticks_left--; testenv.ticks_left--;
if (!testenv.interrupts.empty() || testenv.code_mem_modified_by_guest) {
return;
}
} }
} }
@ -168,7 +171,8 @@ void Unicorn::InterruptHook(uc_engine* uc, u32 int_number, void* user_data) {
this_->testenv.CallSVC(iss); this_->testenv.CallSVC(iss);
break; break;
default: default:
ASSERT_MSG(false, "Unhandled interrupt: int_number: {:#x}, esr: {:#x} (ec: {:#x}, iss: {:#x})", int_number, esr, ec, iss); this_->testenv.interrupts.emplace_back(fmt::format("Unhandled interrupt: int_number: {:#x}, esr: {:#x} (ec: {:#x}, iss: {:#x})", int_number, esr, ec, iss));
break;
} }
} }