aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/A32
diff options
context:
space:
mode:
authorMerryMage <[email protected]>2021-02-06 21:25:08 +0000
committerMerryMage <[email protected]>2021-02-06 21:41:01 +0000
commit331a02e02e50994063b37b37b2a9c8533d6fc6bb (patch)
tree217ed38bb1ee766fd9ef130a76e4764003cabf24 /tests/A32
parent8b612edb757d5447423e10509b9a6ad789df0fb7 (diff)
downloaddynarmic-331a02e02e50994063b37b37b2a9c8533d6fc6bb.tar.gz
dynarmic-331a02e02e50994063b37b37b2a9c8533d6fc6bb.zip
fuzz_arm: Add fuzzing for thumb instructions
Diffstat (limited to 'tests/A32')
-rw-r--r--tests/A32/fuzz_arm.cpp182
-rw-r--r--tests/A32/testenv.h14
2 files changed, 163 insertions, 33 deletions
diff --git a/tests/A32/fuzz_arm.cpp b/tests/A32/fuzz_arm.cpp
index b377995a..2cd9b47e 100644
--- a/tests/A32/fuzz_arm.cpp
+++ b/tests/A32/fuzz_arm.cpp
@@ -36,8 +36,8 @@
namespace {
using namespace Dynarmic;
-bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
- const A32::LocationDescriptor location{pc, {}, {}};
+bool ShouldTestInst(u32 instruction, u32 pc, bool is_thumb, bool is_last_inst) {
+ const A32::LocationDescriptor location = A32::LocationDescriptor{pc, {}, {}}.SetTFlag(is_thumb);
IR::Block block{location};
const bool should_continue = A32::TranslateSingleInstruction(block, location, instruction);
@@ -74,7 +74,7 @@ bool ShouldTestInst(u32 instruction, u32 pc, bool is_last_inst) {
return true;
}
-u32 GenRandomInst(u32 pc, bool is_last_inst) {
+u32 GenRandomArmInst(u32 pc, bool is_last_inst) {
static const struct InstructionGeneratorInfo {
std::vector<InstructionGenerator> generators;
std::vector<InstructionGenerator> invalid;
@@ -139,13 +139,58 @@ u32 GenRandomInst(u32 pc, bool is_last_inst) {
continue;
}
- if (ShouldTestInst(inst, pc, is_last_inst)) {
+ if (ShouldTestInst(inst, pc, false, is_last_inst)) {
return inst;
}
}
}
-Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
+std::vector<u16> GenRandomThumbInst(u32 pc, bool is_last_inst) {
+ static const struct InstructionGeneratorInfo {
+ std::vector<InstructionGenerator> generators;
+ std::vector<InstructionGenerator> invalid;
+ } instructions = []{
+ const std::vector<std::tuple<std::string, const char*>> list {
+#define INST(fn, name, bitstring) {#fn, bitstring},
+#include "frontend/A32/decoder/thumb16.inc"
+#include "frontend/A32/decoder/thumb32.inc"
+#undef INST
+ };
+
+ std::vector<InstructionGenerator> generators;
+ std::vector<InstructionGenerator> invalid;
+
+ // List of instructions not to test
+ static constexpr std::array do_not_test {
+ "thumb16_SETEND",
+ "thumb16_BKPT",
+ };
+
+ for (const auto& [fn, bitstring] : list) {
+ if (std::find(do_not_test.begin(), do_not_test.end(), fn) != do_not_test.end()) {
+ invalid.emplace_back(InstructionGenerator{bitstring});
+ continue;
+ }
+ generators.emplace_back(InstructionGenerator{bitstring});
+ }
+ return InstructionGeneratorInfo{generators, invalid};
+ }();
+
+ while (true) {
+ const size_t index = RandInt<size_t>(0, instructions.generators.size() - 1);
+ const u32 inst = instructions.generators[index].Generate();
+ const bool is_four_bytes = (inst >> 16) != 0;
+
+ if (ShouldTestInst(is_four_bytes ? Common::SwapHalves32(inst) : inst, pc, true, is_last_inst)) {
+ if (is_four_bytes)
+ return { static_cast<u16>(inst >> 16), static_cast<u16>(inst) };
+ return { static_cast<u16>(inst) };
+ }
+ }
+}
+
+template <typename TestEnv>
+Dynarmic::A32::UserConfig GetUserConfig(TestEnv& testenv) {
Dynarmic::A32::UserConfig user_config;
user_config.optimizations &= ~OptimizationFlag::FastDispatch;
user_config.callbacks = &testenv;
@@ -153,22 +198,28 @@ Dynarmic::A32::UserConfig GetUserConfig(ArmTestEnv& testenv) {
return user_config;
}
-static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni,
- ArmTestEnv& jit_env, ArmTestEnv& uni_env,
- const A32Unicorn<ArmTestEnv>::RegisterArray& regs,
- const A32Unicorn<ArmTestEnv>::ExtRegArray& vecs,
- const std::vector<u32>& instructions, const u32 cpsr, const u32 fpscr) {
+template <typename TestEnv>
+static void RunTestInstance(Dynarmic::A32::Jit& jit,
+ A32Unicorn<TestEnv>& uni,
+ TestEnv& jit_env,
+ TestEnv& uni_env,
+ const typename A32Unicorn<TestEnv>::RegisterArray& regs,
+ const typename A32Unicorn<TestEnv>::ExtRegArray& vecs,
+ const std::vector<typename TestEnv::InstructionType>& instructions,
+ const u32 cpsr,
+ const u32 fpscr,
+ const size_t ticks_left) {
const u32 initial_pc = regs[15];
- const u32 num_words = initial_pc / sizeof(u32);
+ const u32 num_words = initial_pc / sizeof(typename TestEnv::InstructionType);
const u32 code_mem_size = num_words + static_cast<u32>(instructions.size());
- jit_env.code_mem.resize(code_mem_size + 1);
- uni_env.code_mem.resize(code_mem_size + 1);
+ jit_env.code_mem.resize(code_mem_size);
+ uni_env.code_mem.resize(code_mem_size);
std::copy(instructions.begin(), instructions.end(), jit_env.code_mem.begin() + num_words);
std::copy(instructions.begin(), instructions.end(), uni_env.code_mem.begin() + num_words);
- jit_env.code_mem.back() = 0xEAFFFFFE; // B .
- uni_env.code_mem.back() = 0xEAFFFFFE; // B .
+ jit_env.PadCodeMem();
+ uni_env.PadCodeMem();
jit_env.modified_memory.clear();
uni_env.modified_memory.clear();
jit_env.interrupts.clear();
@@ -186,10 +237,23 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni
uni.SetCpsr(cpsr);
uni.ClearPageCache();
- jit_env.ticks_left = instructions.size();
+ jit_env.ticks_left = ticks_left;
jit.Run();
- uni_env.ticks_left = instructions.size();
+ uni_env.ticks_left = [&]{
+ if constexpr (std::is_same_v<TestEnv, ThumbTestEnv>) {
+ // Unicorn counts thumb instructions weirdly:
+ // A 32-bit thumb instruction counts as two.
+ // Except for branch instructions which count as one???
+ if (instructions.size() <= 1)
+ return ticks_left;
+ if ((instructions[instructions.size() - 2] & 0xF800) <= 0xE800)
+ return instructions.size();
+ return instructions.size() - 1;
+ } else {
+ return ticks_left;
+ }
+ }();
uni.Run();
SCOPE_FAIL {
@@ -279,7 +343,7 @@ static void RunTestInstance(Dynarmic::A32::Jit& jit, A32Unicorn<ArmTestEnv>& uni
}
} // Anonymous namespace
-TEST_CASE("A32: Single random instruction", "[arm]") {
+TEST_CASE("A32: Single random arm instruction", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@@ -294,7 +358,7 @@ TEST_CASE("A32: Single random instruction", "[arm]") {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
- instructions[0] = GenRandomInst(0, true);
+ instructions[0] = GenRandomArmInst(0, true);
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
@@ -303,11 +367,11 @@ TEST_CASE("A32: Single random instruction", "[arm]") {
INFO("Instruction: 0x" << std::hex << instructions[0]);
regs[15] = start_address;
- RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
+ RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1);
}
}
-TEST_CASE("A32: Small random block", "[arm]") {
+TEST_CASE("A32: Small random arm block", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@@ -322,11 +386,11 @@ TEST_CASE("A32: Small random block", "[arm]") {
std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
- instructions[0] = GenRandomInst(0, false);
- instructions[1] = GenRandomInst(4, false);
- instructions[2] = GenRandomInst(8, false);
- instructions[3] = GenRandomInst(12, false);
- instructions[4] = GenRandomInst(16, true);
+ instructions[0] = GenRandomArmInst(0, false);
+ instructions[1] = GenRandomArmInst(4, false);
+ instructions[2] = GenRandomArmInst(8, false);
+ instructions[3] = GenRandomArmInst(12, false);
+ instructions[4] = GenRandomArmInst(16, true);
const u32 start_address = 100;
const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x10;
@@ -339,11 +403,11 @@ TEST_CASE("A32: Small random block", "[arm]") {
INFO("Instruction 5: 0x" << std::hex << instructions[4]);
regs[15] = start_address;
- RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
+ RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5);
}
}
-TEST_CASE("A32: Large random block", "[arm]") {
+TEST_CASE("A32: Large random arm block", "[arm]") {
ArmTestEnv jit_env{};
ArmTestEnv uni_env{};
@@ -361,7 +425,7 @@ TEST_CASE("A32: Large random block", "[arm]") {
std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
for (size_t j = 0; j < instruction_count; ++j) {
- instructions[j] = GenRandomInst(j * 4, j == instruction_count - 1);
+ instructions[j] = GenRandomArmInst(j * 4, j == instruction_count - 1);
}
const u64 start_address = 100;
@@ -369,6 +433,64 @@ TEST_CASE("A32: Large random block", "[arm]") {
const u32 fpcr = RandomFpcr();
regs[15] = start_address;
- RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr);
+ RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 100);
+ }
+}
+
+TEST_CASE("A32: Single random thumb instruction", "[thumb]") {
+ ThumbTestEnv jit_env{};
+ ThumbTestEnv uni_env{};
+
+ Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
+ A32Unicorn<ThumbTestEnv> uni{uni_env};
+
+ A32Unicorn<ThumbTestEnv>::RegisterArray regs;
+ A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
+ std::vector<u16> instructions;
+
+ for (size_t iteration = 0; iteration < 100000; ++iteration) {
+ std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
+ std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
+
+ instructions = GenRandomThumbInst(0, true);
+
+ const u32 start_address = 100;
+ const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
+ const u32 fpcr = RandomFpcr();
+
+ INFO("Instruction: 0x" << std::hex << instructions[0]);
+
+ regs[15] = start_address;
+ RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 1);
+ }
+}
+
+TEST_CASE("A32: Small random thumb block", "[thumb]") {
+ ThumbTestEnv jit_env{};
+ ThumbTestEnv uni_env{};
+
+ Dynarmic::A32::Jit jit{GetUserConfig(jit_env)};
+ A32Unicorn<ThumbTestEnv> uni{uni_env};
+
+ A32Unicorn<ThumbTestEnv>::RegisterArray regs;
+ A32Unicorn<ThumbTestEnv>::ExtRegArray ext_reg;
+ std::vector<u16> instructions;
+
+ for (size_t iteration = 0; iteration < 100000; ++iteration) {
+ std::generate(regs.begin(), regs.end(), [] { return RandInt<u32>(0, ~u32(0)); });
+ std::generate(ext_reg.begin(), ext_reg.end(), [] { return RandInt<u32>(0, ~u32(0)); });
+
+ instructions.clear();
+ for (size_t i = 0; i < 5; i++) {
+ const std::vector<u16> inst = GenRandomThumbInst(instructions.size() * 2, i == 4);
+ instructions.insert(instructions.end(), inst.begin(), inst.end());
+ }
+
+ const u32 start_address = 100;
+ const u32 cpsr = (RandInt<u32>(0, 0xF) << 28) | 0x1F0;
+ const u32 fpcr = RandomFpcr();
+
+ regs[15] = start_address;
+ RunTestInstance(jit, uni, jit_env, uni_env, regs, ext_reg, instructions, cpsr, fpcr, 5);
}
}
diff --git a/tests/A32/testenv.h b/tests/A32/testenv.h
index 93f207a4..75e15841 100644
--- a/tests/A32/testenv.h
+++ b/tests/A32/testenv.h
@@ -16,19 +16,27 @@
#include "common/assert.h"
#include "common/common_types.h"
-template <typename InstructionType_, u32 infinite_loop>
+template <typename InstructionType_, u32 infinite_loop_u32>
class A32TestEnv final : public Dynarmic::A32::UserCallbacks {
public:
using InstructionType = InstructionType_;
using RegisterArray = std::array<u32, 16>;
using ExtRegsArray = std::array<u32, 64>;
+ static constexpr InstructionType infinite_loop = static_cast<InstructionType>(infinite_loop_u32);
+
u64 ticks_left = 0;
bool code_mem_modified_by_guest = false;
std::vector<InstructionType> code_mem;
std::map<u32, u8> modified_memory;
std::vector<std::string> interrupts;
+ void PadCodeMem() {
+ do {
+ code_mem.push_back(infinite_loop);
+ } while (code_mem.size() % 2 != 0);
+ }
+
std::uint32_t MemoryReadCode(u32 vaddr) override {
const size_t index = vaddr / sizeof(InstructionType);
if (index < code_mem.size()) {
@@ -36,7 +44,7 @@ public:
std::memcpy(&value, &code_mem[index], sizeof(u32));
return value;
}
- return infinite_loop; // B .
+ return infinite_loop_u32; // B .
}
std::uint8_t MemoryRead8(u32 vaddr) override {
@@ -81,7 +89,7 @@ public:
void CallSVC(std::uint32_t swi) override { ASSERT_MSG(false, "CallSVC({})", swi); }
- void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x})", pc); }
+ void ExceptionRaised(u32 pc, Dynarmic::A32::Exception /*exception*/) override { ASSERT_MSG(false, "ExceptionRaised({:08x}) code = {:08x}", pc, MemoryReadCode(pc)); }
void AddTicks(std::uint64_t ticks) override {
if (ticks > ticks_left) {