Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDRIVER-5859 Integer comparison macro #1845

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/common/src/common-bson-dsl-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

#include <bson/bson.h>
#include <common-cmp-private.h>
#include <mlib/cmp.h>

enum {
/// Toggle this value to enable/disable debug output for all bsonDSL
Expand Down Expand Up @@ -119,7 +119,7 @@ BSON_IF_GNU_LIKE (_Pragma ("GCC diagnostic ignored \"-Wshadow\""))
_bsonDSL_begin ("\"%s\" => [%s]", String, _bsonDSL_strElide (30, Element)); \
const char *_bbString = (String); \
const uint64_t length = (Len); \
if (mcommon_in_range_unsigned (int, length)) { \
if (mlib_in_range (int, length)) { \
_bbCtx.key = _bbString; \
_bbCtx.key_len = (int) length; \
_bsonValueOperation (Element); \
Expand Down
17 changes: 8 additions & 9 deletions src/common/src/common-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
#include <common-string-private.h>
#include <common-utf8-private.h>
#include <common-json-private.h>
#include <common-cmp-private.h>


typedef struct {
Expand All @@ -40,7 +39,7 @@ mcommon_json_append_visit_utf8 (
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_utf8_len)) {
if (!mlib_in_range (uint32_t, v_utf8_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down Expand Up @@ -124,7 +123,7 @@ mcommon_json_append_visit_binary (const bson_iter_t *iter,
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_binary_len)) {
if (!mlib_in_range (uint32_t, v_binary_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down Expand Up @@ -158,7 +157,7 @@ mcommon_json_append_visit_regex (
size_t v_options_len = strlen (v_options);
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_regex_len)) {
if (!mlib_in_range (uint32_t, v_regex_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down Expand Up @@ -187,7 +186,7 @@ mcommon_json_append_visit_dbpointer (const bson_iter_t *iter,
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_collection_len)) {
if (!mlib_in_range (uint32_t, v_collection_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down Expand Up @@ -233,7 +232,7 @@ mcommon_json_append_visit_before (const bson_iter_t *iter, const char *key, void

if (state->has_keys) {
size_t key_len = strlen (key);
if (!mcommon_in_range_unsigned (uint32_t, key_len)) {
if (!mlib_in_range (uint32_t, key_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down Expand Up @@ -269,7 +268,7 @@ mcommon_json_append_visit_code (
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_code_len)) {
if (!mlib_in_range (uint32_t, v_code_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand All @@ -283,7 +282,7 @@ mcommon_json_append_visit_symbol (
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_symbol_len)) {
if (!mlib_in_range (uint32_t, v_symbol_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand All @@ -297,7 +296,7 @@ mcommon_json_append_visit_codewscope (
mcommon_json_append_visit_t *state = data;
BSON_UNUSED (iter);
BSON_UNUSED (key);
if (!mcommon_in_range_unsigned (uint32_t, v_code_len)) {
if (!mlib_in_range (uint32_t, v_code_len)) {
mcommon_string_append_overflow (state->append);
return true;
}
Expand Down
8 changes: 4 additions & 4 deletions src/common/src/common-string-private.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
#define MONGO_C_DRIVER_COMMON_STRING_PRIVATE_H

#include <bson/bson.h>
#include <mlib/cmp.h>
#include <string.h>
#include <common-cmp-private.h>


/* Until the deprecated bson_string_t is removed, this must have the same members in the same order, so we can safely
Expand Down Expand Up @@ -111,7 +111,7 @@ mcommon_string_new (const char *str)
{
BSON_ASSERT_PARAM (str);
size_t length = strlen (str);
BSON_ASSERT (mcommon_in_range_unsigned (uint32_t, length) && (uint32_t) length < UINT32_MAX);
BSON_ASSERT (mlib_in_range (uint32_t, length) && (uint32_t) length < UINT32_MAX);
return mcommon_string_new_with_capacity (str, (uint32_t) length, 0);
}

Expand Down Expand Up @@ -181,7 +181,7 @@ mcommon_string_starts_with_str (const mcommon_string_t *string, const char *subs
size_t substring_len = strlen (substring);
uint32_t string_len = string->len;

if (mcommon_in_range_unsigned (uint32_t, substring_len) && (uint32_t) substring_len <= string_len) {
if (mlib_in_range (uint32_t, substring_len) && (uint32_t) substring_len <= string_len) {
return 0 == memcmp (string->str, substring, substring_len);
} else {
return false;
Expand All @@ -202,7 +202,7 @@ mcommon_string_ends_with_str (const mcommon_string_t *string, const char *substr
size_t substring_len = strlen (substring);
uint32_t string_len = string->len;

if (mcommon_in_range_unsigned (uint32_t, substring_len) && (uint32_t) substring_len <= string_len) {
if (mlib_in_range (uint32_t, substring_len) && (uint32_t) substring_len <= string_len) {
uint32_t offset = string_len - (uint32_t) substring_len;
return 0 == memcmp (string->str + offset, substring, substring_len);
} else {
Expand Down
18 changes: 8 additions & 10 deletions src/common/src/common-string.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <common-bits-private.h>
#include <common-utf8-private.h>
#include <common-b64-private.h>
#include <common-cmp-private.h>
#include <mlib/cmp.h>


mcommon_string_t *
Expand Down Expand Up @@ -202,7 +202,7 @@ mcommon_string_append_base64_encode (mcommon_string_append_t *append, const uint
mcommon_string_grow_to_capacity (string, old_len + encoded_target_len);
BSON_ASSERT (encoded_target_len ==
mcommon_b64_ntop (bytes, (size_t) len, string->str + old_len, encoded_target_len + 1));
BSON_ASSERT (mcommon_in_range_unsigned (uint32_t, encoded_target_len));
BSON_ASSERT (mlib_in_range (uint32_t, encoded_target_len));
string->len = old_len + (uint32_t) encoded_target_len;
return true;
} else if (max_append_len == 0) {
Expand Down Expand Up @@ -262,15 +262,15 @@ mcommon_string_append_oid_as_hex (mcommon_string_append_t *append, const bson_oi

bool
mcommon_string_append_selected_chars (mcommon_string_append_t *append,
const char *template,
const char *tmplt,
const char *selector,
size_t selector_len)
{
BSON_ASSERT_PARAM (append);
BSON_ASSERT_PARAM (template);
BSON_ASSERT_PARAM (tmplt);
BSON_ASSERT_PARAM (selector);

for (uint8_t template_char; (template_char = (uint8_t) * template); template ++) {
for (uint8_t template_char; (template_char = (uint8_t) *tmplt); tmplt++) {
BSON_ASSERT (template_char <= 0x7f);
if (memchr (selector, template_char, selector_len) && !mcommon_string_append_unichar (append, template_char)) {
return false;
Expand Down Expand Up @@ -331,7 +331,7 @@ mcommon_string_append_vprintf (mcommon_string_append_t *append, const char *form
int format_result = bson_vsnprintf (format_buffer, format_buffer_alloc, format, args_copy);
va_end (args_copy);

if (format_result > -1 && mcommon_in_range_signed (uint32_t, format_result) &&
if (format_result > -1 && mlib_in_range (uint32_t, format_result) &&
(uint32_t) format_result <= actual_format_buffer_capacity) {
// Successful result, no truncation.
format_buffer[format_result] = '\0';
Expand All @@ -344,8 +344,7 @@ mcommon_string_append_vprintf (mcommon_string_append_t *append, const char *form
if (actual_format_buffer_capacity == max_append_len) {
// No more space to grow into, this must be the final result.

if (format_result > -1 && mcommon_in_range_signed (uint32_t, format_result) &&
(uint32_t) format_result < UINT32_MAX) {
if (format_result > -1 && mlib_in_range (uint32_t, format_result) && (uint32_t) format_result < UINT32_MAX) {
// We have truncated output from vsnprintf. Clean it up by removing
// any partial UTF-8 sequences that might be left on the end.
uint32_t truncated_append_len = mcommon_utf8_truncate_len (
Expand All @@ -362,8 +361,7 @@ mcommon_string_append_vprintf (mcommon_string_append_t *append, const char *form
}

// Choose a larger format_buffer_len and try again. Length will be clamped to max_append_len above.
if (format_result > -1 && mcommon_in_range_signed (uint32_t, format_result) &&
(uint32_t) format_result < UINT32_MAX) {
if (format_result > -1 && mlib_in_range (uint32_t, format_result) && (uint32_t) format_result < UINT32_MAX) {
min_format_buffer_capacity = (uint32_t) format_result + 1u;
} else if (min_format_buffer_capacity < UINT32_MAX / 2) {
min_format_buffer_capacity *= 2;
Expand Down
118 changes: 118 additions & 0 deletions src/common/src/mlib/cmp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* @file mlib/cmp.h
* @brief Safe integer comparison and range checking
* @date 2024-08-29
*
* This file provides safe and intuitive integer comparison macros that behave
* appropriately, regardless of the sign or precision of the integer operands.
*
* @copyright Copyright (c) 2025
*
* 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.
*/
#pragma once

#include <mlib/config.h>
#include <mlib/intutil.h>

#include <stdbool.h>
#include <stdint.h>

/**
* @brief Result type of comparing two integral values with `mlib_cmp`
*
* The enumerator values are chosen such that they can be compared with zero
*/
enum mlib_cmp_result {
// The two values are equivalent
mlib_equal = 0,
// The left-hand operand is less than the right-hand
mlib_less = -1,
// The left-hand operand is greater than the right-hand
mlib_greater = 1,
};

/**
* @brief Compare two integral values safely.
*
* NOTE: This macro may evaluate the operand expressions more than once! Do not
* use expressions that are expensive or have side effects!
*
* This function can be called with two arguments or with three:
*
* - `mlib_cmp(a, b)` Returns a value of type `mlib_cmp_result`
* - `mlib_cmp(a, Op, b)` where `Op` is a relational operator. Evaluates to a boolean value.
*/
#define mlib_cmp(...) MLIB_ARGC_PICK (_mlib_cmp, __VA_ARGS__)
// Compare two integers, and return the result of that comparison:
#define _mlib_cmp_argc_2(L, R) mlib_cmp (mlib_upsize_integer ((L)), mlib_upsize_integer ((R)))
// Compare two integers, but with an infix operator:
#define _mlib_cmp_argc_3(L, Op, R) (mlib_cmp (mlib_upsize_integer ((L)), mlib_upsize_integer ((R))) Op 0)
// Impl for mlib_cmp
static inline enum mlib_cmp_result (mlib_cmp) (struct mlib_upsized_integer x,
struct mlib_upsized_integer y) mlib_noexcept
{
if (x.is_signed) {
if (y.is_signed) {
// Both signed
if (x.i.s < y.i.s) {
return mlib_less;
} else if (x.i.s > y.i.s) {
return mlib_greater;
}
} else {
// X signed, Y unsigned
if (x.i.s < 0 || y.i.u > (uintmax_t) x.i.s) {
return mlib_less;
} else if (y.i.u < (uintmax_t) x.i.s) {
return mlib_greater;
}
}
} else {
if (!y.is_signed) {
// Both unsigned
if (x.i.u < y.i.u) {
return mlib_less;
} else if (x.i.u > y.i.u) {
return mlib_greater;
}
} else {
// X unsigned, Y signed
if (y.i.s < 0 || x.i.u > (uintmax_t) y.i.s) {
return mlib_greater;
} else if (x.i.u < (uintmax_t) y.i.s) {
return mlib_less;
}
}
}
return mlib_equal;
}

/**
* @brief Test whether the given operand is within the range of some other integral type
*
* @param T A type specifier of the target integral type
* @param Operand the expression that is being inspected.
*
* @note This macro may evaluate the operand more than once
*/
#define mlib_in_range(T, Operand) \
mlib_in_range ((intmax_t) mlib_minof (T), (uintmax_t) mlib_maxof (T), mlib_upsize_integer (Operand))
static inline bool (mlib_in_range) (intmax_t min_, uintmax_t max_, struct mlib_upsized_integer val) mlib_noexcept
{
if (val.is_signed) {
return mlib_cmp (val.i.s, >=, min_) && mlib_cmp (val.i.s, <=, max_);
} else {
return mlib_cmp (val.i.u, >=, min_) && mlib_cmp (val.i.u, <=, max_);
}
}
Loading