diff options
author | Shreyas Kalyan <shreyas.kalyan@mongodb.com> | 2023-01-26 13:14:48 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-01-31 02:24:42 +0000 |
commit | cdbfa41dcabb3d52ebf093f13d2b1bacd2af1b8a (patch) | |
tree | cb289d1d1d795b8dd0cc6cd4e74e20964a264352 | |
parent | 8aca0afe03fd7f9c3041463a470c7758e3c2a73e (diff) | |
download | mongo-cdbfa41dcabb3d52ebf093f13d2b1bacd2af1b8a.tar.gz |
SERVER-71325 update libmongocrypt with changes from MONGOCRYPT-509
32 files changed, 2412 insertions, 266 deletions
diff --git a/src/third_party/SConscript b/src/third_party/SConscript index 923fb22c1b9..685917b4c2b 100644 --- a/src/third_party/SConscript +++ b/src/third_party/SConscript @@ -164,7 +164,7 @@ if not use_system_version_of_library('asio'): if not use_system_version_of_library('intel_decimal128'): thirdPartyEnvironmentModifications['intel_decimal128'] = { - 'CPPPATH': ['#/src/third_party/IntelRDFPMathLib20U1/LIBRARY'], + 'CPPPATH': ['#/src/third_party/IntelRDFPMathLib20U1/LIBRARY/src'], } if not use_system_version_of_library('icu'): diff --git a/src/third_party/libmongocrypt/SConscript b/src/third_party/libmongocrypt/SConscript index 84b707889e1..a3cb1327773 100644 --- a/src/third_party/libmongocrypt/SConscript +++ b/src/third_party/libmongocrypt/SConscript @@ -12,6 +12,8 @@ env['CXXFLAGS_WERROR'] = [] env.InjectThirdParty(libraries=['libbson']) +env.InjectThirdParty(libraries=['intel_decimal128']) + env.Append(CPPDEFINES=[ 'KMS_MSG_STATIC', 'MONGOCRYPT_STATIC_DEFINE', @@ -126,6 +128,7 @@ env.Library( 'dist/src/mc-rangeopts.c', 'dist/src/mc-reader.c', 'dist/src/mc-tokens.c', + 'dist/src/mc-writer.c', 'dist/src/mongocrypt-binary.c', 'dist/src/mongocrypt-buffer.c', 'dist/src/mongocrypt-cache-collinfo.c', @@ -153,6 +156,7 @@ env.Library( 'dist/src/mongocrypt.c', ] + additional_sources, LIBDEPS_PRIVATE=[ + '$BUILD_DIR/third_party/shim_intel_decimal128', '$BUILD_DIR/third_party/shim_libbson', ], LIBDEPS_TAGS=[ diff --git a/src/third_party/libmongocrypt/dist/src/csfle-markup.cpp b/src/third_party/libmongocrypt/dist/src/csfle-markup.cpp index 21437826942..10f67c9e87c 100644 --- a/src/third_party/libmongocrypt/dist/src/csfle-markup.cpp +++ b/src/third_party/libmongocrypt/dist/src/csfle-markup.cpp @@ -12,6 +12,7 @@ extern "C" { #include <iostream> #include <fstream> #include <string> +#include <cstdint> #include <mongo_crypt-v1.h> diff --git a/src/third_party/libmongocrypt/dist/src/mc-dec128.h b/src/third_party/libmongocrypt/dist/src/mc-dec128.h new file mode 100644 index 00000000000..c609b57b0f7 --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mc-dec128.h @@ -0,0 +1,684 @@ +#ifndef MC_DEC128_H_INCLUDED +#define MC_DEC128_H_INCLUDED + +#include <bson/bson.h> + +#include <mlib/macros.h> +#include <mlib/int128.h> +#include <mlib/endian.h> + +// Include the header that declares the DFP functions, which may be macros that +// expand to renamed symbols: +#include <bid_conf.h> + +#include <inttypes.h> +#include <string.h> +#include <stdlib.h> +#include <float.h> + +MLIB_C_LINKAGE_BEGIN + +/// Rounding controls for Decimal128 operations +typedef enum mc_dec128_rounding_mode { + MC_DEC128_ROUND_NEAREST_EVEN = 0, + MC_DEC128_ROUND_DOWNWARD = 1, + MC_DEC128_ROUND_UPWARD = 2, + MC_DEC128_ROUND_TOWARD_ZERO = 3, + MC_DEC128_ROUND_NEAREST_AWAY = 4, + MC_DEC128_ROUND_DEFAULT = MC_DEC128_ROUND_NEAREST_EVEN, +} mc_dec128_rounding_mode; + +typedef struct mc_dec128_flagset { + int bits; +} mc_dec128_flagset; + +// This alignment conditional is the same conditions used in Intel's DFP +// library, ensuring we match the ABI of the library without pulling the header +#if defined _MSC_VER +#if defined _M_IX86 && !defined __INTEL_COMPILER +#define _mcDec128Align(n) +#else +#define _mcDec128Align(n) __declspec(align (n)) +#endif +#else +#if !defined HPUX_OS +#define _mcDec128Align(n) __attribute__ ((aligned (n))) +#else +#define _mcDec128Align(n) +#endif +#endif + +typedef union _mcDec128Align (16) +{ + uint64_t _words[2]; +#if !defined(__INTELLISENSE__) && defined(__GNUC__) && defined(__amd64) && \ + !defined(__APPLE__) && !defined(__clang__) + // If supported by the compiler, emit a field that can be used to visualize + // the value in a debugger. + float value_ __attribute__ ((mode (TD))); +#endif +} +mc_dec128; + +#undef _mcDec128Align + +/// Expands to a dec128 constant value. +#ifdef __cplusplus +#define MC_DEC128_C(N) \ + mc_dec128 _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0)) +#else +#define MC_DEC128_C(N) \ + _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0)) +#endif + +#define MC_DEC128(N) MLIB_INIT (mc_dec128) MC_DEC128_C (N) + +#define _mcDec128Combination(Bits) ((uint64_t) (Bits) << (47)) +#define _mcDec128ZeroExpCombo _mcDec128Combination (1 << 7 | 1 << 13 | 1 << 14) +#define _mcDec128Const(N, Negate) \ + _mcDec128ConstFromParts ( \ + N, (_mcDec128ZeroExpCombo | ((uint64_t) (Negate) << 63))) +#define _mcDec128ConstFromParts(CoeffLow, HighWord) \ + { \ + { \ + MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (CoeffLow) \ + : (uint64_t) (HighWord), \ + MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (HighWord) \ + : (uint64_t) (CoeffLow), \ + }, \ + } + +static const mc_dec128 MC_DEC128_ZERO = MC_DEC128_C (0); +static const mc_dec128 MC_DEC128_ONE = MC_DEC128_C (1); +static const mc_dec128 MC_DEC128_MINUSONE = MC_DEC128_C (-1); + +/// The greatest-magnitude finite negative value representable in a Decimal128 +#define MC_DEC128_LARGEST_NEGATIVE \ + mc_dec128_from_string ("-9999999999999999999999999999999999E6111") +/// The least-magnitude non-zero negative value representable in a Decimal128 +#define MC_DEC128_SMALLEST_NEGATIVE mc_dec128_from_string ("-1E-6176") +/// The greatest-magnitude finite positive value representable in a Decimal128 +#define MC_DEC128_LARGEST_POSITIVE \ + mc_dec128_from_string ("9999999999999999999999999999999999E6111") +/// The least-magnitude non-zero positive value representable in a Decimal128 +#define MC_DEC128_SMALLEST_POSITIVE mc_dec128_from_string ("1E-6176") +/// The normalized zero of Decimal128 +#define MC_DEC128_NORMALIZED_ZERO MC_DEC128_C (0) +/// A zero of Decimal128 with the least exponent +#define MC_DEC128_NEGATIVE_EXPONENT_ZERO mc_dec128_from_string ("0E-6176") +#define _mcDec128InfCombo \ + _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12) +#define _mcDec128QuietNaNCombo \ + _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | 1 << 11) + +/// Positive infinity of Decimal128 +#define MC_DEC128_POSITIVE_INFINITY \ + _mcDec128ConstFromParts (0, _mcDec128InfCombo) +/// Negative infinity of Decimal128 +#define MC_DEC128_NEGATIVE_INFINITY \ + _mcDec128ConstFromParts (0, _mcDec128InfCombo | 1ull << 63) +/// Positve quiet NaN of Decimal128 +#define MC_DEC128_POSITIVE_NAN \ + _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo) +/// Negative quiet NaN of Decimal128 +#define MC_DEC128_NEGATIVE_NAN \ + _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo | 1ull << 63) + + +/** + * @brief Convert a double-precision binary floating point value into the + * nearest Decimal128 value + * + * @param d The number to conver + * @param rnd The rounding mode in case the value is not exactly representable + * @param flags Out param for exception/error flags (Optional) + */ +static inline mc_dec128 +mc_dec128_from_double_ex (double d, + mc_dec128_rounding_mode rnd, + mc_dec128_flagset *flags) +{ + extern mc_dec128 binary64_to_bid128 ( + double d, mc_dec128_rounding_mode, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + return binary64_to_bid128 (d, rnd, flags ? flags : &zero_flags); +} + +/** + * @brief Convert a double-precision binary floating point value into the + * nearest Decimal128 value + */ +static inline mc_dec128 +mc_dec128_from_double (double d) +{ + return mc_dec128_from_double_ex (d, MC_DEC128_ROUND_DEFAULT, NULL); +} + +/** + * @brief Convert a string representation of a number into the nearest + * Decimal128 value + * + * @param s The string to parse. MUST be null-terminated + * @param rnd The rounding mode to use if the result is not representable + * @param flags Out param for exception/error flags (Optional) + */ +static inline mc_dec128 +mc_dec128_from_string_ex (const char *s, + mc_dec128_rounding_mode rnd, + mc_dec128_flagset *flags) +{ + extern mc_dec128 bid128_from_string ( + const char *, mc_dec128_rounding_mode, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + return bid128_from_string (s, rnd, flags ? flags : &zero_flags); +} + +/** + * @brief Convert a string representation of a number into the nearest + * Decimal128 value + */ +static inline mc_dec128 +mc_dec128_from_string (const char *s) +{ + return mc_dec128_from_string_ex (s, MC_DEC128_ROUND_DEFAULT, NULL); +} + +/** + * @brief A type capable of holding a string rendering of a Decimal128 in + * engineering notation. + */ +typedef struct mc_dec128_string { + /// The character array of the rendered value. Null-terminated + char str[48]; +} mc_dec128_string; + +/** + * @brief Render a Decimal128 value as a string (in engineering notation) + * + * @param d The number to represent + * @param flags Output parameter for exception/error flags (optional) + */ +static inline mc_dec128_string +mc_dec128_to_string_ex (mc_dec128 d, mc_dec128_flagset *flags) +{ + extern void bid128_to_string (char *, mc_dec128 d, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + mc_dec128_string out = {{0}}; + bid128_to_string (out.str, d, flags ? flags : &zero_flags); + return out; +} + +/** + * @brief Render a Decimal128 value as a string (in engineering notation) + */ +static inline mc_dec128_string +mc_dec128_to_string (mc_dec128 d) +{ + return mc_dec128_to_string_ex (d, NULL); +} + +/// Compare two dec128 numbers +#define DECL_IDF_COMPARE_1(Oper) \ + static inline bool mc_dec128_##Oper##_ex ( \ + mc_dec128 left, mc_dec128 right, mc_dec128_flagset *flags) \ + { \ + extern int bid128_quiet_##Oper ( \ + mc_dec128 left, mc_dec128 right, mc_dec128_flagset *); \ + mc_dec128_flagset zero_flags = {0}; \ + return 0 != \ + bid128_quiet_##Oper (left, right, flags ? flags : &zero_flags); \ + } \ + \ + static inline bool mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \ + { \ + return mc_dec128_##Oper##_ex (left, right, NULL); \ + } + +#define DECL_IDF_COMPARE(Op) \ + DECL_IDF_COMPARE_1 (Op) \ + DECL_IDF_COMPARE_1 (not_##Op) + +DECL_IDF_COMPARE (equal) +DECL_IDF_COMPARE (greater) +DECL_IDF_COMPARE (greater_equal) +DECL_IDF_COMPARE (less) +DECL_IDF_COMPARE (less_equal) + +#undef DECL_IDF_COMPARE +#undef DECL_IDF_COMPARE_1 + +/// Test properties of Decimal128 numbers +#define DECL_PREDICATE(Name, BIDName) \ + static inline bool mc_dec128_##Name (mc_dec128 d) \ + { \ + extern int bid128_##BIDName (mc_dec128 d); \ + return 0 != bid128_##BIDName (d); \ + } + +DECL_PREDICATE (is_zero, isZero) +DECL_PREDICATE (is_negative, isSigned) +DECL_PREDICATE (is_inf, isInf) +DECL_PREDICATE (is_finite, isFinite) +DECL_PREDICATE (is_nan, isNaN) + +#undef DECL_PREDICATE + +/// Binary arithmetic operations on two Decimal128 numbers +#define DECL_IDF_BINOP_WRAPPER(Oper) \ + static inline mc_dec128 mc_dec128_##Oper##_ex ( \ + mc_dec128 left, \ + mc_dec128 right, \ + mc_dec128_rounding_mode mode, \ + mc_dec128_flagset *flags) \ + { \ + extern mc_dec128 bid128_##Oper (mc_dec128 left, \ + mc_dec128 right, \ + mc_dec128_rounding_mode rounding, \ + mc_dec128_flagset *flags); \ + mc_dec128_flagset zero_flags = {0}; \ + return bid128_##Oper (left, right, mode, flags ? flags : &zero_flags); \ + } \ + \ + static inline mc_dec128 mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \ + { \ + return mc_dec128_##Oper##_ex ( \ + left, right, MC_DEC128_ROUND_DEFAULT, NULL); \ + } + +DECL_IDF_BINOP_WRAPPER (add) +DECL_IDF_BINOP_WRAPPER (mul) +DECL_IDF_BINOP_WRAPPER (div) +DECL_IDF_BINOP_WRAPPER (sub) +DECL_IDF_BINOP_WRAPPER (pow) + +#undef DECL_IDF_BINOP_WRAPPER + +/// Unary arithmetic operations on two Decimal128 numbers +#define DECL_IDF_UNOP_WRAPPER(Oper) \ + static inline mc_dec128 mc_dec128_##Oper##_ex (mc_dec128 operand, \ + mc_dec128_flagset *flags) \ + { \ + extern mc_dec128 bid128_##Oper ( \ + mc_dec128 v, mc_dec128_rounding_mode, mc_dec128_flagset *); \ + mc_dec128_flagset zero_flags = {0}; \ + return bid128_##Oper ( \ + operand, MC_DEC128_ROUND_DEFAULT, flags ? flags : &zero_flags); \ + } \ + \ + static inline mc_dec128 mc_dec128_##Oper (mc_dec128 operand) \ + { \ + return mc_dec128_##Oper##_ex (operand, NULL); \ + } + +DECL_IDF_UNOP_WRAPPER (round_integral_zero) +DECL_IDF_UNOP_WRAPPER (round_integral_positive) +DECL_IDF_UNOP_WRAPPER (round_integral_negative) +DECL_IDF_UNOP_WRAPPER (round_integral_exact) +DECL_IDF_UNOP_WRAPPER (log2) +DECL_IDF_UNOP_WRAPPER (log10) +DECL_IDF_UNOP_WRAPPER (negate) +DECL_IDF_UNOP_WRAPPER (abs) +#undef DECL_IDF_UNOP_WRAPPER + +/** + * @brief Scale the given Decimal128 by a power of ten + * + * @param fac The value being scaled + * @param exp The exponent: 10^exp is the scale factor + * @param rounding Rounding behavior + * @param flags Control/result flags + * @return mc_dec128 The product + */ +static inline mc_dec128 +mc_dec128_scale_ex (mc_dec128 fac, + long int exp, + mc_dec128_rounding_mode rounding, + mc_dec128_flagset *flags) +{ + extern mc_dec128 bid128_scalbln ( + mc_dec128 fac, long int, mc_dec128_rounding_mode, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + return bid128_scalbln (fac, exp, rounding, flags ? flags : &zero_flags); +} + +/** + * @brief Scale the given Decimal128 by a power of ten + * + * @param fac The value being scaled + * @param exp The exponent: 10^exp is the scale factor + * @return mc_dec128 The product + */ +static inline mc_dec128 +mc_dec128_scale (mc_dec128 fac, long int exp) +{ + return mc_dec128_scale_ex (fac, exp, MC_DEC128_ROUND_DEFAULT, NULL); +} + +/// The result of a dec_128 modf operation +typedef struct mc_dec128_modf_result { + /// The whole part of the result + mc_dec128 whole; + /// The fractional part of the result + mc_dec128 frac; +} mc_dec128_modf_result; + +/** + * @brief Split a Decimal128 into its whole and fractional parts. + * + * The "whole" value is the integral value of the Decimal128 rounded towards + * zero. The "frac" part is the remainder after removing the whole. + * + * @param d The value to inspect + * @param flags Results status flags + */ +static inline mc_dec128_modf_result +mc_dec128_modf_ex (mc_dec128 d, mc_dec128_flagset *flags) +{ + extern mc_dec128 bid128_modf ( + mc_dec128 d, mc_dec128 * iptr, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + mc_dec128_modf_result res; + res.frac = bid128_modf (d, &res.whole, flags ? flags : &zero_flags); + return res; +} + +/** + * @brief Split a Decimal128 into its whole and fractional parts. + * + * The "whole" value is the integral value of the Decimal128 rounded towards + * zero. The "frac" part is the remainder after removing the whole. + * + * @param d The value to inspect + */ +static inline mc_dec128_modf_result +mc_dec128_modf (mc_dec128 d) +{ + return mc_dec128_modf_ex (d, NULL); +} + +/** + * @brief Compute the "fmod", the remainder after decimal division rounding + * towards zero. + * + * @param numer The dividend of the modulus + * @param denom The divisor of the modulus + * @param flags Control/status flags + */ +static inline mc_dec128 +mc_dec128_fmod_ex (mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *flags) +{ + extern mc_dec128 bid128_fmod ( + mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + return bid128_fmod (numer, denom, flags ? flags : &zero_flags); +} + +/** + * @brief Compute the "fmod", the remainder after decimal division rounding + * towards zero. + * + * @param numer The dividend of the modulus + * @param denom The divisor of the modulus + */ +static inline mc_dec128 +mc_dec128_fmod (mc_dec128 numer, mc_dec128 denom) +{ + return mc_dec128_fmod_ex (numer, denom, NULL); +} + +/** + * @brief Obtain the a 64-bit binary integer value for the given Decimal128 + * value, nearest rounding toward zero. + * + * @param d The value to inspect + * @param flags Control/status flags + */ +static inline int64_t +mc_dec128_to_int64_ex (mc_dec128 d, mc_dec128_flagset *flags) +{ + extern int64_t bid128_to_int64_int (mc_dec128 d, mc_dec128_flagset *); + mc_dec128_flagset zero_flags = {0}; + return bid128_to_int64_int (d, flags ? flags : &zero_flags); +} + +/** + * @brief Obtain the a 64-bit binary integer value for the given Decimal128 + * value, nearest rounding toward zero. + * + * @param d The value to inspect + */ +static inline int64_t +mc_dec128_to_int64 (mc_dec128 d) +{ + return mc_dec128_to_int64_ex (d, NULL); +} + +/// Constants for inspection/deconstruction of Decimal128 +enum { + /// Least non-canonical combination bits value + MC_DEC128_COMBO_NONCANONICAL = 3 << 15, + /// Least combination value indicating infinity + MC_DEC128_COMBO_INFINITY = 0x1e << 12, + /// The greatest Decimal128 biased exponent + MC_DEC128_MAX_BIASED_EXPONENT = 6143 + 6144, + /// The exponent bias of Decimal128 + MC_DEC128_EXPONENT_BIAS = 6143 + 33, // +33 to include the 34 decimal digits + /// Minimum exponent value (without bias) + MC_DEC_MIN_EXPONENT = -6143, + /// Maximum exponent value (without bias) + MC_DEC_MAX_EXPONENT = 6144, +}; + +/// Obtain the value of the combination bits of a decimal128 +static inline uint32_t +mc_dec128_combination (mc_dec128 d) +{ + // Grab the high 64 bits: + uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0]; + // Sign is the 64th bit: + int signpos = 64 - 1; + // Combo is the next 16 bits: + int fieldpos = signpos - 17; + int fieldmask = (1 << 17) - 1; + return (uint32_t) ((hi >> fieldpos) & (uint32_t) fieldmask); +} + +/** + * @brief Obtain the value of the high 49 bits of the Decimal128 coefficient + */ +static inline uint64_t +mc_dec128_coeff_high (mc_dec128 d) +{ + uint64_t hi_field_mask = (1ull << 49) - 1; + uint32_t combo = mc_dec128_combination (d); + if (combo < MC_DEC128_COMBO_NONCANONICAL) { + uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0]; + return hi & hi_field_mask; + } else { + return 0; + } +} + +/** + * @brief Obtain the value of the low 64 bits of the Decimal128 coefficient + */ +static inline uint64_t +mc_dec128_coeff_low (mc_dec128 d) +{ + uint32_t combo = mc_dec128_combination (d); + if (combo < MC_DEC128_COMBO_NONCANONICAL) { + uint64_t lo = d._words[MLIB_IS_LITTLE_ENDIAN ? 0 : 1]; + return lo; + } else { + return 0; + } +} + +/** + * @brief Obtain the full coefficient of the given Decimal128 number. Requires a + * 128-bit integer. + */ +static inline mlib_int128 +mc_dec128_coeff (mc_dec128 d) +{ + // Hi bits + uint64_t hi = mc_dec128_coeff_high (d); + // Lo bits + uint64_t lo = mc_dec128_coeff_low (d); + // Shift and add + mlib_int128 hi_128 = mlib_int128_lshift (MLIB_INT128_CAST (hi), 64); + return mlib_int128_add (hi_128, MLIB_INT128_CAST (lo)); +} + +/** + * @brief Obtain the biased value of the Decimal128 exponent. + * + * The value is "biased" in that its binary value not actually zero for 10^0. It + * is offset by half the exponent range (the "bias") so it can encode the full + * positive and negative exponent range. The bias value is defined as + * MC_DEC128_EXPONENT_BIAS. + */ +static inline uint32_t +mc_dec128_get_biased_exp (mc_dec128 d) +{ + uint32_t combo = mc_dec128_combination (d); + if (combo < MC_DEC128_COMBO_NONCANONICAL) { + return combo >> 3; + } + if (combo >= MC_DEC128_COMBO_INFINITY) { + return MC_DEC128_MAX_BIASED_EXPONENT + 1; + } else { + return (combo >> 1) & ((1 << 14) - 1); + } +} + +/// Create a decimal string from a dec128 number. The result must be freed. +static inline char * +mc_dec128_to_new_decimal_string (mc_dec128 d) +{ + if (mc_dec128_is_zero (d)) { + // Just return "0" + char *s = (char *) calloc (2, 1); + if (s) { + s[0] = '0'; + } + return s; + } + + if (mc_dec128_is_negative (d)) { + // Negate the result, return a string with a '-' prefix + d = mc_dec128_negate (d); + char *s = mc_dec128_to_new_decimal_string (d); + if (!s) { + return NULL; + } + char *s1 = (char *) calloc (strlen (s) + 2, 1); + if (s1) { + s1[0] = '-'; + strcpy (s1 + 1, s); + } + free (s); + return s1; + } + + if (mc_dec128_is_inf (d) || mc_dec128_is_nan (d)) { + const char *r = mc_dec128_is_inf (d) ? "Infinity" : "NaN"; + char *c = (char *) calloc (strlen (r) + 1, 1); + if (c) { + strcpy (c, r); + } + return c; + } + + const char DIGITS[] = "0123456789"; + const mc_dec128 TEN = MC_DEC128_C (10); + + // Format the whole and fractional part separately. + mc_dec128_modf_result modf = mc_dec128_modf (d); + + if (mc_dec128_is_zero (modf.frac)) { + // This is a non-zero integer + // Allocate enough digits: + mc_dec128 log10 = mc_dec128_modf (mc_dec128_log10 (d)).whole; + int64_t ndigits = mc_dec128_to_int64 (log10) + 1; + // +1 for null + char *strbuf = (char *) calloc ((size_t) (ndigits + 1), 1); + if (strbuf) { + // Write the string backwards: + char *optr = strbuf + ndigits - 1; + while (!mc_dec128_is_zero (modf.whole)) { + mc_dec128 rem = mc_dec128_fmod (modf.whole, TEN); + int64_t remi = mc_dec128_to_int64 (rem); + *optr-- = DIGITS[remi]; + // Divide ten + modf = mc_dec128_modf (mc_dec128_div (modf.whole, TEN)); + } + } + return strbuf; + } else if (mc_dec128_is_zero (modf.whole)) { + // This is only a fraction (less than one, but more than zero) + while (!mc_dec128_is_zero (mc_dec128_modf (d).frac)) { + d = mc_dec128_mul (d, TEN); + } + // 'd' is now a whole number + char *part = mc_dec128_to_new_decimal_string (d); + if (!part) { + return NULL; + } + char *buf = (char *) calloc (strlen (part) + 3, 1); + if (buf) { + buf[0] = '0'; + buf[1] = '.'; + strcpy (buf + 2, part); + } + free (part); + return buf; + } else { + // We have both a whole part and a fractional part + char *whole = mc_dec128_to_new_decimal_string (modf.whole); + if (!whole) { + return NULL; + } + char *frac = mc_dec128_to_new_decimal_string (modf.frac); + if (!frac) { + free (whole); + return NULL; + } + char *ret = (char *) calloc (strlen (whole) + strlen (frac) + 1, 1); + if (ret) { + char *out = ret; + strcpy (out, whole); + out += strlen (whole); + // "frac" contains a leading zero, which we don't want + strcpy (out, frac + 1); + } + free (whole); + free (frac); + return ret; + } +} + +static inline mc_dec128 +mc_dec128_from_bson_iter (bson_iter_t *it) +{ + bson_decimal128_t b; + if (!bson_iter_decimal128 (it, &b)) { + mc_dec128 nan = MC_DEC128_POSITIVE_NAN; + return nan; + } + mc_dec128 ret; + memcpy (&ret, &b, sizeof b); + return ret; +} + +static inline bson_decimal128_t +mc_dec128_to_bson_decimal128 (mc_dec128 v) +{ + bson_decimal128_t ret; + memcpy (&ret, &v, sizeof ret); + return ret; +} + +MLIB_C_LINKAGE_END + +#endif // MC_DEC128_H_INCLUDED diff --git a/src/third_party/libmongocrypt/dist/src/mc-dec128.test.cpp b/src/third_party/libmongocrypt/dist/src/mc-dec128.test.cpp new file mode 100644 index 00000000000..b5bd0bebcb0 --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mc-dec128.test.cpp @@ -0,0 +1,72 @@ +#include <mc-dec128.h> + +#include <stdlib.h> + +#include <mlib/check.hpp> +#define CHECK MLIB_CHECK + +inline std::ostream & +operator<< (std::ostream &out, mc_dec128 d) noexcept +{ + auto s = mc_dec128_to_new_decimal_string (d); + out << s; + free (s); + return out; +} + +#define OPER(Op, Fn) \ + inline auto operator Op (mc_dec128 a, mc_dec128 b) noexcept /**/ \ + ->decltype (mc_dec128_##Fn (a, b)) \ + { \ + return mc_dec128_##Fn (a, b); \ + } + +OPER (+, add) +OPER (-, sub) +OPER (*, mul) +OPER (/, div) +OPER (==, equal) +OPER (>, greater) +OPER (<, less) + +int +main () +{ + mc_dec128 a = MC_DEC128_ZERO; + CHECK (mc_dec128_is_zero (a)); + + mc_dec128 b = MC_DEC128_ZERO; + mc_dec128 c = a * b; + CHECK (c == MC_DEC128_ZERO); + CHECK (mc_dec128_is_zero (c)); + + b = MC_DEC128_C (1); + // 0 + 1 = 1 + c = a + b; + CHECK (c == MC_DEC128_C (1)); + + // 1 + 1 = 2 + c = b + b; + CHECK (c == MC_DEC128_C (2)); + + // 2 * 2 = 4 + c = c * c; + CHECK (c == MC_DEC128_C (4)); + + // (4 + 1) / 2 = 2.5 + c = (c + MC_DEC128_C (1)) / MC_DEC128_C (2); + CHECK (c == mc_dec128_from_string ("2.5")); + + mc_dec128_string s = mc_dec128_to_string (c); + CHECK (std::string (s.str) == "+25E-1"); + + char *str = mc_dec128_to_new_decimal_string (c); + CHECK (std::string (str) == "2.5"); + free (str); + + mc_dec128 infin = MC_DEC128_POSITIVE_INFINITY; + CHECK (mc_dec128_is_inf (infin)); + + mc_dec128 nan = MC_DEC128_POSITIVE_NAN; + CHECK (mc_dec128_is_nan (nan)); +} diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private.h b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private.h index e1309821068..b8c771b3c41 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private.h +++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev-private.h @@ -88,10 +88,10 @@ typedef struct _mc_FLE2IndexedEqualityEncryptedValue_t mc_FLE2IndexedEncryptedValue_t; struct _mc_FLE2IndexedEqualityEncryptedValueTokens { + uint64_t counter; _mongocrypt_buffer_t edc; _mongocrypt_buffer_t esc; _mongocrypt_buffer_t ecc; - uint64_t counter; }; typedef struct _mc_FLE2IndexedEqualityEncryptedValueTokens @@ -103,11 +103,34 @@ mc_FLE2IndexedEncryptedValue_new (void); mc_FLE2IndexedEqualityEncryptedValueTokens * mc_FLE2IndexedEqualityEncryptedValueTokens_new (void); +/** + * This function is used by the server codebase. + */ +bool +mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer ( + mc_FLE2IndexedEqualityEncryptedValueTokens *tokens, + _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status); + bool mc_FLE2IndexedEncryptedValue_parse (mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status); +/** + * This function is used by the server codebase. + */ +bool +mc_FLE2IndexedEncryptedValue_write ( + _mongocrypt_crypto_t *crypto, + const bson_type_t original_bson_type, + const _mongocrypt_buffer_t *S_KeyId, + const _mongocrypt_buffer_t *ClientEncryptedValue, + mc_ServerDataEncryptionLevel1Token_t *token, + mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, + _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status); + /* mc_FLE2IndexedEncryptedValue_get_original_bson_type returns * original_bson_type. Returns 0 and sets @status on error. * It is an error to call before mc_FLE2IndexedEncryptedValue_parse. */ @@ -134,7 +157,10 @@ mc_FLE2IndexedEncryptedValue_add_S_Key (_mongocrypt_crypto_t *crypto, /* mc_FLE2IndexedEncryptedValue_decrypt decrypts InnerEncrypted with the * ServerDataEncryptionLevel1Token on the server-side. Returns false and sets * @status on error. It is an error to call before - * mc_FLE2IndexedEncryptedValue_parse. */ + * mc_FLE2IndexedEncryptedValue_parse. + * + * This function is used by the server codebase. + */ bool mc_FLE2IndexedEncryptedValue_decrypt_equality ( _mongocrypt_crypto_t *crypto, @@ -168,6 +194,13 @@ const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValue_get_ClientValue ( const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); +/** + * This function is used by the server codebase. + */ +const _mongocrypt_buffer_t * +mc_FLE2IndexedEncryptedValue_get_ClientEncryptedValue ( + const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status); + void mc_FLE2IndexedEncryptedValue_destroy (mc_FLE2IndexedEncryptedValue_t *iev); diff --git a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c index 2f8767cf055..ef6e28689b0 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c +++ b/src/third_party/libmongocrypt/dist/src/mc-fle2-payload-iev.c @@ -14,18 +14,21 @@ * limitations under the License. */ +#include "mongocrypt-buffer-private.h" #include "mongocrypt-private.h" #include "mc-fle-blob-subtype-private.h" #include "mc-fle2-payload-iev-private.h" #include "mc-tokens-private.h" #include "mc-reader-private.h" +#include "mc-writer-private.h" #include <stdint.h> #define CHECK_AND_RETURN(x) \ if (!(x)) { \ return false; \ } + struct _mc_FLE2IndexedEqualityEncryptedValue_t { _mongocrypt_buffer_t S_KeyId; _mongocrypt_buffer_t InnerEncrypted; @@ -53,6 +56,240 @@ mc_FLE2IndexedEqualityEncryptedValueTokens_new (void) } bool +mc_FLE2IndexedEqualityEncryptedValueTokens_init_from_buffer ( + mc_FLE2IndexedEqualityEncryptedValueTokens *tokens, + _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (tokens); + BSON_ASSERT_PARAM (buf); + + mc_reader_t reader; + mc_reader_init_from_buffer (&reader, buf, __FUNCTION__); + + CHECK_AND_RETURN (mc_reader_read_u64 (&reader, &tokens->counter, status)); + + CHECK_AND_RETURN ( + mc_reader_read_prfblock_buffer (&reader, &tokens->edc, status)); + + CHECK_AND_RETURN ( + mc_reader_read_prfblock_buffer (&reader, &tokens->esc, status)); + + CHECK_AND_RETURN ( + mc_reader_read_prfblock_buffer (&reader, &tokens->ecc, status)); + + return true; +} + +static bool +mc_fle2IndexedEncryptedValue_encrypt ( + _mongocrypt_crypto_t *crypto, + const _mongocrypt_buffer_t *ClientEncryptedValue, + mc_ServerDataEncryptionLevel1Token_t *token, + mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, + _mongocrypt_buffer_t *out, + mongocrypt_status_t *status); + +bool +safe_uint32_t_sum (const uint32_t a, + const uint32_t b, + uint32_t *out, + mongocrypt_status_t *status) +{ + if (a > UINT32_MAX - b) { + CLIENT_ERR ("safe_uint32_t_sum overflow, %" PRIu32 ", %" PRIu32, a, b); + return false; + } + *out = a + b; + return true; +} + +bool +mc_FLE2IndexedEncryptedValue_write ( + _mongocrypt_crypto_t *crypto, + const bson_type_t original_bson_type, + const _mongocrypt_buffer_t *S_KeyId, + const _mongocrypt_buffer_t *ClientEncryptedValue, + mc_ServerDataEncryptionLevel1Token_t *token, + mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, + _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status) +{ +#define CHECK_AND_GOTO(x) \ + if (!(x)) { \ + goto cleanup; \ + } + + bool ok = false; + + BSON_ASSERT_PARAM (crypto); + BSON_ASSERT_PARAM (index_tokens); + BSON_ASSERT_PARAM (S_KeyId); + BSON_ASSERT_PARAM (ClientEncryptedValue); + BSON_ASSERT_PARAM (token); + BSON_ASSERT_PARAM (index_tokens); + BSON_ASSERT_PARAM (buf); + + if (ClientEncryptedValue->len == 0) { + CLIENT_ERR ( + "mc_FLE2IndexedEncryptedValue_write iev must have an encrypted value"); + return ok; + } + + if (S_KeyId->len == 0) { + CLIENT_ERR ( + "mc_FLE2IndexedEncryptedValue_write iev SKeyId must have value"); + return ok; + } + + _mongocrypt_buffer_t encryption_out; + _mongocrypt_buffer_init (&encryption_out); + + CHECK_AND_GOTO (mc_fle2IndexedEncryptedValue_encrypt (crypto, + ClientEncryptedValue, + token, + index_tokens, + &encryption_out, + status)); + uint32_t expected_plaintext_size = 0; + CHECK_AND_GOTO (safe_uint32_t_sum ( + ClientEncryptedValue->len, + (uint32_t) (sizeof (uint64_t) * 2 + sizeof (uint32_t) * 3), + &expected_plaintext_size, + status)); + + uint32_t expected_cipher_size = _mongocrypt_fle2_calculate_ciphertext_len ( + expected_plaintext_size, status); + + if (expected_cipher_size == 0) { + CHECK_AND_GOTO (false); + } + + uint32_t expected_buf_size = 0; + CHECK_AND_RETURN (safe_uint32_t_sum (expected_cipher_size, + (uint32_t) (1 + sizeof (S_KeyId)), + &expected_buf_size, + status)); + + if (buf->len < expected_buf_size) { + CLIENT_ERR ( + "mc_FLE2IndexedEncryptedValue_write buf is not large enough for iev"); + CHECK_AND_GOTO (false); + } + + mc_writer_t writer; + mc_writer_init_from_buffer (&writer, buf, __FUNCTION__); + + const uint8_t subtype = + (uint8_t) MC_SUBTYPE_FLE2IndexedEqualityEncryptedValue; + + if ((original_bson_type < 0) || (original_bson_type > 0xFF)) { + CLIENT_ERR ("Field 't' must be a valid BSON type, got: %d", + original_bson_type); + CHECK_AND_GOTO (false); + } + + const uint8_t bson_type = (uint8_t) original_bson_type; + + CHECK_AND_GOTO (mc_writer_write_u8 ( + &writer, subtype, status)); + CHECK_AND_GOTO (mc_writer_write_buffer ( + &writer, S_KeyId, S_KeyId->len, status)); + CHECK_AND_GOTO (mc_writer_write_u8 ( + &writer, bson_type, status)); + + CHECK_AND_GOTO (mc_writer_write_buffer ( + &writer, &encryption_out, encryption_out.len, status)); + + ok = true; + +cleanup: + _mongocrypt_buffer_cleanup (&encryption_out); + return ok; +#undef CHECK_AND_GOTO +} + +static bool +mc_fle2IndexedEncryptedValue_encrypt ( + _mongocrypt_crypto_t *crypto, + const _mongocrypt_buffer_t *ClientEncryptedValue, + mc_ServerDataEncryptionLevel1Token_t *token, + mc_FLE2IndexedEqualityEncryptedValueTokens *index_tokens, + _mongocrypt_buffer_t *out, + mongocrypt_status_t *status) +{ +#define CHECK_AND_GOTO(x) \ + if (!(x)) { \ + goto cleanup; \ + } + + bool ok = false; + _mongocrypt_buffer_t in; + _mongocrypt_buffer_t iv; + + _mongocrypt_buffer_init (&in); + _mongocrypt_buffer_init_size (&iv, MONGOCRYPT_IV_LEN); + + uint32_t expected_buf_size = 0; + CHECK_AND_GOTO ( + safe_uint32_t_sum (ClientEncryptedValue->len, + (uint32_t) (sizeof (uint64_t) * 2 + (32 * 3)), + &expected_buf_size, + status)); + + _mongocrypt_buffer_resize (&in, expected_buf_size); + + uint32_t ciphertext_len = + _mongocrypt_fle2_calculate_ciphertext_len (expected_buf_size, status); + + if (ciphertext_len == 0) { + return false; + } + + _mongocrypt_buffer_resize (out, ciphertext_len); + + mc_writer_t writer; + mc_writer_init_from_buffer (&writer, &in, __FUNCTION__); + + uint64_t length; + length = ClientEncryptedValue->len; + CHECK_AND_GOTO (mc_writer_write_u64 (&writer, length, status)); + + CHECK_AND_GOTO (mc_writer_write_buffer ( + &writer, ClientEncryptedValue, ClientEncryptedValue->len, status)); + + CHECK_AND_GOTO ( + mc_writer_write_u64 (&writer, index_tokens->counter, status)); + + CHECK_AND_GOTO ( + mc_writer_write_prfblock_buffer (&writer, &index_tokens->edc, status)); + + CHECK_AND_GOTO ( + mc_writer_write_prfblock_buffer (&writer, &index_tokens->esc, status)); + + CHECK_AND_GOTO ( + mc_writer_write_prfblock_buffer (&writer, &index_tokens->ecc, status)); + + const _mongocrypt_buffer_t *token_buf = + mc_ServerDataEncryptionLevel1Token_get (token); + + uint32_t bytes_written; + + CHECK_AND_GOTO (_mongocrypt_random (crypto, &iv, MONGOCRYPT_IV_LEN, status)); + + CHECK_AND_GOTO (_mongocrypt_fle2_do_encryption ( + crypto, &iv, token_buf, &in, out, &bytes_written, status)); + + ok = true; + +cleanup: + _mongocrypt_buffer_cleanup (&iv); + _mongocrypt_buffer_cleanup (&in); + return ok; +#undef CHECK_AND_GOTO +} + +bool mc_FLE2IndexedEncryptedValue_parse (mc_FLE2IndexedEncryptedValue_t *iev, const _mongocrypt_buffer_t *buf, mongocrypt_status_t *status) @@ -228,8 +465,8 @@ mc_FLE2IndexedEncryptedValue_decrypt ( uint64_t expected_length = mc_reader_get_consumed_length (&reader) + length - 16; if (length > iev->Inner.len || expected_length > iev->Inner.len) { - CLIENT_ERR ("mc_FLE2IndexedEncryptedValue_add_S_Key expected " - "byte length >= %" PRIu32 " got: %" PRIu32, + CLIENT_ERR ("mc_FLE2IndexedEncryptedValue_decrypt expected " + "byte length >= %" PRIu64 " got: %" PRIu32, expected_length, iev->Inner.len); return false; @@ -327,6 +564,15 @@ mc_FLE2IndexedEqualityEncryptedValue_add_K_Key ( } const _mongocrypt_buffer_t * +mc_FLE2IndexedEncryptedValue_get_ClientEncryptedValue ( + const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (iev); + + return &iev->ClientEncryptedValue; +} + +const _mongocrypt_buffer_t * mc_FLE2IndexedEncryptedValue_get_ClientValue ( const mc_FLE2IndexedEncryptedValue_t *iev, mongocrypt_status_t *status) { diff --git a/src/third_party/libmongocrypt/dist/src/mc-optional-private.h b/src/third_party/libmongocrypt/dist/src/mc-optional-private.h index 3ce6a0b26d5..506fd063755 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-optional-private.h +++ b/src/third_party/libmongocrypt/dist/src/mc-optional-private.h @@ -20,6 +20,9 @@ #include <stdbool.h> #include <stdint.h> +#include "./mc-dec128.h" +#include "./mlib/int128.h" + typedef struct { bool set; int32_t value; @@ -100,4 +103,21 @@ typedef struct { .set = true, .value = val \ } +typedef struct { + bool set; + mc_dec128 value; +} mc_optional_dec128_t; + +#define OPT_MC_DEC128(...) \ + (mc_optional_dec128_t) \ + { \ + .set = true, .value = __VA_ARGS__ \ + } + + +#define OPT_NULLOPT \ + { \ + .set = false \ + } + #endif /* MC_OPTIONAL_PRIVATE_H */ diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h index 033a9f5f8cf..865ff7a88ad 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h +++ b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation-private.h @@ -21,6 +21,8 @@ #include <stdint.h> #include "mc-optional-private.h" #include "mongocrypt-status-private.h" +#include "mc-dec128.h" +#include <mlib/int128.h> // mc_edges_t represents a list of edges. typedef struct _mc_edges_t mc_edges_t; @@ -75,24 +77,87 @@ typedef struct { mc_edges_t * mc_getEdgesDouble (mc_getEdgesDouble_args_t args, mongocrypt_status_t *status); +typedef struct { + mc_dec128 value; + size_t sparsity; + mc_optional_dec128_t min, max; + mc_optional_uint32_t precision; +} mc_getEdgesDecimal128_args_t; + +mc_edges_t * +mc_getEdgesDecimal128 (mc_getEdgesDecimal128_args_t args, + mongocrypt_status_t *status); + + // count_leading_zeros_u64 returns the number of leading 0 bits of `in`. -size_t -mc_count_leading_zeros_u64 (uint64_t in); +static inline size_t +mc_count_leading_zeros_u64 (uint64_t in) +{ +#ifdef __has_builtin +#if __has_builtin(__builtin_clzl) +// Pointer-cast to ensure we are speaking the right type +#ifdef __APPLE__ + unsigned long long *p = ∈ + return (size_t) (in ? __builtin_clzll (*p) : 64); +#else + unsigned long *p = ∈ + return (size_t) (in ? __builtin_clzl (*p) : 64); +#endif +#endif +#endif + uint64_t bit = UINT64_C (1) << 63; + size_t count = 0; + while ((bit & in) == 0 && bit > 0) { + bit >>= 1; + ++count; + } + return count; +} // count_leading_zeros_u32 returns the number of leading 0 bits of `in`. -size_t -mc_count_leading_zeros_u32 (uint32_t in); +static inline size_t +mc_count_leading_zeros_u32 (uint32_t in) +{ +#ifdef __has_builtin +#if __has_builtin(__builtin_clz) + // Pointer-cast to ensure we are speaking the right type + unsigned int *p = ∈ + return (size_t) (in ? __builtin_clz (*p) : 32); +#endif +#endif + uint32_t bit = UINT32_C (1) << 31; + int count = 0; + while ((bit & in) == 0 && bit > 0) { + bit >>= 1; + ++count; + } + return (size_t) count; +} + +static inline size_t +mc_count_leading_zeros_u128 (mlib_int128 in) +{ + size_t hi = mc_count_leading_zeros_u64 ( + mlib_int128_to_u64 (mlib_int128_rshift (in, 64))); + size_t lo = mc_count_leading_zeros_u64 (mlib_int128_to_u64 (in)); + return hi + ((hi == 64 ? 1u : 0u) * lo); +} + +typedef struct mc_bitstring { + char str[129]; +} mc_bitstring; // mc_convert_to_bitstring_u64 returns a 64 character string of 1's and 0's -// representing the bits of `in`. Caller must call `bson_free` on returned -// value. -char * +// representing the bits of `in` +mc_bitstring mc_convert_to_bitstring_u64 (uint64_t in); // mc_convert_to_bitstring_u32 returns a 32 character string of 1's and 0's -// representing the bits of `in`. Caller must call `bson_free` on returned -// value. -char * +// representing the bits of `in`. +mc_bitstring mc_convert_to_bitstring_u32 (uint32_t in); +mc_bitstring +mc_convert_to_bitstring_u128 (mlib_int128 i); + #endif /* MC_RANGE_EDGE_GENERATION_PRIVATE_H */ diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c index 5faffb552bf..caa7e65b2f5 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c +++ b/src/third_party/libmongocrypt/dist/src/mc-range-edge-generation.c @@ -89,66 +89,53 @@ mc_edges_destroy (mc_edges_t *edges) bson_free (edges); } -size_t -mc_count_leading_zeros_u64 (uint64_t in) -{ - uint64_t bit = UINT64_C (1) << 63; - size_t count = 0; - while ((bit & in) == 0 && bit > 0) { - bit >>= 1; - ++count; - } - return count; -} - -size_t -mc_count_leading_zeros_u32 (uint32_t in) -{ - uint32_t bit = UINT32_C (1) << 31; - size_t count = 0; - while ((bit & in) == 0 && bit > 0) { - bit >>= 1; - ++count; - } - return count; -} - -char * +mc_bitstring mc_convert_to_bitstring_u64 (uint64_t in) { - char *out = bson_malloc (64 + 1); + mc_bitstring ret = {{0}}; + char *out = ret.str; uint64_t bit = UINT64_C (1) << 63; - size_t count = 0; while (bit > 0) { if (bit & in) { - out[count] = '1'; + *out++ = '1'; } else { - out[count] = '0'; + *out++ = '0'; } bit >>= 1; - ++count; } - out[64] = '\0'; - return out; + return ret; } -char * +mc_bitstring mc_convert_to_bitstring_u32 (uint32_t in) { - char *out = bson_malloc (32 + 1); + mc_bitstring ret = {{0}}; + char *out = ret.str; uint32_t bit = UINT32_C (1) << 31; - size_t count = 0; while (bit > 0) { if (bit & in) { - out[count] = '1'; + *out++ = '1'; } else { - out[count] = '0'; + *out++ = '0'; } bit >>= 1; - ++count; } - out[32] = '\0'; - return out; + return ret; +} + +mc_bitstring +mc_convert_to_bitstring_u128 (mlib_int128 i) +{ + const uint64_t lo = mlib_int128_to_u64 (i); + const uint64_t hi = mlib_int128_to_u64 (mlib_int128_rshift (i, 64)); + mc_bitstring his = mc_convert_to_bitstring_u64 (hi); + mc_bitstring los = mc_convert_to_bitstring_u64 (lo); + mc_bitstring ret = {{0}}; + const size_t off = + mlib_strnmcopy (ret.str, sizeof ret.str, his.str, sizeof his.str); + mlib_strnmcopy ( + ret.str + off, (sizeof ret.str) - off, los.str, sizeof los.str); + return ret; } mc_edges_t * @@ -168,11 +155,10 @@ mc_getEdgesInt32 (mc_getEdgesInt32_args_t args, mongocrypt_status_t *status) // for consistency with the server implementation. BSON_ASSERT (got.min == 0); - char *valueBin = mc_convert_to_bitstring_u32 (got.value); + mc_bitstring valueBin = mc_convert_to_bitstring_u32 (got.value); size_t offset = mc_count_leading_zeros_u32 (got.max); - const char *leaf = valueBin + offset; + const char *leaf = valueBin.str + offset; mc_edges_t *ret = mc_edges_new (leaf, args.sparsity, status); - bson_free (valueBin); return ret; } @@ -193,11 +179,10 @@ mc_getEdgesInt64 (mc_getEdgesInt64_args_t args, mongocrypt_status_t *status) // for consistency with the server implementation. BSON_ASSERT (got.min == 0); - char *valueBin = mc_convert_to_bitstring_u64 (got.value); + mc_bitstring valueBin = mc_convert_to_bitstring_u64 (got.value); size_t offset = mc_count_leading_zeros_u64 (got.max); - const char *leaf = valueBin + offset; + const char *leaf = valueBin.str + offset; mc_edges_t *ret = mc_edges_new (leaf, args.sparsity, status); - bson_free (valueBin); return ret; } @@ -221,10 +206,35 @@ mc_getEdgesDouble (mc_getEdgesDouble_args_t args, mongocrypt_status_t *status) // for consistency with the server implementation. BSON_ASSERT (got.min == 0); - char *valueBin = mc_convert_to_bitstring_u64 (got.value); + mc_bitstring valueBin = mc_convert_to_bitstring_u64 (got.value); size_t offset = mc_count_leading_zeros_u64 (got.max); - const char *leaf = valueBin + offset; + const char *leaf = valueBin.str + offset; + mc_edges_t *ret = mc_edges_new (leaf, args.sparsity, status); + return ret; +} + +mc_edges_t * +mc_getEdgesDecimal128 (mc_getEdgesDecimal128_args_t args, + mongocrypt_status_t *status) +{ + mc_OSTType_Decimal128 got; + if (!mc_getTypeInfoDecimal128 ( + (mc_getTypeInfoDecimal128_args_t){ + .value = args.value, + .min = args.min, + .max = args.max, + .precision = args.precision, + }, + &got, + status)) { + return NULL; + } + + BSON_ASSERT (mlib_int128_eq (got.min, MLIB_INT128 (0))); + + mc_bitstring bits = mc_convert_to_bitstring_u128 (got.value); + size_t offset = mc_count_leading_zeros_u128 (got.max); + const char *leaf = bits.str + offset; mc_edges_t *ret = mc_edges_new (leaf, args.sparsity, status); - bson_free (valueBin); return ret; } diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h index b96f2dfd54a..96840f558e0 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h +++ b/src/third_party/libmongocrypt/dist/src/mc-range-encoding-private.h @@ -17,10 +17,14 @@ #ifndef MC_RANGE_ENCODING_PRIVATE_H #define MC_RANGE_ENCODING_PRIVATE_H -#include <stdint.h> #include "mc-optional-private.h" +#include "mc-dec128.h" #include "mongocrypt-status-private.h" +#include <mlib/int128.h> + +#include <stdint.h> + /* mc-range-encoding-private.h has functions to encode numeric types for * Queryable Encryption Range queries. It is a translation from server code: * https://github.com/mongodb/mongo/blob/1364f5c5004ac5503837ac5b315c189625f97269/src/mongo/crypto/fle_crypto.h#L1194-L1196 @@ -92,4 +96,31 @@ mc_getTypeInfoDouble (mc_getTypeInfoDouble_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; +/** + * @brief OST-encoding of a Decimal128 + */ +typedef struct { + mlib_int128 value, min, max; +} mc_OSTType_Decimal128; + +typedef struct { + mc_dec128 value; + mc_optional_dec128_t min, max; + mc_optional_uint32_t precision; +} mc_getTypeInfoDecimal128_args_t; + +/** + * @brief Obtain the OST encoding of a finite Decimal128 value. + * + * @param out Output for the result + * @param status Output for status on error + * @retval true On success + * @retval false Otherwise + */ +bool +mc_getTypeInfoDecimal128 (mc_getTypeInfoDecimal128_args_t args, + mc_OSTType_Decimal128 *out, + mongocrypt_status_t *status) + MONGOCRYPT_WARN_UNUSED_RESULT; + #endif /* MC_RANGE_ENCODING_PRIVATE_H */ diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c b/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c index 0d95218e64a..24cf0fdd655 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c +++ b/src/third_party/libmongocrypt/dist/src/mc-range-encoding.c @@ -230,7 +230,7 @@ mc_getTypeInfoDouble (mc_getTypeInfoDouble_args_t args, uint32_t bits_range; if (args.precision.set) { // Subnormal representations can support up to 5x10^-324 as a number - if (args.precision.value < 0 || args.precision.value > 324) { + if (args.precision.value > 324) { CLIENT_ERR ( "Precision must be between 0 and 324 inclusive, got: %" PRIu32, args.precision.value); @@ -313,3 +313,303 @@ mc_getTypeInfoDouble (mc_getTypeInfoDouble_args_t args, return true; } + +/** + * @brief There is no shipped algorithm for creating a full 128-bit integer from + * a Decimal128, but it's easy enough to write one of our own. + * + * @param dec + * @return mlib_int128 + */ +static mlib_int128 +dec128_to_int128 (mc_dec128 dec) +{ + // Only normal numbers + BSON_ASSERT (mc_dec128_is_finite (dec)); + BSON_ASSERT (!mc_dec128_is_nan (dec)); + // We don't support negative numbers + BSON_ASSERT (!mc_dec128_is_negative (dec)); + // There is no fractional part: + BSON_ASSERT (mc_dec128_is_zero (mc_dec128_modf (dec).frac)); + + mlib_int128 ret = mc_dec128_coeff (dec); + + // Scale the resulting number by a power of ten matching the exponent of the + // Decimal128: + int32_t exp = + ((int32_t) mc_dec128_get_biased_exp (dec)) - MC_DEC128_EXPONENT_BIAS; + // We will scale up/down based on whether it is negative: + mlib_int128 e1 = mlib_int128_pow10 ((uint8_t) abs (exp)); + if (exp < 0) { + ret = mlib_int128_div (ret, e1); + } else { + ret = mlib_int128_mul (ret, e1); + } + + return ret; +} + +bool +mc_getTypeInfoDecimal128 (mc_getTypeInfoDecimal128_args_t args, + mc_OSTType_Decimal128 *out, + mongocrypt_status_t *status) +{ + /// Basic param checks + if (args.min.set != args.max.set || args.min.set != args.precision.set) { + CLIENT_ERR ( + "min, max, and precision must all be set or must all be unset"); + return false; + } + + // We only accept normal numbers + if (mc_dec128_is_inf (args.value) || mc_dec128_is_nan (args.value)) { + CLIENT_ERR ("Infinity and Nan Decimal128 values are not supported."); + return false; + } + + // Check boundary if a range is set + if (args.min.set) { + // [min,max] must be valid + if (mc_dec128_greater_equal (args.min.value, args.max.value)) { + CLIENT_ERR ( + "The minimum value must be less than the maximum value, got " + "min: %s, max: %s", + mc_dec128_to_string (args.min.value).str, + mc_dec128_to_string (args.max.value).str); + return false; + } + + // Value must be within [min,max) + if (mc_dec128_greater (args.value, args.max.value) || + mc_dec128_less (args.value, args.min.value)) { + CLIENT_ERR ("Value must be greater than or equal to the minimum value " + "and less than or equal to the maximum value, got " + "min: %s, max: %s, value: %s", + mc_dec128_to_string (args.min.value).str, + mc_dec128_to_string (args.max.value).str, + mc_dec128_to_string (args.value).str); + return false; + } + } + + // Should we use precision mode? + // + // When we use precision mode, we try to represent as a decimal128 value that + // fits in [-2^127, 2^127] (i.e. is a valid int128) + // + // This check determines if we can represent any precision-truncated value as + // a 128-bit integer I.e. Is ((ub - lb) * 10^precision) < 128 bits. + // + // It is important that we determine whether a range and its precision would + // fit, regardless of the value to be encoded, because the encoding for + // precision-truncated-decimal128 is incompatible with the encoding of the + // full range. + bool use_precision_mode = false; + // The number of bits required to hold the result (used for precision mode) + uint8_t bits_range = 0; + if (args.precision.set) { + // Subnormal representations can support up to 5x10^-6182 as a number + if (args.precision.value > 6182) { + CLIENT_ERR ( + "Precision must be between 0 and 6182 inclusive, got: %" PRIu32, + args.precision.value); + return false; + } + + // max - min + mc_dec128 bounds_n1 = mc_dec128_sub (args.max.value, args.min.value); + // The size of [min, max]: (max - min) + 1 + mc_dec128 bounds = mc_dec128_add (bounds_n1, MC_DEC128_ONE); + + // We can overflow if max = max_dec128 and min = min_dec128 so make sure + // we have finite number after we do subtraction + if (mc_dec128_is_finite (bounds)) { + // This creates a range which is wider then we permit by our min/max + // bounds check with the +1 but it is as the algorithm is written in + // WRITING-11907. + mc_dec128 precision_scaled_bounds = + mc_dec128_scale (bounds, args.precision.value); + /// The number of bits required to hold the result for the given + /// precision (as decimal) + mc_dec128 bits_range_dec = mc_dec128_log2 (precision_scaled_bounds); + + if (mc_dec128_is_finite (bits_range_dec) && + mc_dec128_less (bits_range_dec, MC_DEC128 (128))) { + // We need fewer than 128 bits to hold the result. But round up, + // just to be sure: + int64_t r = mc_dec128_to_int64 ( + mc_dec128_round_integral_positive (bits_range_dec)); + BSON_ASSERT (r >= 0); + BSON_ASSERT (r <= UINT8_MAX); + // We've computed the proper 'bits_range' + bits_range = (uint8_t) r; + + if (bits_range < 128) { + use_precision_mode = true; + } + } + } + } + + // Constant zero + const mlib_int128 i128_zero = MLIB_INT128 (0); + // Constant 1 + const mlib_int128 i128_one = MLIB_INT128 (1); + // Constant 10 + const mlib_int128 i128_ten = MLIB_INT128 (10); + // Constant: 2^127 + const mlib_int128 i128_2pow127 = mlib_int128_lshift (i128_one, 127); + // ↑ Coincidentally has the same bit pattern as INT128_SMIN, but we're + // treating it as an unsigned number here, so don't get confused! + + if (use_precision_mode) { + BSON_ASSERT (args.precision.set); + // Example value: 31.4159 + // Example Precision = 2 + + // Shift the number up + // Returns: 3141.9 + mc_dec128 valScaled = mc_dec128_scale (args.value, args.precision.value); + + // Round the number down + // Returns 3141.0 + mc_dec128 valTrunc = mc_dec128_round_integral_zero (valScaled); + + // Shift the number down + // Returns: 31.41 + mc_dec128 v_prime = + mc_dec128_scale (valTrunc, -(int32_t) args.precision.value); + + // Adjust the number by the lower bound + // Make it an integer by scaling the number + // + // Returns 3141.0 + mc_dec128 v_prime2 = mc_dec128_scale ( + mc_dec128_sub (v_prime, args.min.value), args.precision.value); + // Round the number down again. min may have a fractional value with more + // decimal places than the precision (e.g. .001). Subtracting min may have + // resulted in v_prime2 with a non-zero fraction. v_prime2 is expected to + // have no fractional value when converting to int128. + v_prime2 = mc_dec128_round_integral_zero (v_prime2); + + BSON_ASSERT (mc_dec128_less (mc_dec128_log2 (v_prime2), MC_DEC128 (128))); + + // Resulting OST maximum + mlib_int128 ost_max = + mlib_int128_sub (mlib_int128_pow2 (bits_range), i128_one); + + // Now we need to get the Decimal128 out as a 128-bit integer + // But Decimal128 does not support conversion to Int128. + // + // If we think the Decimal128 fits in the range, based on the maximum + // value, we try to convert to int64 directly. + if (bits_range < 64) { + // Try conversion to int64, it may fail but since it is easy we try + // this first. + mc_dec128_flagset flags = {0}; + int64_t as64 = mc_dec128_to_int64_ex (v_prime2, &flags); + if (flags.bits == 0) { + // No error. It fits + *out = (mc_OSTType_Decimal128){ + .value = MLIB_INT128_CAST (as64), + .min = i128_zero, + .max = ost_max, + }; + return true; + } else { + // Conversion failure to 64-bit. Possible overflow, imprecision, + // etc. Fallback to slower dec128_to_int128 + } + } + + mlib_int128 u_ret = dec128_to_int128 (v_prime2); + + *out = (mc_OSTType_Decimal128){ + .value = u_ret, + .min = i128_zero, + .max = ost_max, + }; + + return true; + } + + // The coefficient of the number, without exponent/sign + const mlib_int128 coeff = mc_dec128_coeff (args.value); + + if (mlib_int128_eq (coeff, i128_zero)) { + // If the coefficient is zero, the result is encoded as the midpoint + // between zero and 2^128-1 + *out = (mc_OSTType_Decimal128){ + .value = i128_2pow127, + .min = i128_zero, + .max = MLIB_INT128_UMAX, + }; + return true; + } + + // Coefficient is an unsigned value. We'll later scale our answer based on + // the sign of the actual Decimal128 + const bool isNegative = mc_dec128_is_negative (args.value); + + // cMax = 10^34 - 1 (The largest integer representable in Decimal128) + const mlib_int128 cMax = + mlib_int128_sub (mlib_int128_pow10 (34), MLIB_INT128_CAST (1)); + const mlib_int128 cMax_div_ten = mlib_int128_div (cMax, i128_ten); + + // The biased exponent from the decimal number. The paper refers to the + // expression (e - e_min), which is the value of the biased exponent. + const uint32_t exp_biased = mc_dec128_get_biased_exp (args.value); + + // ρ (rho) is the greatest integer such that: coeff×10^ρ <= cMax + unsigned rho = 0; + // Keep track of the subexpression coeff×10^ρ rather than recalculating it + // time. + // Initially: (ρ = 0) -> (10^ρ = 1) -> (coeff×10^ρ = coeff×1 = coeff): + mlib_int128 coeff_scaled = coeff; + // Calculate ρ: This could be done using a log10 with a division, but that + // is far more work than just a few multiplications. + // While: coeff×ten^ρ < cMax/10: + while (mlib_int128_ucmp (coeff_scaled, cMax_div_ten) < 0) { + // Increase rho until we pass cMax/10 + rho++; + // Scale our computed subexpression rather than fully recomputing it + coeff_scaled = mlib_int128_mul (coeff_scaled, i128_ten); + } + + // No multiplication by 10 should ever send us from N < cMax/10 to N > cMax + BSON_ASSERT (mlib_int128_ucmp (coeff_scaled, cMax) <= 0); + + mlib_int128 result; + if (rho <= exp_biased) { + // ρ is less-than/equal to the exponent with bias. + + // Diff between the biased exponent and ρ. + // Value in paper is spelled "e - e_min - ρ" + const uint32_t exp_diff = exp_biased - (uint32_t) rho; + // cMax * (exp_diff) + const mlib_int128 cmax_scaled = + mlib_int128_mul (cMax, MLIB_INT128_CAST (exp_diff)); + // coeff * 10^rho * cMax * (exp_biased - rho) + result = mlib_int128_add (coeff_scaled, cmax_scaled); + } else { + const mlib_int128 biased_scale = mlib_int128_pow10 ((uint8_t) exp_biased); + result = mlib_int128_mul (biased_scale, coeff); + } + + // Always add 2^127: + result = mlib_int128_add (result, i128_2pow127); + + if (isNegative) { + // We calculated the value of the positive coefficient, but the decimal is + // negative. That's okay: Just flip the sign of the encoded result: + result = mlib_int128_negate (result); + } + + *out = (mc_OSTType_Decimal128){ + .value = result, + .min = i128_zero, + .max = MLIB_INT128_UMAX, + }; + + return true; +} diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h index 9b348a156f3..54fa345ae69 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h +++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-generator.template.h @@ -16,9 +16,6 @@ // mc-range-mincover-generator.template.h is meant to be included in another // source file. -#ifndef BITS -#error "must be included with BITS defined" -#endif // TODO: replace `CONCAT` with `BSON_CONCAT` after libbson dependency is // upgraded to 1.20.0 or higher. @@ -32,11 +29,75 @@ #define CONCAT3(a, b, c) CONCAT (a, CONCAT (b, c)) #endif -#define UINT_T CONCAT3 (uint, BITS, _t) -#define UINT_C CONCAT3 (UINT, BITS, _C) -#define FMT_UINT_T CONCAT (PRId, BITS) -#define WITH_BITS(X) CONCAT3 (X, _u, BITS) +#if !(defined(UINT_T) && defined(UINT_C) && defined(UINT_FMT_S) && \ + defined(DECORATE_NAME)) +#ifdef __INTELLISENSE__ +#define UINT_T uint32_t +#define UINT_C UINT32_C +#define UINT_FMT_S PRIu32 +#define DECORATE_NAME(Name) Name##_u32 +#else +#error All of UINT_T, UINT_C, UINT_FMT_S, UINT_FMT_ARG, and DECORATE_NAME must be defined before #including this file +#endif +#endif + +#define BITS (sizeof (UINT_T) * CHAR_BIT) + +#define ZERO UINT_C (0) + +// Default for UINT_FMT_ARG +#ifndef UINT_FMT_ARG +#define UINT_FMT_ARG(X) X +#endif + +// Default comparison +#ifndef UINT_LESSTHAN +#define UINT_LESSTHAN(A, B) ((A) < (B)) +#endif +#ifndef MC_UINT_MAX +#define MC_UINT_MAX ~(UINT_C (0)) +#endif + +// Default addition +#ifndef UINT_ADD +#define UINT_ADD(A, B) ((A) + (B)) +#endif +#ifndef UINT_SUB +#define UINT_SUB(A, B) ((A) - (B)) +#endif + +// Default lshift (also handles negatives as right-shift) +#ifndef UINT_LSHIFT +static inline UINT_T +DECORATE_NAME (_mc_default_lshift) (UINT_T lhs, int off) +{ + if (off < 0) { + return lhs >> -off; + } else { + return lhs << off; + } +} +#define UINT_LSHIFT DECORATE_NAME (_mc_default_lshift) +#endif + +#ifndef UINT_BITOR +#define UINT_BITOR(A, B) ((A) | (B)) +#endif + +static inline int +DECORATE_NAME (_mc_compare) (UINT_T lhs, UINT_T rhs) +{ + if (UINT_LESSTHAN (lhs, rhs)) { + return -1; + } else if (UINT_LESSTHAN (rhs, lhs)) { + return 1; + } else { + return 0; + } +} + +#define UINT_COMPARE DECORATE_NAME (_mc_compare) // MinCoverGenerator models the MinCoverGenerator type added in // SERVER-68600. @@ -46,31 +107,31 @@ typedef struct { size_t _sparsity; // _maxlen is the maximum bit length of edges in the mincover. size_t _maxlen; -} WITH_BITS (MinCoverGenerator); - -static inline WITH_BITS (MinCoverGenerator) * - WITH_BITS (MinCoverGenerator_new) (UINT_T rangeMin, - UINT_T rangeMax, - UINT_T max, - size_t sparsity, - mongocrypt_status_t *status) +} DECORATE_NAME (MinCoverGenerator); + +static inline DECORATE_NAME (MinCoverGenerator) * + DECORATE_NAME (MinCoverGenerator_new) (UINT_T rangeMin, + UINT_T rangeMax, + UINT_T max, + size_t sparsity, + mongocrypt_status_t *status) { BSON_ASSERT_PARAM (status); - if (rangeMin > rangeMax) { - CLIENT_ERR ("Range min (%" FMT_UINT_T - ") must be less than or equal to range max (%" FMT_UINT_T + if (UINT_COMPARE (rangeMin, rangeMax) > 0) { + CLIENT_ERR ("Range min (%" UINT_FMT_S + ") must be less than or equal to range max (%" UINT_FMT_S ") for range search", - rangeMin, - rangeMax); + UINT_FMT_ARG (rangeMin), + UINT_FMT_ARG (rangeMax)); return NULL; } - if (rangeMax > max) { - CLIENT_ERR ("Range max (%" FMT_UINT_T - ") must be less than or equal to max (%" FMT_UINT_T + if (UINT_COMPARE (rangeMax, max) > 0) { + CLIENT_ERR ("Range max (%" UINT_FMT_S + ") must be less than or equal to max (%" UINT_FMT_S ") for range search", - rangeMax, - max); + UINT_FMT_ARG (rangeMax), + UINT_FMT_ARG (max)); return NULL; } @@ -78,17 +139,18 @@ static inline WITH_BITS (MinCoverGenerator) * CLIENT_ERR ("Sparsity must be > 0"); return NULL; } - WITH_BITS (MinCoverGenerator) *mcg = - bson_malloc0 (sizeof (WITH_BITS (MinCoverGenerator))); + DECORATE_NAME (MinCoverGenerator) *mcg = + bson_malloc0 (sizeof (DECORATE_NAME (MinCoverGenerator))); mcg->_rangeMin = rangeMin; mcg->_rangeMax = rangeMax; - mcg->_maxlen = (size_t) BITS - WITH_BITS (mc_count_leading_zeros) (max); + mcg->_maxlen = (size_t) BITS - DECORATE_NAME (mc_count_leading_zeros) (max); mcg->_sparsity = sparsity; return mcg; } static inline void -WITH_BITS (MinCoverGenerator_destroy) (WITH_BITS (MinCoverGenerator) * mcg) +DECORATE_NAME (MinCoverGenerator_destroy) (DECORATE_NAME (MinCoverGenerator) * + mcg) { bson_free (mcg); } @@ -96,9 +158,9 @@ WITH_BITS (MinCoverGenerator_destroy) (WITH_BITS (MinCoverGenerator) * mcg) // applyMask applies a mask of 1 bits starting from the right. // Bits 0 to bit-1 are replaced with 1. Other bits are left as-is. static inline UINT_T -WITH_BITS (applyMask) (UINT_T value, size_t maskedBits) +DECORATE_NAME (applyMask) (UINT_T value, size_t maskedBits) { - const UINT_T ones = ~UINT_C (0); + const UINT_T ones = MC_UINT_MAX; BSON_ASSERT (maskedBits <= (size_t) BITS); BSON_ASSERT (maskedBits >= 0); @@ -108,14 +170,13 @@ WITH_BITS (applyMask) (UINT_T value, size_t maskedBits) } const size_t shift = ((size_t) BITS - maskedBits); - const UINT_T mask = ones >> shift; - return value | mask; + const UINT_T mask = UINT_LSHIFT (ones, -(int) shift); + return UINT_BITOR (value, mask); } static inline bool -WITH_BITS (MinCoverGenerator_isLevelStored) (WITH_BITS (MinCoverGenerator) * - mcg, - size_t maskedBits) +DECORATE_NAME (MinCoverGenerator_isLevelStored) ( + DECORATE_NAME (MinCoverGenerator) * mcg, size_t maskedBits) { BSON_ASSERT_PARAM (mcg); size_t level = mcg->_maxlen - maskedBits; @@ -123,9 +184,8 @@ WITH_BITS (MinCoverGenerator_isLevelStored) (WITH_BITS (MinCoverGenerator) * } char * -WITH_BITS (MinCoverGenerator_toString) (WITH_BITS (MinCoverGenerator) * mcg, - UINT_T start, - size_t maskedBits) +DECORATE_NAME (MinCoverGenerator_toString) ( + DECORATE_NAME (MinCoverGenerator) * mcg, UINT_T start, size_t maskedBits) { BSON_ASSERT_PARAM (mcg); BSON_ASSERT (maskedBits <= mcg->_maxlen); @@ -136,33 +196,35 @@ WITH_BITS (MinCoverGenerator_toString) (WITH_BITS (MinCoverGenerator) * mcg, return bson_strdup ("root"); } - UINT_T shifted = start >> maskedBits; - char *valueBin = WITH_BITS (mc_convert_to_bitstring) (shifted); + UINT_T shifted = UINT_LSHIFT (start, -(int) maskedBits); + mc_bitstring valueBin = DECORATE_NAME (mc_convert_to_bitstring) (shifted); char *ret = - bson_strndup (valueBin + ((size_t) BITS - mcg->_maxlen + maskedBits), + bson_strndup (valueBin.str + ((size_t) BITS - mcg->_maxlen + maskedBits), mcg->_maxlen + maskedBits); - bson_free (valueBin); return ret; } static inline void -WITH_BITS (MinCoverGenerator_minCoverRec) (WITH_BITS (MinCoverGenerator) * mcg, - mc_array_t *c, - UINT_T blockStart, - size_t maskedBits) +DECORATE_NAME (MinCoverGenerator_minCoverRec) ( + DECORATE_NAME (MinCoverGenerator) * mcg, + mc_array_t *c, + UINT_T blockStart, + size_t maskedBits) { BSON_ASSERT_PARAM (mcg); BSON_ASSERT_PARAM (c); - const UINT_T blockEnd = WITH_BITS (applyMask) (blockStart, maskedBits); + const UINT_T blockEnd = DECORATE_NAME (applyMask) (blockStart, maskedBits); - if (blockEnd < mcg->_rangeMin || blockStart > mcg->_rangeMax) { + if (UINT_COMPARE (blockEnd, mcg->_rangeMin) < 0 || + UINT_COMPARE (blockStart, mcg->_rangeMax) > 0) { return; } - if (blockStart >= mcg->_rangeMin && blockEnd <= mcg->_rangeMax && - WITH_BITS (MinCoverGenerator_isLevelStored) (mcg, maskedBits)) { - char *edge = - WITH_BITS (MinCoverGenerator_toString) (mcg, blockStart, maskedBits); + if (UINT_COMPARE (blockStart, mcg->_rangeMin) >= 0 && + UINT_COMPARE (blockEnd, mcg->_rangeMax) <= 0 && + DECORATE_NAME (MinCoverGenerator_isLevelStored) (mcg, maskedBits)) { + char *edge = DECORATE_NAME (MinCoverGenerator_toString) ( + mcg, blockStart, maskedBits); _mc_array_append_val (c, edge); return; } @@ -170,18 +232,22 @@ WITH_BITS (MinCoverGenerator_minCoverRec) (WITH_BITS (MinCoverGenerator) * mcg, BSON_ASSERT (maskedBits > 0); const size_t newBits = maskedBits - 1u; - WITH_BITS (MinCoverGenerator_minCoverRec) (mcg, c, blockStart, newBits); - WITH_BITS (MinCoverGenerator_minCoverRec) - (mcg, c, blockStart | UINT_C (1) << newBits, newBits); + DECORATE_NAME (MinCoverGenerator_minCoverRec) (mcg, c, blockStart, newBits); + DECORATE_NAME (MinCoverGenerator_minCoverRec) + (mcg, + c, + UINT_BITOR (blockStart, UINT_LSHIFT (UINT_C (1), (int) newBits)), + newBits); } static inline mc_mincover_t * -WITH_BITS (MinCoverGenerator_minCover) (WITH_BITS (MinCoverGenerator) * mcg) +DECORATE_NAME (MinCoverGenerator_minCover) (DECORATE_NAME (MinCoverGenerator) * + mcg) { BSON_ASSERT_PARAM (mcg); mc_mincover_t *mc = mc_mincover_new (); - WITH_BITS (MinCoverGenerator_minCoverRec) - (mcg, &mc->mincover, 0, mcg->_maxlen); + DECORATE_NAME (MinCoverGenerator_minCoverRec) + (mcg, &mc->mincover, ZERO, mcg->_maxlen); return mc; } @@ -190,43 +256,53 @@ WITH_BITS (MinCoverGenerator_minCover) (WITH_BITS (MinCoverGenerator) * mcg) // lowerBound, min, upperBound, and max are expected to come from the result // of mc_getTypeInfo. static bool -WITH_BITS (adjustBounds) (UINT_T *lowerBound, - bool includeLowerBound, - UINT_T min, - UINT_T *upperBound, - bool includeUpperBound, - UINT_T max, - mongocrypt_status_t *status) +DECORATE_NAME (adjustBounds) (UINT_T *lowerBound, + bool includeLowerBound, + UINT_T min, + UINT_T *upperBound, + bool includeUpperBound, + UINT_T max, + mongocrypt_status_t *status) { BSON_ASSERT_PARAM (lowerBound); BSON_ASSERT_PARAM (upperBound); if (!includeLowerBound) { - if (*lowerBound >= max) { - CLIENT_ERR ("Lower bound (%" FMT_UINT_T - ") must be less than the range maximum (%" FMT_UINT_T + if (UINT_COMPARE (*lowerBound, max) >= 0) { + CLIENT_ERR ("Lower bound (%" UINT_FMT_S + ") must be less than the range maximum (%" UINT_FMT_S ") if lower bound is excluded from range.", - *lowerBound, - max); + UINT_FMT_ARG (*lowerBound), + UINT_FMT_ARG (max)); return false; } - *lowerBound += 1u; + *lowerBound = UINT_ADD (*lowerBound, UINT_C (1)); } if (!includeUpperBound) { - if (*upperBound <= min) { - CLIENT_ERR ("Upper bound (%" FMT_UINT_T - ") must be greater than the range minimum (%" FMT_UINT_T + if (UINT_COMPARE (*upperBound, min) <= 0) { + CLIENT_ERR ("Upper bound (%" UINT_FMT_S + ") must be greater than the range minimum (%" UINT_FMT_S ") if upper bound is excluded from range.", - *upperBound, - max); + UINT_FMT_ARG (*upperBound), + UINT_FMT_ARG (min)); return false; } - *upperBound -= 1u; + *upperBound = UINT_SUB (*upperBound, UINT_C (1)); } return true; } #undef UINT_T #undef UINT_C -#undef FMT_UINT_T -#undef WITH_BITS +#undef UINT_FMT_S +#undef UINT_FMT_ARG +#undef DECORATE_NAME +#undef BITS +#undef UINT_COMPARE +#undef UINT_ADD +#undef UINT_SUB +#undef UINT_LSHIFT +#undef UINT_BITOR +#undef MC_UINT_MAX +#undef ZERO +#undef UINT_LESSTHAN diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h index 78d6bafdf60..1a9ec6daf8e 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h +++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover-private.h @@ -21,6 +21,7 @@ #include <stdint.h> #include "mc-optional-private.h" #include "mongocrypt-status-private.h" +#include "mc-dec128.h" // mc_mincover_t represents the results of the mincover algorithm. typedef struct _mc_mincover_t mc_mincover_t; @@ -88,4 +89,22 @@ mc_getMincoverDouble (mc_getMincoverDouble_args_t args, mongocrypt_status_t *status) MONGOCRYPT_WARN_UNUSED_RESULT; + +typedef struct { + mc_dec128 lowerBound; + bool includeLowerBound; + mc_dec128 upperBound; + bool includeUpperBound; + size_t sparsity; + mc_optional_dec128_t min, max; + mc_optional_uint32_t precision; +} mc_getMincoverDecimal128_args_t; + +// mc_getMincoverDecimal128 implements the Mincover Generation algorithm +// described in SERVER-68600 for Decimal128 (as mc_dec128). +mc_mincover_t * +mc_getMincoverDecimal128 (mc_getMincoverDecimal128_args_t args, + mongocrypt_status_t *status) + MONGOCRYPT_WARN_UNUSED_RESULT; + #endif /* MC_RANGE_MINCOVER_PRIVATE_H */ diff --git a/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c b/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c index a8e1de4458f..d5c9dd17232 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c +++ b/src/third_party/libmongocrypt/dist/src/mc-range-mincover.c @@ -68,49 +68,73 @@ mc_mincover_destroy (mc_mincover_t *mincover) bson_free (mincover); } -#define BITS 32 +#define UINT_T uint32_t +#define UINT_C UINT32_C +#define UINT_FMT_S PRIu32 +#define DECORATE_NAME(N) N##_u32 #include "mc-range-mincover-generator.template.h" -#undef BITS + +#define UINT_T uint64_t +#define UINT_C UINT64_C +#define UINT_FMT_S PRIu64 +#define DECORATE_NAME(N) N##_u64 +#include "mc-range-mincover-generator.template.h" + +#define UINT_T mlib_int128 +#define UINT_C MLIB_INT128 +#define UINT_FMT_S "s" +#define UINT_FMT_ARG(X) (mlib_int128_format (X).str) +#define DECORATE_NAME(N) N##_u128 +#define UINT_LESSTHAN(L, R) (mlib_int128_ucmp (L, R) < 0) +#define UINT_ADD mlib_int128_add +#define UINT_SUB mlib_int128_sub +#define UINT_LSHIFT mlib_int128_lshift +#define MC_UINT_MAX MLIB_INT128_UMAX +#define UINT_BITOR mlib_int128_bitor +#include "mc-range-mincover-generator.template.h" + // Check bounds and return an error message including the original inputs. -#define CHECK_BOUNDS(args, FMT) \ +#define IDENTITY(X) X +#define LESSTHAN(L, R) ((L) < (R)) +#define CHECK_BOUNDS(args, FMT, FormatArg, LessThan) \ if (1) { \ if ((args).min.set) { \ - if ((args).upperBound < (args).min.value) { \ + if (LessThan ((args).upperBound, (args).min.value)) { \ CLIENT_ERR ( \ "Upper bound (%" FMT \ ") must be greater than or equal to the range minimum (%" FMT \ ")", \ - (args).upperBound, \ - (args).min.value); \ + FormatArg ((args).upperBound), \ + FormatArg ((args).min.value)); \ return false; \ } \ if (!(args).includeUpperBound && \ - (args).upperBound <= (args).min.value) { \ + !LessThan ((args.min.value), (args.upperBound))) { \ CLIENT_ERR ("Upper bound (%" FMT \ ") must be greater than the range minimum (%" FMT \ ") if upper bound is excluded from range", \ - (args).upperBound, \ - (args).min.value); \ + FormatArg ((args).upperBound), \ + FormatArg ((args).min.value)); \ return false; \ } \ } \ if ((args).max.set) { \ - if ((args).lowerBound > (args).max.value) { \ + if (LessThan ((args).max.value, (args).lowerBound)) { \ CLIENT_ERR ( \ "Lower bound (%" FMT \ ") must be less than or equal to the range maximum (%" FMT ")", \ - (args).lowerBound, \ - (args).max.value); \ + FormatArg ((args).lowerBound), \ + FormatArg ((args).max.value)); \ return false; \ } \ if (!(args).includeLowerBound && \ - (args).lowerBound >= (args).max.value) { \ + !LessThan ((args).lowerBound, (args).max.value)) { \ CLIENT_ERR ("Lower bound (%" FMT \ ") must be less than the range maximum (%" FMT \ ") if lower bound is excluded from range", \ - (args).lowerBound, \ - (args).max.value); \ + FormatArg ((args).lowerBound), \ + FormatArg ((args).max.value)); \ return false; \ } \ } \ @@ -122,7 +146,7 @@ mc_getMincoverInt32 (mc_getMincoverInt32_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM (status); - CHECK_BOUNDS (args, PRId32); + CHECK_BOUNDS (args, PRId32, IDENTITY, LESSTHAN); mc_OSTType_Int32 a, b; if (!mc_getTypeInfo32 ((mc_getTypeInfo32_args_t){.min = args.min, .max = args.max, @@ -162,16 +186,12 @@ mc_getMincoverInt32 (mc_getMincoverInt32_args_t args, return mc; } -#define BITS 64 -#include "mc-range-mincover-generator.template.h" -#undef BITS - mc_mincover_t * mc_getMincoverInt64 (mc_getMincoverInt64_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM (status); - CHECK_BOUNDS (args, PRId64); + CHECK_BOUNDS (args, PRId64, IDENTITY, LESSTHAN); mc_OSTType_Int64 a, b; if (!mc_getTypeInfo64 ((mc_getTypeInfo64_args_t){.min = args.min, .max = args.max, @@ -218,7 +238,7 @@ mc_getMincoverDouble (mc_getMincoverDouble_args_t args, mongocrypt_status_t *status) { BSON_ASSERT_PARAM (status); - CHECK_BOUNDS (args, "g"); + CHECK_BOUNDS (args, "g", IDENTITY, LESSTHAN); mc_OSTType_Double a, b; if (!mc_getTypeInfoDouble ( @@ -262,3 +282,54 @@ mc_getMincoverDouble (mc_getMincoverDouble_args_t args, MinCoverGenerator_destroy_u64 (mcg); return mc; } + +mc_mincover_t * +mc_getMincoverDecimal128 (mc_getMincoverDecimal128_args_t args, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (status); +#define ToString(Dec) (mc_dec128_to_string (Dec).str) + CHECK_BOUNDS (args, "s", ToString, mc_dec128_less); + + mc_OSTType_Decimal128 a, b; + if (!mc_getTypeInfoDecimal128 ( + (mc_getTypeInfoDecimal128_args_t){.value = args.lowerBound, + .min = args.min, + .max = args.max, + .precision = args.precision}, + &a, + status)) { + return NULL; + } + if (!mc_getTypeInfoDecimal128 ( + (mc_getTypeInfoDecimal128_args_t){.value = args.upperBound, + .min = args.min, + .max = args.max, + .precision = args.precision}, + &b, + status)) { + return NULL; + } + + BSON_ASSERT (mlib_int128_eq (a.min, b.min)); + BSON_ASSERT (mlib_int128_eq (a.max, b.max)); + + if (!adjustBounds_u128 (&a.value, + args.includeLowerBound, + a.min, + &b.value, + args.includeUpperBound, + b.max, + status)) { + return NULL; + } + + MinCoverGenerator_u128 *mcg = MinCoverGenerator_new_u128 ( + a.value, b.value, a.max, args.sparsity, status); + if (!mcg) { + return NULL; + } + mc_mincover_t *mc = MinCoverGenerator_minCover_u128 (mcg); + MinCoverGenerator_destroy_u128 (mcg); + return mc; +} diff --git a/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c b/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c index 3bacc15795a..3b6a8d85a6b 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c +++ b/src/third_party/libmongocrypt/dist/src/mc-rangeopts.c @@ -282,6 +282,13 @@ mc_RangeOpts_appendMin (const mc_RangeOpts_t *ro, CLIENT_ERR ("failed to append BSON"); return false; } + } else if (valueType == BSON_TYPE_DECIMAL128) { + const bson_decimal128_t min = + mc_dec128_to_bson_decimal128 (MC_DEC128_LARGEST_NEGATIVE); + if (!BSON_APPEND_DECIMAL128 (out, fieldName, &min)) { + CLIENT_ERR ("failed to append BSON"); + return false; + } } else { CLIENT_ERR ("unsupported BSON type: %s for range", mc_bson_type_to_string (valueType)); @@ -337,6 +344,13 @@ mc_RangeOpts_appendMax (const mc_RangeOpts_t *ro, CLIENT_ERR ("failed to append BSON"); return false; } + } else if (valueType == BSON_TYPE_DECIMAL128) { + const bson_decimal128_t max = + mc_dec128_to_bson_decimal128 (MC_DEC128_LARGEST_POSITIVE); + if (!BSON_APPEND_DECIMAL128 (out, fieldName, &max)) { + CLIENT_ERR ("failed to append BSON"); + return false; + } } else { CLIENT_ERR ("unsupported BSON type: %s for range", mc_bson_type_to_string (valueType)); diff --git a/src/third_party/libmongocrypt/dist/src/mc-reader.c b/src/third_party/libmongocrypt/dist/src/mc-reader.c index 8b87b815fae..a5730bd744d 100644 --- a/src/third_party/libmongocrypt/dist/src/mc-reader.c +++ b/src/third_party/libmongocrypt/dist/src/mc-reader.c @@ -185,7 +185,8 @@ mc_reader_read_buffer (mc_reader_t *reader, buf, ptr, (size_t) length)) { CLIENT_ERR ("%s failed to copy " "data of length %" PRIu64, - reader->parser_name); + reader->parser_name, + length); return false; } diff --git a/src/third_party/libmongocrypt/dist/src/mc-writer-private.h b/src/third_party/libmongocrypt/dist/src/mc-writer-private.h new file mode 100644 index 00000000000..96c132d354a --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mc-writer-private.h @@ -0,0 +1,92 @@ +/* + * Copyright 2022-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MONGOCRYPT_WRITER_PRIVATE_H +#define MONGOCRYPT_WRITER_PRIVATE_H + +#include "mongocrypt-buffer-private.h" +#include <stdint.h> +#include <stdlib.h> + +/** + * A non-owning forward-only cursor api to write to a buffer. + * + * Tracks length of buffer and current position of buffer. parser_name is + * typically __FUNCTION__ to provide useful error messages automatically. + * + * All numbers are written as little endian. + */ + +struct _mc_writer_t { + uint8_t *ptr; + uint64_t pos; + uint64_t len; + + const char *parser_name; +}; + +typedef struct _mc_writer_t mc_writer_t; + +void +mc_writer_init (mc_writer_t *writer, + uint8_t *ptr, + uint64_t len, + const char *parser_name); + +void +mc_writer_init_from_buffer (mc_writer_t *writer, + _mongocrypt_buffer_t *buf, + const char *parser_name); + +mc_writer_t * +mc_writer_new (uint8_t *ptr, uint64_t len, const char *parser_name); + +void +mc_writer_destroy (mc_writer_t *writer); + +bool +mc_writer_write_u8 (mc_writer_t *writer, + const uint8_t value, + mongocrypt_status_t *status); + +bool +mc_writer_write_u32 (mc_writer_t *writer, + const uint32_t value, + mongocrypt_status_t *status); + +bool +mc_writer_write_u64 (mc_writer_t *writer, + const uint64_t value, + mongocrypt_status_t *status); + +bool +mc_writer_write_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + uint64_t length, + mongocrypt_status_t *status); + + +bool +mc_writer_write_uuid_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status); + +bool +mc_writer_write_prfblock_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status); + +#endif /* MONGOCRYPT_READER_PRIVATE_H */ diff --git a/src/third_party/libmongocrypt/dist/src/mc-writer.c b/src/third_party/libmongocrypt/dist/src/mc-writer.c new file mode 100644 index 00000000000..55c208a2020 --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mc-writer.c @@ -0,0 +1,177 @@ +/* + * Copyright 2022-present MongoDB, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "mongocrypt-private.h" + +#include "mc-writer-private.h" + +#define CHECK_AND_RETURN(x) \ + if (!(x)) { \ + return false; \ + } + +#define CHECK_REMAINING_BUFFER_AND_RET(write_size) \ + if ((write_size) > writer->len - writer->pos) { \ + CLIENT_ERR ("%s expected at most %" PRIu64 \ + " bytes, got: %" PRIu64, \ + writer->parser_name, \ + (writer->len - writer->pos), \ + (uint64_t) (write_size)); \ + return false; \ + } + +void +mc_writer_init (mc_writer_t *writer, + uint8_t *ptr, + uint64_t len, + const char *parser_name) +{ + BSON_ASSERT_PARAM (writer); + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (parser_name); + + *writer = (mc_writer_t){ + .pos = 0u, .ptr = ptr, .len = len, .parser_name = parser_name}; +} + +void +mc_writer_init_from_buffer (mc_writer_t *writer, + _mongocrypt_buffer_t *buf, + const char *parser_name) +{ + BSON_ASSERT_PARAM (writer); + BSON_ASSERT_PARAM (buf); + BSON_ASSERT_PARAM (parser_name); + + mc_writer_init (writer, buf->data, buf->len, parser_name); +} + +mc_writer_t * +mc_writer_new (uint8_t *ptr, uint64_t len, const char *parser_name) +{ + BSON_ASSERT_PARAM (ptr); + BSON_ASSERT_PARAM (parser_name); + + mc_writer_t *writer = bson_malloc (sizeof (mc_writer_t)); + mc_writer_init (writer, ptr, len, parser_name); + return writer; +} + +void +mc_writer_destroy (mc_writer_t *writer) +{ + bson_free (writer); +} + +bool +mc_writer_write_u8 (mc_writer_t *writer, + const uint8_t value, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (writer); + CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint8_t)); + memcpy (writer->ptr + writer->pos, &value, sizeof (uint8_t)); + + writer->pos += sizeof (uint8_t); + return true; +} + +bool +mc_writer_write_u32 (mc_writer_t *writer, + const uint32_t value, + mongocrypt_status_t *status) +{ + CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint32_t)); + + uint32_t temp = BSON_UINT32_TO_LE (value); + memcpy (writer->ptr + writer->pos, &temp, sizeof (uint32_t)); + writer->pos += sizeof (uint32_t); + + return true; +} + +bool +mc_writer_write_u64 (mc_writer_t *writer, + const uint64_t value, + mongocrypt_status_t *status) +{ + CHECK_REMAINING_BUFFER_AND_RET (sizeof (uint64_t)); + + uint64_t temp = BSON_UINT64_TO_LE (value); + memcpy (writer->ptr + writer->pos, &temp, sizeof (uint64_t)); + writer->pos += sizeof (uint64_t); + + return true; +} + +bool +mc_writer_write_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + uint64_t length, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (writer); + BSON_ASSERT_PARAM (buf); + + if (length > buf->len) { + CLIENT_ERR ("%s cannot write %" PRIu64 + " bytes from buffer with length %" PRIu32, + writer->parser_name, + length, + buf->len); + + return false; + } + + CHECK_REMAINING_BUFFER_AND_RET (length); + + if (length > SIZE_MAX) { + CLIENT_ERR ("%s failed to copy " + "data of length %" PRIu64, + writer->parser_name, + length); + return false; + } + + memcpy (writer->ptr + writer->pos, buf->data, (size_t) length); + writer->pos += length; + + return true; +} + +bool +mc_writer_write_uuid_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (writer); + BSON_ASSERT_PARAM (buf); + + CHECK_AND_RETURN (mc_writer_write_buffer (writer, buf, UUID_LEN, status)); + return true; +} + +bool +mc_writer_write_prfblock_buffer (mc_writer_t *writer, + const _mongocrypt_buffer_t *buf, + mongocrypt_status_t *status) +{ + BSON_ASSERT_PARAM (writer); + BSON_ASSERT_PARAM (buf); + + CHECK_AND_RETURN (mc_writer_write_buffer (writer, buf, PRF_LEN, status)); + return true; +} diff --git a/src/third_party/libmongocrypt/dist/src/mlib/check.hpp b/src/third_party/libmongocrypt/dist/src/mlib/check.hpp new file mode 100644 index 00000000000..18642b2f624 --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mlib/check.hpp @@ -0,0 +1,89 @@ +#ifndef MLIB_CHECK_HPP_INCLUDED +#define MLIB_CHECK_HPP_INCLUDED + +#include <iostream> +#include <cstdio> +#include <string> + +namespace mlib +{ +namespace detail +{ +struct check_info { + const char *filename; + int line; + const char *expr; +}; + +struct nil { +}; + +template <typename Left> struct bound_lhs { + check_info info; + Left value; + +#define DEFOP(Oper) \ + template <typename Rhs> nil operator Oper (Rhs rhs) const noexcept \ + { \ + if (value Oper rhs) { \ + return {}; \ + } \ + std::fprintf (stderr, \ + "%s:%d: CHECK( %s ) failed!\n", \ + info.filename, \ + info.line, \ + info.expr); \ + std::cerr << "Expanded expression: " << value << " " #Oper " " << rhs \ + << '\n'; \ + std::exit (2); \ + } + DEFOP (==) + DEFOP (!=) + DEFOP (<) + DEFOP (<=) + DEFOP (>) + DEFOP (>=) +#undef DEFOP +}; + +struct check_magic { + check_info info; + + template <typename Oper> + bound_lhs<Oper> + operator->*(Oper op) + { + return bound_lhs<Oper>{info, op}; + } +}; + +struct check_consume { + void + operator= (nil) + { + } + + void + operator= (bound_lhs<bool> const &l) + { + // Invoke the test for truthiness: + (void) (l == true); + } +}; + +/** + * @brief Create an assertion that prints the expanded expression upon failure. + * + * Only supports simple comparison binary expressions, and plain boolean + * expressions + */ +#define MLIB_CHECK(Cond) \ + ::mlib::detail::check_consume{} = \ + ::mlib::detail::check_magic{ \ + ::mlib::detail::check_info{__FILE__, __LINE__, #Cond}} \ + ->*Cond + +} // namespace detail +} // namespace mlib + +#endif // MLIB_CHECK_HPP_INCLUDED diff --git a/src/third_party/libmongocrypt/dist/src/mlib/endian.h b/src/third_party/libmongocrypt/dist/src/mlib/endian.h new file mode 100644 index 00000000000..e44080dde1c --- /dev/null +++ b/src/third_party/libmongocrypt/dist/src/mlib/endian.h @@ -0,0 +1,42 @@ +#ifndef MLIB_ENDIAN_H_INCLUDED +#define MLIB_ENDIAN_H_INCLUDED + +#ifdef __has_include +// Some platforms require including other headers that will define the necessary +// macros +#if __has_include(<endian.h>) +// This may recursively include our own file in a pathological case, but that +// won't cause an issue. The default will include the system's version instead: +#include <endian.h> +#endif +#if __has_include(<sys/param.h>) +#include <sys/param.h> +#endif +#endif + +#include "./macros.h" + +enum mlib_endian_kind { +#ifdef _MSC_VER // MSVC only targets little-endian arches at the moment + MLIB_ENDIAN_LITTLE = 1234, + MLIB_ENDIAN_BIG = 4321, + MLIB_ENDIAN_NATIVE = MLIB_ENDIAN_LITTLE, +#elif defined(__BYTE_ORDER__) // Commonly built-in defined in newer compilers + MLIB_ENDIAN_LITTLE = __ORDER_LITTLE_ENDIAN__, + MLIB_ENDIAN_BIG = __ORDER_BIG_ENDIAN__, + MLIB_ENDIAN_NATIVE = __BYTE_ORDER__, +#elif defined(__BYTE_ORDER) // Common in <sys/param.h> or <endian.h> + MLIB_ENDIAN_LITTLE = __LITTLE_ENDIAN, + MLIB_ENDIAN_BIG = __BIG_ENDIAN, + MLIB_ENDIAN_NATIVE = __BYTE_ORDER, +#else +#error This compiler does not define an endianness macro. +#endif +}; + +enum { + MLIB_IS_LITTLE_ENDIAN = MLIB_ENDIAN_NATIVE == MLIB_ENDIAN_LITTLE, + MLIB_IS_BIG_ENDIAN = MLIB_ENDIAN_NATIVE == MLIB_ENDIAN_BIG, +}; + +#endif // MLIB_ENDIAN_H_INCLUDED diff --git a/src/third_party/libmongocrypt/dist/src/mlib/int128.h b/src/third_party/libmongocrypt/dist/src/mlib/int128.h index dfb02a02668..149411710fe 100644 --- a/src/third_party/libmongocrypt/dist/src/mlib/int128.h +++ b/src/third_party/libmongocrypt/dist/src/mlib/int128.h @@ -131,7 +131,7 @@ mlib_int128_add (mlib_int128 left, mlib_int128 right) { uint64_t losum = left.r.lo + right.r.lo; // Overflow check - int carry = (losum < left.r.lo || losum < right.r.lo); + unsigned carry = (losum < left.r.lo || losum < right.r.lo); uint64_t hisum = left.r.hi + right.r.hi + carry; return MLIB_INIT (mlib_int128) MLIB_INT128_FROM_PARTS (losum, hisum); } @@ -156,7 +156,7 @@ mlib_int128_negate (mlib_int128 v) static mlib_constexpr_fn mlib_int128 mlib_int128_sub (mlib_int128 from, mlib_int128 less) { - int borrow = from.r.lo < less.r.lo; + unsigned borrow = from.r.lo < less.r.lo; uint64_t low = from.r.lo - less.r.lo; uint64_t high = from.r.hi - less.r.hi; high -= borrow; @@ -338,7 +338,7 @@ _mlibKnuth431D (uint32_t *const u, u[i + j] = (u32) (t); k = t >> 32; } - u[j + n] += (int32_t) k; + u[j + n] += (u32) k; } // D7: @@ -516,9 +516,9 @@ mlib_int128_divmod (mlib_int128 numer, mlib_int128 denom) (uint32_t) (denom.r.hi >> (32 - d)), }; if (d != 0) { - u[2] |= numer.r.lo >> (64 - d); - u[4] |= numer.r.hi >> (64 - d); - v[2] |= denom.r.lo >> (64 - d); + u[2] |= (uint32_t) (numer.r.lo >> (64 - d)); + u[4] |= (uint32_t) (numer.r.hi >> (64 - d)); + v[2] |= (uint32_t) (denom.r.lo >> (64 - d)); }; uint32_t q[2] = {0}; @@ -561,7 +561,7 @@ mlib_int128_mod (mlib_int128 numer, mlib_int128 denom) * @brief Get the nth power of ten as a 128-bit number */ static mlib_constexpr_fn mlib_int128 -mlib_int128_pow10 (long nth) +mlib_int128_pow10 (uint8_t nth) { mlib_int128 r = MLIB_INT128 (1); while (nth-- > 0) { @@ -574,9 +574,9 @@ mlib_int128_pow10 (long nth) * @brief Get the Nth power of two as a 128-bit number */ static mlib_constexpr_fn mlib_int128 -mlib_int128_pow2 (long nth) +mlib_int128_pow2 (uint8_t nth) { - return mlib_int128_lshift (MLIB_INT128 (1), nth); + return mlib_int128_lshift (MLIB_INT128 (1), (int) nth); } /** @@ -612,7 +612,8 @@ mlib_int128_from_string (const char *s, const char **end) continue; } if (c >= 'a') { - c -= 'a' - 'A'; // Uppercase (if a letter, otherwise some other punct) + // Uppercase (if a letter, otherwise some other punct): + c = (char) (c - ('a' - 'A')); } int digit = c - '0'; if (c >= 'A') { @@ -655,7 +656,7 @@ typedef struct { static mlib_constexpr_fn mlib_int128_charbuf mlib_int128_format (mlib_int128 i) { - mlib_int128_charbuf into = {0}; + mlib_int128_charbuf into = {{0}}; char *out = into.str + (sizeof into) - 1; int len = 0; if (mlib_int128_eq (i, MLIB_INT128 (0))) { diff --git a/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp b/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp index e9e24788844..a1c1e4b5542 100644 --- a/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp +++ b/src/third_party/libmongocrypt/dist/src/mlib/int128.test.cpp @@ -1,4 +1,8 @@ #include "./int128.h" +#include "./endian.h" + +#include "./check.hpp" +#define CHECK MLIB_CHECK #include <iostream> #include <random> @@ -155,73 +159,6 @@ operator<< (std::ostream &out, const mlib_int128 &v) return out; } -struct check_info { - const char *filename; - int line; - const char *expr; -}; - -struct nil { -}; - -template <typename Left> struct bound_lhs { - check_info info; - Left value; - -#define DEFOP(Oper) \ - template <typename Rhs> nil operator Oper (Rhs rhs) const noexcept \ - { \ - if (value Oper rhs) { \ - return {}; \ - } \ - fprintf (stderr, \ - "%s:%d: CHECK( %s ) failed!\n", \ - info.filename, \ - info.line, \ - info.expr); \ - fprintf (stderr, "Expanded expression: "); \ - std::cerr << value << " " #Oper " " << rhs << '\n'; \ - std::exit (1); \ - return {}; \ - } - DEFOP (==) - DEFOP (!=) - DEFOP (<) - DEFOP (<=) - DEFOP (>) - DEFOP (>=) -#undef DEFOP -}; - -struct check_magic { - check_info info; - - template <typename Oper> - bound_lhs<Oper> - operator->*(Oper op) - { - return bound_lhs<Oper>{info, op}; - } -}; - -struct check_consume { - void - operator= (nil) - { - } - - void - operator= (bound_lhs<bool> const &l) - { - // Invoke the test for truthiness: - (void) (l == true); - } -}; - -#undef CHECK -#define CHECK(Cond) \ - check_consume{} = check_magic{check_info{__FILE__, __LINE__, #Cond}}->*Cond - #ifndef BROKEN_CONSTEXPR static_assert (mlib_int128 (MLIB_INT128_UMAX) == 340282366920938463463374607431768211455_i128, @@ -239,6 +176,46 @@ static_assert (mlib_int128_negate (9223372036854775809_i128) < "fail"); #endif +#ifdef __SIZEOF_INT128__ +// mlib_int128_to_native converts mlib_int128 to native __uint128_t. +// Endian-ness is accounted for. +static __uint128_t +mlib_int128_to_native (mlib_int128 in) +{ + __uint128_t out; + uint8_t *out_u8 = (uint8_t *) &out; + if (MLIB_IS_BIG_ENDIAN) { + // Copy hi, then lo. + memcpy (out_u8, &in.r.hi, sizeof (in.r.hi)); + memcpy (out_u8 + sizeof (in.r.hi), &in.r.lo, sizeof (in.r.lo)); + } else { + // Copy lo, then hi. + memcpy (out_u8, &in.r.lo, sizeof (in.r.lo)); + memcpy (out_u8 + sizeof (in.r.lo), &in.r.hi, sizeof (in.r.hi)); + } + return out; +} + +// native_to_mlib_int128 converts native __uint128_t to mlib_int128. +// Endian-ness is accounted for. +static mlib_int128 +native_to_mlib_int128 (__uint128_t in) +{ + mlib_int128 out; + uint8_t *in_u8 = (uint8_t *) ∈ + if (MLIB_IS_BIG_ENDIAN) { + // Copy hi, then lo. + memcpy (&out.r.hi, in_u8, sizeof (out.r.hi)); + memcpy (&out.r.lo, in_u8 + sizeof (out.r.hi), sizeof (out.r.lo)); + } else { + // Copy lo, then hi. + memcpy (&out.r.lo, in_u8, sizeof (out.r.lo)); + memcpy (&out.r.hi, in_u8 + sizeof (out.r.lo), sizeof (out.r.hi)); + } + return out; +} +#endif // __SIZEOF_INT128__ + static mlib_int128_divmod_result div_check (mlib_int128 num, mlib_int128 den) { @@ -246,15 +223,23 @@ div_check (mlib_int128 num, mlib_int128 den) mlib_int128_divmod_result res = mlib_int128_divmod (num, den); #ifdef __SIZEOF_INT128__ // When we have an existing i128 impl, test against that: - __uint128_t num1; - __uint128_t den1; - memcpy (&num1, &num.r, sizeof num); - memcpy (&den1, &den.r, sizeof den); + __uint128_t num1 = mlib_int128_to_native (num); + __uint128_t den1 = mlib_int128_to_native (den); __uint128_t q = num1 / den1; __uint128_t r = num1 % den1; mlib_int128_divmod_result expect; - memcpy (&expect.quotient.r, &q, sizeof q); - memcpy (&expect.remainder.r, &r, sizeof r); + expect.quotient = native_to_mlib_int128 (q); + expect.remainder = native_to_mlib_int128 (r); + if (!mlib_int128_eq (expect.quotient, res.quotient) || + !mlib_int128_eq (expect.remainder, res.remainder)) { + std::cout << "unexpected result in division" + << " num=" << mlib_int128_format (num).str + << " den=" << mlib_int128_format (den).str + << " expect.quotient=" + << mlib_int128_format (expect.quotient).str + << " expect.remainder=" + << mlib_int128_format (expect.remainder).str << std::endl; + } CHECK (expect.quotient == res.quotient); CHECK (expect.remainder == res.remainder); #endif diff --git a/src/third_party/libmongocrypt/dist/src/mlib/macros.h b/src/third_party/libmongocrypt/dist/src/mlib/macros.h index f586597b48a..14311622505 100644 --- a/src/third_party/libmongocrypt/dist/src/mlib/macros.h +++ b/src/third_party/libmongocrypt/dist/src/mlib/macros.h @@ -43,4 +43,11 @@ */
#define mlib_constexpr_fn _mlibConstexprFn
+#ifdef __GNUC__
+#define MLIB_ANNOTATE_PRINTF(FStringArgAt, VarArgsStartAt) \
+ __attribute__ ((format (__printf__, FStringArgAt, VarArgsStartAt)))
+#else
+#define MLIB_ANNOTATE_PRINTF(FStringArgAt, VarArgsStartAt) /* no-op */
+#endif
+
#endif // MLIB_MACROS_H_INCLUDED
diff --git a/src/third_party/libmongocrypt/dist/src/mlib/str.h b/src/third_party/libmongocrypt/dist/src/mlib/str.h index 3c36b61fe89..18f9ee9d339 100644 --- a/src/third_party/libmongocrypt/dist/src/mlib/str.h +++ b/src/third_party/libmongocrypt/dist/src/mlib/str.h @@ -975,6 +975,54 @@ mlib_strlen (const char *s) return r; } +/** + * @brief Copy characters into the destination, guaranteed null-terminated and + * bounds checked. + * + * @param dst Pointer to the beginning of the destination array. + * @param dst_bufsize The size of the destination array, in characters. MUST be + * greater than zero. + * @param src Pointer to the beginning of a null-terminated character array. + * @param src_bufsize The size of the array pointed-to by `src`. + * @return size_t The number `R` of characters written (NOT including the null + * terminator). R is guaranteed to be less than dst_bufsize, and + * less-than-or-equal-to src_bufsize. + * + * @note Characters beyond (dst + R) are unmodified. dst[R] is guaranteed to + * be a null terminator. + */ +static mlib_constexpr_fn size_t +mlib_strnmcopy (char *dst, + size_t dst_bufsize, + const char *src, + size_t src_bufsize) +{ + // No empty destination, since we *must* write a null terminator: + assert (dst_bufsize > 0); + // The maximum number of characters in the dest is one less than the buffer + // size, since we need room for the null terminator: + const size_t dstlen = dst_bufsize - 1u; + // The actual maximum number of characters we can copy is the less of the + // source length and the dest length: + const size_t minlen = dstlen < src_bufsize ? dstlen : src_bufsize; + // Track what we copy: + size_t ncopied = 0; + while (ncopied != minlen // Stop if we hit our character limit + && *src != 0 // Or if we hit the null terminator in the source + ) { + // Copy: + *dst = *src; + // Advance: + ++dst; + ++src; + ++ncopied; + } + // "dst" now points past the final character we copied (if any), and is still + // in-bounds. This will be the null terminator. + *dst = 0; + return ncopied; +} + MLIB_C_LINKAGE_END #endif // MONGOCRYPT_STR_PRIVATE_H diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer-private.h index fa25876d71d..2d77fb9125a 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer-private.h +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-buffer-private.h @@ -22,6 +22,7 @@ #include "mongocrypt-compat.h" #define UUID_LEN 16 +#define PRF_LEN 32 struct _mongocrypt_binary_t; diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c index 8c77cd75176..63612d43bd5 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-ctx-encrypt.c @@ -1075,15 +1075,25 @@ _try_run_csfle_marking (mongocrypt_ctx_t *ctx) mongocrypt_binary_t *marked = mongocrypt_binary_new_from_data (marked_bson, marked_bson_len); if (!_mongo_feed_markings (ctx, marked)) { - _mongocrypt_ctx_fail_w_msg ( - ctx, "Consuming the generated csfle markings failed"); + // Wrap error with additional information. + _mongocrypt_set_error ( + ctx->status, + MONGOCRYPT_STATUS_ERROR_CLIENT, + MONGOCRYPT_GENERIC_ERROR_CODE, + "Consuming the generated csfle markings failed: %s", + mongocrypt_status_message (ctx->status, NULL /* len */)); goto fail_feed_markings; } okay = _mongo_done_markings (ctx); if (!okay) { - _mongocrypt_ctx_fail_w_msg ( - ctx, "Finalizing the generated csfle markings failed"); + // Wrap error with additional information. + _mongocrypt_set_error ( + ctx->status, + MONGOCRYPT_STATUS_ERROR_CLIENT, + MONGOCRYPT_GENERIC_ERROR_CODE, + "Finalizing the generated csfle markings failed: %s", + mongocrypt_status_message (ctx->status, NULL /* len */)); } fail_feed_markings: diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c index 83433aeb5b9..fbb6b20ac0e 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-key-broker.c @@ -151,7 +151,7 @@ _key_broker_fail_w_msg (_mongocrypt_key_broker_t *kb, const char *msg) kb->state = KB_ERROR; status = kb->status; - CLIENT_ERR (msg); + CLIENT_ERR ("%s", msg); return false; } diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c index 3062d046d9c..a01c97414ab 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-marking.c @@ -701,6 +701,22 @@ get_edges (mc_FLE2RangeInsertSpec_t *insertSpec, return mc_getEdgesDouble (args, status); } + else if (value_type == BSON_TYPE_DECIMAL128) { + const mc_dec128 value = mc_dec128_from_bson_iter (&insertSpec->v); + mc_getEdgesDecimal128_args_t args = { + .value = value, + .sparsity = sparsity, + }; + if (insertSpec->precision.set) { + const mc_dec128 min = mc_dec128_from_bson_iter (&insertSpec->min); + const mc_dec128 max = mc_dec128_from_bson_iter (&insertSpec->max); + args.min = OPT_MC_DEC128 (min); + args.max = OPT_MC_DEC128 (max); + args.precision = insertSpec->precision; + } + return mc_getEdgesDecimal128 (args, status); + } + CLIENT_ERR ("unsupported BSON type: %s for range", mc_bson_type_to_string (value_type)); @@ -1160,10 +1176,30 @@ mc_get_mincover_from_FLE2RangeFindSpec (mc_FLE2RangeFindSpec_t *findSpec, } return mc_getMincoverDouble (args, status); } - case BSON_TYPE_DECIMAL128: - CLIENT_ERR ("FLE2 find not yet implemented for type: %s", - mc_bson_type_to_string (bsonType)); - return NULL; + case BSON_TYPE_DECIMAL128: { + BSON_ASSERT (bson_iter_type (&lowerBound) == BSON_TYPE_DECIMAL128); + BSON_ASSERT (bson_iter_type (&upperBound) == BSON_TYPE_DECIMAL128); + BSON_ASSERT (bson_iter_type (&findSpec->edgesInfo.value.indexMin) == + BSON_TYPE_DECIMAL128); + BSON_ASSERT (bson_iter_type (&findSpec->edgesInfo.value.indexMax) == + BSON_TYPE_DECIMAL128); + + mc_getMincoverDecimal128_args_t args = { + .lowerBound = mc_dec128_from_bson_iter (&lowerBound), + .includeLowerBound = includeLowerBound, + .upperBound = mc_dec128_from_bson_iter (&upperBound), + .includeUpperBound = includeUpperBound, + .sparsity = sparsity, + }; + if (findSpec->edgesInfo.value.precision.set) { + args.min = OPT_MC_DEC128 ( + mc_dec128_from_bson_iter (&findSpec->edgesInfo.value.indexMin)); + args.max = OPT_MC_DEC128 ( + mc_dec128_from_bson_iter (&findSpec->edgesInfo.value.indexMax)); + args.precision = findSpec->edgesInfo.value.precision; + } + return mc_getMincoverDecimal128 (args, status); + } case BSON_TYPE_EOD: case BSON_TYPE_UTF8: diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h b/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h index 2e8a24320b9..458f7676279 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt-private.h @@ -33,6 +33,8 @@ #include "mongo_crypt-v1.h" +#include <mlib/macros.h> + #define MONGOCRYPT_GENERIC_ERROR_CODE 1 @@ -64,7 +66,16 @@ tmp_json (const bson_t *bson); const char * tmp_buf (const _mongocrypt_buffer_t *buf); - +// _mongocrypt_set_error sets an error status. +// `status->message` is set after evaluating `format` and variadic args. +// It is safe to uses a `status->message` as an argument to wrap an error. For example: +// _mongocrypt_set_error ( +// status, +// MONGOCRYPT_STATUS_ERROR_CLIENT, +// MONGOCRYPT_GENERIC_ERROR_CODE, +// "Error occurred. Original error: %s", +// mongocrypt_status_message (status, NULL /* len */)); +MLIB_ANNOTATE_PRINTF (4, 5) void _mongocrypt_set_error (mongocrypt_status_t *status, mongocrypt_status_type_t type, diff --git a/src/third_party/libmongocrypt/dist/src/mongocrypt.c b/src/third_party/libmongocrypt/dist/src/mongocrypt.c index 5e2dbaad278..5b4f7888803 100644 --- a/src/third_party/libmongocrypt/dist/src/mongocrypt.c +++ b/src/third_party/libmongocrypt/dist/src/mongocrypt.c @@ -310,7 +310,7 @@ mongocrypt_setopt_schema_map (mongocrypt_t *crypt, } if (!bson_validate_with_error (&tmp, BSON_VALIDATE_NONE, &bson_err)) { - CLIENT_ERR (bson_err.message); + CLIENT_ERR ("%s", bson_err.message); return false; } @@ -353,7 +353,7 @@ mongocrypt_setopt_encrypted_field_config_map (mongocrypt_t *crypt, } if (!bson_validate_with_error (&as_bson, BSON_VALIDATE_NONE, &bson_err)) { - CLIENT_ERR (bson_err.message); + CLIENT_ERR ("%s", bson_err.message); return false; } diff --git a/src/third_party/libmongocrypt/import.sh b/src/third_party/libmongocrypt/import.sh index a96884fe6f7..df923ef0203 100755 --- a/src/third_party/libmongocrypt/import.sh +++ b/src/third_party/libmongocrypt/import.sh @@ -18,7 +18,7 @@ if grep -q Microsoft /proc/version; then fi NAME=libmongocrypt -REVISION=2c564d555b168e9bdb60ade186b6f3aa759e010b +REVISION=d01892a44db2504253274fc51c1c51c76f467881 if grep -q Microsoft /proc/version; then SRC_ROOT=$(wslpath -u $(powershell.exe -Command "Get-ChildItem Env:TEMP | Get-Content | Write-Host")) |