diff --git a/include/rapidjson/schema.h b/include/rapidjson/schema.h index f049285f4..e32fda9c3 100644 --- a/include/rapidjson/schema.h +++ b/include/rapidjson/schema.h @@ -341,8 +341,14 @@ class Hasher { bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } bool Double(double d) { Number n; - if (d < 0) n.u.i = static_cast(d); - else n.u.u = static_cast(d); + if (d < 0) + // INT64_MIN (-2^63) is exactly representable as double; cast is UB outside that range + n.u.i = d >= static_cast((std::numeric_limits::min)()) + ? static_cast(d) : 0; + else + // 2^64 is exactly representable as double; any d in [0, 2^64) fits in uint64_t + n.u.u = d < 18446744073709551616.0 + ? static_cast(d) : 0; n.d = d; return WriteNumber(n); } diff --git a/test/unittest/schematest.cpp b/test/unittest/schematest.cpp index 9d95cd40b..d94e25d23 100644 --- a/test/unittest/schematest.cpp +++ b/test/unittest/schematest.cpp @@ -3573,6 +3573,38 @@ TEST(SchemaValidator, NullableFalse) { "}}"); } +TEST(SchemaValidator, UniqueItemsDoubleOutOfIntRangeUB) { + Document sd; + sd.Parse("{\"type\":\"array\",\"uniqueItems\":true}"); + ASSERT_FALSE(sd.HasParseError()); + SchemaDocument schema(sd); + + // Large positive doubles well outside uint64_t range (~1.8e19 max) + { + SchemaValidator v(schema); + Document doc; + doc.Parse("[1e33, 2e33]"); + ASSERT_FALSE(doc.HasParseError()); + EXPECT_TRUE(doc.Accept(v)); // unique — must not crash or misidentify + } + { + SchemaValidator v(schema); + Document doc; + doc.Parse("[1e33, 1e33]"); + ASSERT_FALSE(doc.HasParseError()); + EXPECT_FALSE(doc.Accept(v)); // duplicates + } + + // Large negative doubles outside int64_t range (~-9.2e18 min) + { + SchemaValidator v(schema); + Document doc; + doc.Parse("[-1e33, -2e33]"); + ASSERT_FALSE(doc.HasParseError()); + EXPECT_TRUE(doc.Accept(v)); // unique + } +} + #if defined(_MSC_VER) || defined(__clang__) RAPIDJSON_DIAG_POP #endif