fix: tighten jsontx_exact integer boundary at 2^53

jsontx_exact: change the double boundary check from exclusive to inclusive
(d > max -> d >= max, d < -max -> d <= -max). Doubles cannot uniquely
represent odd integers at or above 2^53, so values like 2^53+1 silently
round to 2^53, corrupting the canonical form. The safe ceiling for
round-trip-exact integers is 2^53 - 1.

tests: add coverage for the 2^53 boundary (positive and negative sides,
and the silent rounding case).
This commit is contained in:
RichardAHBot
2026-09-13 21:35:31 +10:00
committed by Richard Holland
parent 4fb88f5f80
commit e470d8a70b
2 changed files with 37 additions and 2 deletions

View File

@@ -247,8 +247,7 @@ jsontx_exact(Json::Value const& v, std::int64_t& out)
return false;
}
double const d = v.asDouble();
if (!std::isfinite(d) || d != std::trunc(d) || d < -jsontx_exact_max ||
d > jsontx_exact_max)
if (!std::isfinite(d) || d != std::trunc(d) || d <= -jsontx_exact_max || d >= jsontx_exact_max)
return false;
out = static_cast<std::int64_t>(d);
return true;

View File

@@ -635,6 +635,42 @@ public:
BEAST_EXPECT(r2.second.size() >= r1.second.size());
});
// ---- 2^53 boundary: values at and above 2^53 must be rejected ----
// Doubles cannot uniquely represent odd integers >= 2^53, so the
// safe boundary for exact integer round-trips is 2^53 - 1.
section("exact_at_2exp53_boundary",
[&] {
// 2^53 - 1 should be accepted (max safe value)
Json::Value ok;
ok = 9007199254740991;
std::int64_t out = 0;
BEAST_EXPECT(jsontx_exact(ok, out));
BEAST_EXPECT(out == 9007199254740991);
// 2^53 should be rejected (not uniquely representable)
Json::Value bad;
bad = 9007199254740992;
BEAST_EXPECT(!jsontx_exact(bad, out));
// 2^53 + 1 rounds down to 2^53 in double representation
// and should also be rejected
Json::Value bad2;
bad2 = 9007199254740993;
BEAST_EXPECT(!jsontx_exact(bad2, out));
// Negative boundary: -2^53 should be rejected
Json::Value neg_bad;
neg_bad = -9007199254740992;
BEAST_EXPECT(!jsontx_exact(neg_bad, out));
// -2^53 + 1 should be accepted
Json::Value neg_ok;
neg_ok = -9007199254740991;
BEAST_EXPECT(jsontx_exact(neg_ok, out));
BEAST_EXPECT(out == -9007199254740991);
});
}
};