// Defensive regression reproducer for concurrent VmBase::allocate AES probes. // Link against a ThreadSanitizer-instrumented, otherwise unchanged RandomX. // Light/interpreted VMs exercise the same hard-AES VmBase::allocate probe as // production full/JIT VMs, avoiding a 2 GiB dataset and uninstrumented JIT code. #include "randomx.h" #include #include #include #include #include #include int main(int argc, char** argv) { const bool serialize = argc == 2 && std::strcmp(argv[1], "--serialize") == 0; if (argc > 1 && !serialize) return 2; if (!(randomx_get_flags() & RANDOMX_FLAG_HARD_AES)) { std::puts("SKIP: hardware AES unavailable"); return 77; } randomx_cache* cache = randomx_alloc_cache(RANDOMX_FLAG_DEFAULT); if (!cache) return 2; const char key[] = "synthetic-review-fixture"; randomx_init_cache(cache, key, sizeof(key) - 1); std::atomic start{false}; std::atomic failed{false}; std::mutex creation_mutex; std::vector workers; for (int i = 0; i < 7; ++i) { workers.emplace_back([&] { while (!start.load(std::memory_order_acquire)) std::this_thread::yield(); for (int round = 0; round < 100; ++round) { const auto flags = static_cast(RANDOMX_FLAG_HARD_AES | RANDOMX_FLAG_V2); std::unique_lock guard(creation_mutex, std::defer_lock); if (serialize) guard.lock(); randomx_vm* vm = randomx_create_vm(flags, cache, nullptr); if (guard.owns_lock()) guard.unlock(); if (!vm) { failed.store(true); break; } randomx_destroy_vm(vm); } }); } start.store(true, std::memory_order_release); for (auto& worker : workers) worker.join(); randomx_release_cache(cache); return failed.load() ? 2 : 0; }