mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
guest_cycle_scale: range-check TOML integers before narrowing
Reject negative and >32-bit addr/value/size/mask in guest_cycle_scale_gate and out-of-range guest_cycle_scale instead of wrapping or clamping; add regression cases. Regenerate the OpenBIOS emitter stamp. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
||||
69641913e358f4c3128a93dadf5824c7929afeaf8df6c5b9a6cc645c0f43d0f5
|
||||
01cfc201a7c71e2555e5828c67aeaa29f295bf4ebfb4e182a3c606e2a4812857
|
||||
|
||||
@@ -618,16 +618,16 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
|
||||
// guest clock a fixed cost per instruction, and scale N charges 1/N of
|
||||
// it, so N times as much game code fits between two VBlanks while
|
||||
// VBlank, timers, CD, SPU and DMA keep hardware time. 1 = faithful;
|
||||
// clamped to 1..64. guest_cycle_scale_gated: the scale applies only
|
||||
// values outside 1..64 are rejected. guest_cycle_scale_gated: the scale applies only
|
||||
// while the mod gate is open (psx_mod_set_guest_cycle_scale_gate); see
|
||||
// guest_cycle_scale_gate below for the declarative RAM gate.
|
||||
if (cfg.contains("timing")) {
|
||||
const toml::value& tm = toml::find(cfg, "timing");
|
||||
if (tm.contains("guest_cycle_scale")) {
|
||||
long long n = toml::find<long long>(tm, "guest_cycle_scale");
|
||||
if (n < 1) n = 1;
|
||||
if (n > 64) n = 64;
|
||||
rt.guest_cycle_scale = (int)n;
|
||||
const long long n = toml::find<long long>(tm, "guest_cycle_scale");
|
||||
if (n < 1 || n > 64)
|
||||
throw std::runtime_error("[timing] guest_cycle_scale must be 1..64");
|
||||
rt.guest_cycle_scale = static_cast<int>(n);
|
||||
}
|
||||
if (tm.contains("guest_cycle_scale_gated"))
|
||||
rt.guest_cycle_scale_gated = toml::find<bool>(tm, "guest_cycle_scale_gated");
|
||||
@@ -640,12 +640,22 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
|
||||
else preds.push_back(g);
|
||||
if (preds.size() > 8)
|
||||
throw std::runtime_error("[timing] guest_cycle_scale_gate: at most 8 predicates");
|
||||
// Range-check the signed TOML integer before narrowing, so an
|
||||
// oversized or negative value cannot wrap into a valid one.
|
||||
auto u32_field = [](const toml::value& pv, const char* key) -> uint32_t {
|
||||
const long long v = toml::find<long long>(pv, key);
|
||||
if (v < 0 || v > 0xFFFFFFFFLL)
|
||||
throw std::runtime_error(
|
||||
std::string("[timing] guest_cycle_scale_gate: ") + key +
|
||||
" must be 0..0xFFFFFFFF");
|
||||
return static_cast<uint32_t>(v);
|
||||
};
|
||||
for (const toml::value& pv : preds) {
|
||||
RuntimeConfig::GuestCycleScaleGatePred p;
|
||||
p.addr = (uint32_t)toml::find<long long>(pv, "addr");
|
||||
p.value = (uint32_t)toml::find<long long>(pv, "value");
|
||||
if (pv.contains("size")) p.size = (uint32_t)toml::find<long long>(pv, "size");
|
||||
if (pv.contains("mask")) p.mask = (uint32_t)toml::find<long long>(pv, "mask");
|
||||
p.addr = u32_field(pv, "addr");
|
||||
p.value = u32_field(pv, "value");
|
||||
if (pv.contains("size")) p.size = u32_field(pv, "size");
|
||||
if (pv.contains("mask")) p.mask = u32_field(pv, "mask");
|
||||
const uint32_t phys = p.addr & 0x1FFFFFFFu;
|
||||
if ((p.size != 1 && p.size != 2 && p.size != 4) ||
|
||||
(phys & (p.size - 1u)) != 0 || phys >= 0x00800000u)
|
||||
|
||||
@@ -499,6 +499,29 @@ static void test_timing_gate() {
|
||||
try { (void)PSXRecompV4::load_game_config(p); } catch (const std::exception&) { rejected = true; }
|
||||
check(rejected, "timing: non-RAM gate address rejected");
|
||||
fs::remove(p);
|
||||
|
||||
/* Oversized and negative values must be rejected, not wrapped to 32 bits. */
|
||||
const struct { const char* body; const char* what; } bad[] = {
|
||||
{ "guest_cycle_scale_gate = { addr = 0x100010000, value = 1 }", "oversized addr" },
|
||||
{ "guest_cycle_scale_gate = { addr = -4, value = 1 }", "negative addr" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = 0x100000001 }", "oversized value" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = -1 }", "negative value" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = 1, size = 0x100000004 }", "oversized size" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = 1, size = -4 }", "negative size" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = 1, mask = 0x1FFFFFFFF }", "oversized mask" },
|
||||
{ "guest_cycle_scale_gate = { addr = 0x80010000, value = 1, mask = -1 }", "negative mask" },
|
||||
{ "guest_cycle_scale = 0", "scale 0" },
|
||||
{ "guest_cycle_scale = 65", "scale 65" },
|
||||
{ "guest_cycle_scale = -8", "negative scale" },
|
||||
{ "guest_cycle_scale = 0x100000008", "oversized scale" },
|
||||
};
|
||||
for (const auto& b : bad) {
|
||||
p = write_game_toml("ves_timing_range.toml", std::string("[timing]\n") + b.body + "\n");
|
||||
rejected = false;
|
||||
try { (void)PSXRecompV4::load_game_config(p); } catch (const std::exception&) { rejected = true; }
|
||||
check(rejected, (std::string("timing: rejected ") + b.what).c_str());
|
||||
fs::remove(p);
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
Reference in New Issue
Block a user