1
0
Fork 0
mirror of https://github.com/Neargye/magic_enum.git synced 2026-01-10 23:44:29 +00:00

Fix underflow in out of range check (#88)

* Special case bool in cmp_less to avoid msvc's warnings

* Fix underflow in out of range check

The previous fix addressed bool but it seems that was not the heart
of the issue.

The problem is that (lhs - 1) can underflow.

Another example of the check trigger when it shouldn't is:
uint8_t with min_value set to 0 and a value of uint8_t::max
lhs-1 would underflow and find max (see added test).

The issue happens when lhs == rhs. To avoid that situation: flip the if-else!
`cmp_less(rhs, lhs)`means that rhs is strictly less than lhs, so
the check will not trigger when lhs == rhs.

Also, cleanup the bool special-case, as it is no longer necessary.

Note: for the bug to manifest, it is important that the min
of the enums is customized to be 0, so corresponding enum_range
specializations need to be present in the regression tests.
This commit is contained in:
Alexander Karatarakis 2021-06-25 05:00:54 -07:00 committed by GitHub
parent b927151677
commit 41c916432b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 8 deletions

View file

@ -280,6 +280,10 @@ constexpr bool cmp_less(L lhs, R rhs) noexcept {
if constexpr (std::is_signed_v<L> == std::is_signed_v<R>) {
// If same signedness (both signed or both unsigned).
return lhs < rhs;
} else if constexpr (std::is_same_v<L, bool>) { // bool special case due to msvc's C4804, C4018
return static_cast<R>(lhs) < rhs;
} else if constexpr (std::is_same_v<R, bool>) { // bool special case due to msvc's C4804, C4018
return lhs < static_cast<L>(rhs);
} else if constexpr (std::is_signed_v<R>) {
// If 'right' is negative, then result is 'false', otherwise cast & compare.
return rhs > 0 && lhs < static_cast<std::make_unsigned_t<R>>(rhs);
@ -383,13 +387,11 @@ constexpr int reflected_min() noexcept {
static_assert(lhs > (std::numeric_limits<std::int16_t>::min)(), "magic_enum::enum_range requires min must be greater than INT16_MIN.");
constexpr auto rhs = (std::numeric_limits<U>::min)();
if constexpr (std::is_same_v<bool, U>) {
return static_cast<int>(rhs);
} else if constexpr (cmp_less(lhs, rhs)) {
return rhs;
} else {
if constexpr (cmp_less(rhs, lhs)) {
static_assert(!is_valid<E, value<E, lhs - 1, IsFlags>(0)>(), "magic_enum::enum_range detects enum value smaller than min range size.");
return lhs;
} else {
return rhs;
}
}
}
@ -405,9 +407,7 @@ constexpr int reflected_max() noexcept {
static_assert(lhs < (std::numeric_limits<std::int16_t>::max)(), "magic_enum::enum_range requires max must be less than INT16_MAX.");
constexpr auto rhs = (std::numeric_limits<U>::max)();
if constexpr (std::is_same_v<bool, U>) {
return static_cast<int>(rhs);
} else if constexpr (cmp_less(lhs, rhs)) {
if constexpr (cmp_less(lhs, rhs)) {
static_assert(!is_valid<E, value<E, lhs + 1, IsFlags>(0)>(), "magic_enum::enum_range detects enum value larger than max range size.");
return lhs;
} else {