From cfa0e57f8fd4e79ba36e990af3c1326b11a7c4e8 Mon Sep 17 00:00:00 2001 From: Mark Benvenuto Date: Wed, 24 Jul 2024 00:10:11 -0400 Subject: [PATCH] Add checks for min/max values themselves that lose precision --- src/mc-range-encoding.c | 8 +++++--- test/test-mc-range-encoding.c | 37 +++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/mc-range-encoding.c b/src/mc-range-encoding.c index b4d87bc59..1c757ecac 100644 --- a/src/mc-range-encoding.c +++ b/src/mc-range-encoding.c @@ -162,7 +162,7 @@ bool mc_getTypeInfo64(mc_getTypeInfo64_args_t args, mc_OSTType_Int64 *out, mongo } #define exp10Double(x) pow(10, x) -#define SCALED_DOUBLE_BOUNDS 9223372036854775807.0 // 2^63 - 1 +#define SCALED_DOUBLE_BOUNDS 9007199254740992.0 // 2^53 #define UINT_64_MAX 18446744073709551615ull uint64_t subtract_int64_t(int64_t max, int64_t min) { @@ -229,13 +229,15 @@ bool mc_canUsePrecisionModeDouble(double min, } if (fabs(scaled_max) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid upper bounds for double precision. abs(max) must be less than 9223372036854775807. max: %g", + CLIENT_ERR("Invalid upper bounds for double precision. abs(max * 10^precision) must be less than " + "9007199254740992. max: %g", max); return false; } if (fabs(scaled_min) >= SCALED_DOUBLE_BOUNDS) { - CLIENT_ERR("Invalid lower bounds for double precision. abs(min) must be less than 9223372036854775807. min: %g", + CLIENT_ERR("Invalid lower bounds for double precision. abs(min * 10^precision) must be less than " + "9007199254740992. min: %g", min); return false; } diff --git a/test/test-mc-range-encoding.c b/test/test-mc-range-encoding.c index 7ac35e56c..268878a31 100644 --- a/test/test-mc-range-encoding.c +++ b/test/test-mc-range-encoding.c @@ -223,12 +223,15 @@ static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE(0.0, 16.0, 0, true, 5); // 2^53 + 1 is where double starts to lose precision, so we need to ensure that we get the // correct value for max_bits out. - CAN_USE_PRECISION_MODE(1.0, 9007199254740992.0, 0, true, 53); - CAN_USE_PRECISION_MODE(0.0, 9007199254740992.0, 0, true, 54); + CAN_USE_PRECISION_MODE_ERRORS(1.0, 9007199254740992.0, 0, "Invalid upper bounds for double precision. abs"); + CAN_USE_PRECISION_MODE_ERRORS(0.0, 9007199254740992.0, 0, "Invalid upper bounds for double precision. abs"); CAN_USE_PRECISION_MODE(2.718281, 314.159265, 6, true, 29); - CAN_USE_PRECISION_MODE(-1000000000.0, 9223372036844775424.0, 0, false, 64); + CAN_USE_PRECISION_MODE_ERRORS(-1000000000.0, + 9223372036844775424.0, + 0, + "Invalid upper bounds for double precision. abs"); CAN_USE_PRECISION_MODE_ERRORS(2.710000, 314.150000, 2, "Invalid upper bounds for double precision. Digits after"); CAN_USE_PRECISION_MODE_ERRORS(314.150000, 350.0, 2, "Invalid lower bounds for double precision. Digits after"); @@ -236,12 +239,9 @@ static void _test_canUsePrecisionModeDouble(_mongocrypt_tester_t *tester) { CAN_USE_PRECISION_MODE_ERRORS((double)9007199254740992, INT_64_MAX_DOUBLE, 0, - "Invalid upper bounds for double precision. abs(max) must"); - CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE, - -1 * (double)9007199254740992, - 0, - "Invalid lower bounds for double precision. abs(min) must"); - CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0, 92233720368547.0, 5, "Invalid value for precision."); + "Invalid upper bounds for double precision. abs(max"); + CAN_USE_PRECISION_MODE_ERRORS(-1 * INT_64_MAX_DOUBLE, 1.0, 0, "Invalid lower bounds for double precision. abs(min"); + CAN_USE_PRECISION_MODE_ERRORS(-92233720368547.0, 92233720368547.0, 5, "Invalid upper bounds for double"); #undef CAN_USE_PRECISION_MODE #undef CAN_USE_PRECISION_MODE_ERRORS @@ -414,7 +414,7 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .max = OPT_DOUBLE_C(5), .min = OPT_DOUBLE_C(0), .precision = OPT_U32_C(16), - .expectError = "The domain of double values specified by the min"}, + .expectError = "Invalid upper bounds for double precision"}, {.value = -5, .max = OPT_DOUBLE_C(-1), .min = OPT_DOUBLE_C(-10), @@ -465,14 +465,14 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 53 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision"}, + .expectError = "Invalid upper bounds for double precision. Digits"}, {.value = 1E-30, .max = OPT_DOUBLE_C(10E-30), .min = OPT_DOUBLE_C(1E-30), .precision = OPT_U32_C(35), // Applying min/max/precision result in a domain needing >= 53 bits to represent. // For range v2, expect an error. - .expectError = "Invalid upper bounds for double precision"}, + .expectError = "Invalid upper bounds for double precision. Digits"}, /* Test max and min integer bounds for doubles */ {.value = DOUBLE_MIN_SAFE_INT, .max = OPT_DOUBLE_C(DOUBLE_MAX_SAFE_INT), @@ -481,13 +481,24 @@ static void _test_RangeTest_Encode_Double(_mongocrypt_tester_t *tester) { .expect = 0, // Applying min/max/precision result in a domain needing >= 53 bits to represent. // For range v2, expect an error. - .expectError = "The domain of double values specified by the min"}, + .expectError = "Invalid upper bounds for double precision. abs"}, {.value = 900719925474099.6, .max = OPT_DOUBLE_C(900719925474100.0), .min = OPT_DOUBLE_C(900719925474099.0), .precision = OPT_U32_C(0), .expect = 0, .expectMax = OPT_U64_C(1)}, + // Domain size is small but min/max * 10^precision loses precision. + {.value = 900719925474099.6, + .max = OPT_DOUBLE_C(900719925474100.0), + .min = OPT_DOUBLE_C(900719925474099.0), + .precision = OPT_U32_C(1), + .expectError = "Invalid upper bounds for double precision. abs"}, + {.value = -900719925474099.6, + .max = OPT_DOUBLE_C(-900719925474099.0), + .min = OPT_DOUBLE_C(-900719925474100.0), + .precision = OPT_U32_C(1), + .expectError = "Invalid lower bounds for double precision. abs"}, // 2^52 // The expected values increase by 4503599627370496 * 2^(i-52) + j // i.e. the gaps between integers as the exponent increases since doubles lose precision as