142 lines
5.6 KiB
C++
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;
|
|
}
|