From 5b00521ecfdcfa5ba681955bae365a2a1b1f9e13 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Mon, 27 Jan 2025 16:27:35 +0100 Subject: [PATCH] qAbs: check that we're not overflowing qAbs shouldn't produce a result if that is not representable by the return type. Not only that, but attempting to negate an integral type may trigger UB. For instance, if one calls qAbs(INT_MIN), the code will attempt negating INT_MIN, causing UB. Add an assert that checks that the return is representable. Formally, things are slightly more tricky, because the unary operator- applies integral promotions before negating the value. This means that something like qAbs(short(SHRT_MIN)) is actually well-defined behavior, although extremely surprising. Inside qAbs: * the unary negation will promote the argument to `int`; * the negation is well defined (will produce `SHRT_MAX+1`, in range); * the result of the ternary operator inside `qAbs` is, indeed, `int`; * the return type is `short`, so there's an implicit conversion from the returned expression to the returned value. This conversion is implicit and well-defined even in case of overflow. The fun bit is that it will yield SHRT_MIN (!), a negative number. So, rather than checking that the applying unary minus to the _promoted_ type doesn't yield UB, I'll check that we're not dealing with the minimal value of the input type. Change-Id: I26fbbf10d3a3ac333876864cff74422cb4cdafb9 Reviewed-by: Thiago Macieira --- src/corelib/global/qnumeric.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index 2e13da4b850..bb2859031a6 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -8,6 +8,7 @@ #pragma qt_class(QtNumeric) #endif +#include #include #include #include @@ -334,7 +335,12 @@ template bool qMulOverflow(T v1, T *r) } template -constexpr inline T qAbs(const T &t) { return t >= 0 ? t : -t; } +constexpr inline T qAbs(const T &t) +{ + if constexpr (std::is_integral_v && std::is_signed_v) + Q_ASSERT(t != std::numeric_limits::min()); + return t >= 0 ? t : -t; +} namespace QtPrivate { template