Skip to content

[CDRIVER-5996] Prevent multi-evaluation of arithmetic macro-function operands #1999

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

Open
wants to merge 1 commit 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
5 changes: 1 addition & 4 deletions src/common/src/mlib/ckdint.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
*
* This file implements the C23 checked-integer-arithmetic functions as macros.
*
* The implementation is nearly perfect: The macros necessarily evaluate the
* operand expressions more than once, so callers should be aware of this caveat.
*
* The function-like macros are defined:
*
* - `mlib_add(Dst, L, R)` / `mlib_add(Dst, A)`
Expand Down Expand Up @@ -159,7 +156,7 @@ mlib_extern_c_begin ();
* @param T A type specifier for a target integral type for the cast.
* @param Operand The integral value to be converted.
*
* If the cast would result in the operand value chaning, the program will be
* If the cast would result in the operand value changing, the program will be
* terminated with a diagnostic.
*/
#define mlib_assert_narrow(T, Operand) \
Expand Down
3 changes: 0 additions & 3 deletions src/common/src/mlib/cmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ enum mlib_cmp_result {
/**
* @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`
Expand Down
9 changes: 7 additions & 2 deletions src/common/src/mlib/intutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,21 @@ typedef struct mlib_upsized_integer {
* an i64 losslessly.
*
* If the integer to upcast is the same size as `intmax_t`, we need to decide whether to store
* it as unsigned. The expression `(0 & Value) - 1 < 0` will be `true` iff the operand is signed,
* it as unsigned. The expression `(_mlibGetZero(Values)) - 1 < 0` will be `true` iff the operand is signed,
* otherwise false. If the operand is signed, we can safely cast to `intmax_t` (it probably already
* is of that type), otherwise, we can to `uintmax_t` and the returned `mlib_upsized_integer` will
* indicate that the stored value is unsigned.
*/
#define mlib_upsize_integer(Value) \
/* NOLINTNEXTLINE(bugprone-sizeof-expression) */ \
((sizeof ((Value)) < sizeof (intmax_t) || ((0 & (Value)) - 1) < 0) \
((sizeof ((Value)) < sizeof (intmax_t) || (_mlibGetZero(Value) - 1) < _mlibGetZero(Value)) \
? mlib_init(mlib_upsized_integer) {{(intmax_t) (Value)}, true} \
: mlib_init(mlib_upsized_integer) {{(intmax_t) (uintmax_t) (Value)}})
// Yield a zero value of similar-ish type to the given expression. The ternary
// forces an integer promotion of literal zero match the type of `V`, while leaving
// `V` unevaluated. Note that this will also promote `V` to be at least `(unsigned) int`,
// so the zero value is only "similar" to `V`, and may be of a larger type
#define _mlibGetZero(V) (1 ? 0 : (V))
// clang-format on

#endif // MLIB_INTUTIL_H_INCLUDED
8 changes: 8 additions & 0 deletions src/common/tests/test-mlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,14 @@ _test_cmp (void)
mlib_diagnostic_pop ();
// mlib_cmp produces the correct answer:
ASSERT (mlib_cmp (-27, <, 20u));

{
// Check that we do not double-evaluate the operand expression.
intmax_t a = 4;
mlib_check (mlib_cmp (++a, ==, 5));
// We only increment once:
mlib_check (a, eq, 5);
}
}

static void
Expand Down