java-topology/defects/cxbx-reloaded-0001/test/test_cxbx_reloaded_0001.cpp

142 lines
5.6 KiB
C++

// UNDF: UNDF-2026-XXXXXXXXX
// Test: cxbx-reloaded-0001 — IsRdtscInstruction O(P) std::find -> O(1) unordered_set
//
// Defect: IsRdtscInstruction() is called from a Windows Vectored Exception Handler
// (VEH) on every STATUS_PRIVILEGED_INSTRUCTION exception. Cxbx-Reloaded
// replaces rdtsc opcodes with a privileged OUT instruction so it can
// intercept timing reads and substitute Xbox-accurate values. Every time
// emulated code executes a patched rdtsc site, the VEH fires and calls
// IsRdtscInstruction(), which does std::find over g_RdtscPatches
// (std::vector<addr_xt>). With P patched sites the lookup is O(P).
//
// Most Xbox titles use rdtsc heavily for timing. Games like Halo 1 and
// Unreal Championship have 30-50+ rdtsc sites that are executed at 60+ Hz
// each, yielding tens of thousands of O(P) scans per second.
//
// Fix: replace g_RdtscPatches with std::unordered_set<addr_xt> for O(1)
// average-case membership check. push_back -> insert (idempotent and
// no ordering dependency needed here).
//
// Compile: g++ -std=c++17 -O2 -o test_cxbx_reloaded_0001 test_cxbx_reloaded_0001.cpp && ./test_cxbx_reloaded_0001
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <unordered_set>
#include <vector>
// Simulate addr_xt as uintptr_t (32-bit Xbox address)
using addr_xt = uint32_t;
// ---------------------------------------------------------------------------
// BEFORE: defect path — std::vector + std::find O(P)
// ---------------------------------------------------------------------------
static bool is_rdtsc_vector(const std::vector<addr_xt>& patches, addr_xt addr) {
return std::find(patches.begin(), patches.end(), addr) != patches.end();
}
// ---------------------------------------------------------------------------
// AFTER: fixed path — std::unordered_set O(1)
// ---------------------------------------------------------------------------
static bool is_rdtsc_set(const std::unordered_set<addr_xt>& patches, addr_xt addr) {
return patches.count(addr) != 0;
}
// ---------------------------------------------------------------------------
// Correctness test
// ---------------------------------------------------------------------------
static void test_correctness() {
const int NUM_PATCHES = 50;
std::vector<addr_xt> vec_patches;
std::unordered_set<addr_xt> set_patches;
for (int i = 0; i < NUM_PATCHES; ++i) {
addr_xt addr = 0x10000 + static_cast<addr_xt>(i) * 4;
vec_patches.push_back(addr);
set_patches.insert(addr);
}
// Patched addresses must be found by both implementations
for (int i = 0; i < NUM_PATCHES; ++i) {
addr_xt addr = 0x10000 + static_cast<addr_xt>(i) * 4;
assert(is_rdtsc_vector(vec_patches, addr) == true);
assert(is_rdtsc_set(set_patches, addr) == true);
}
// Unpatched addresses must not be found
for (int i = NUM_PATCHES; i < NUM_PATCHES * 2; ++i) {
addr_xt addr = 0x10000 + static_cast<addr_xt>(i) * 4;
assert(is_rdtsc_vector(vec_patches, addr) == false);
assert(is_rdtsc_set(set_patches, addr) == false);
}
// insert is idempotent for unordered_set (no duplicate entries)
size_t before = set_patches.size();
set_patches.insert(0x10000); // already present
assert(set_patches.size() == before);
printf("Correctness: PASS\n");
}
// ---------------------------------------------------------------------------
// Benchmark: O(P) vs O(1) at P=50 patches, 10000 VEH calls
// ---------------------------------------------------------------------------
static void test_performance() {
const int NUM_PATCHES = 50;
const int NUM_CALLS = 10000; // VEH fires this many times per second per site
const int REPEAT = 100;
std::vector<addr_xt> vec_patches;
std::unordered_set<addr_xt> set_patches;
for (int i = 0; i < NUM_PATCHES; ++i) {
addr_xt addr = 0x10000 + static_cast<addr_xt>(i) * 4;
vec_patches.push_back(addr);
set_patches.insert(addr);
}
// Use an address not in our patches so the scan goes the full distance
addr_xt miss_addr = 0x10000 + NUM_PATCHES * 4 + 0x100;
// --- defect path (vector + std::find) ---
auto t0 = std::chrono::high_resolution_clock::now();
volatile bool sink = false;
for (int r = 0; r < REPEAT; ++r) {
for (int c = 0; c < NUM_CALLS; ++c) {
sink ^= is_rdtsc_vector(vec_patches, miss_addr);
}
}
auto t1 = std::chrono::high_resolution_clock::now();
double ms_vector = std::chrono::duration<double, std::milli>(t1 - t0).count();
// --- fixed path (unordered_set) ---
t0 = std::chrono::high_resolution_clock::now();
for (int r = 0; r < REPEAT; ++r) {
for (int c = 0; c < NUM_CALLS; ++c) {
sink ^= is_rdtsc_set(set_patches, miss_addr);
}
}
t1 = std::chrono::high_resolution_clock::now();
double ms_set = std::chrono::duration<double, std::milli>(t1 - t0).count();
double ratio = (ms_set > 0.0) ? (ms_vector / ms_set) : 9999.0;
printf("Performance (P=%d patches, %d VEH calls, repeat=%d):\n",
NUM_PATCHES, NUM_CALLS, REPEAT);
printf(" vector+find : %.3f ms\n", ms_vector);
printf(" unordered_set : %.3f ms\n", ms_set);
printf(" speedup : %.1fx\n", ratio);
assert(ratio >= 2.0 && "Expected at least 2x speedup from unordered_set at P=50");
printf("Performance: PASS\n");
(void)sink;
}
int main() {
test_correctness();
test_performance();
printf("ALL TESTS PASSED\n");
return 0;
}