Work around nlohmann changes
https://github.com/nlohmann/json/issues/4475 recently changed behavior
that we rely on in a lot of places, and unit tests appear to have caught
the failure. Functionally, this changes the readJson* class of values
to attempt to read as the type requested first, then attempt to read as
the opposite int/uint type requested, with a range check.
In addition, the range check functions now need updated to handle
comparisons between non-similar value types. Luckily c++20 added
cmp_less/greater type functions that we can make use of.
Tested: unit tests pass.
Change-Id: If114bd55225a3a9948e80a407b19b69f50d048b6
Signed-off-by: Ed Tanous <etanous@nvidia.com>
diff --git a/redfish-core/include/utils/json_utils.hpp b/redfish-core/include/utils/json_utils.hpp
index 60994fe..f621a45 100644
--- a/redfish-core/include/utils/json_utils.hpp
+++ b/redfish-core/include/utils/json_utils.hpp
@@ -95,8 +95,7 @@
};
template <typename ToType, typename FromType>
-bool checkRange(const FromType& from [[maybe_unused]],
- std::string_view key [[maybe_unused]])
+bool checkRange(const FromType& from, std::string_view key)
{
if constexpr (std::is_floating_point_v<ToType>)
{
@@ -105,21 +104,19 @@
BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
return false;
}
+ // Assume for the moment that all floats can represent the full range
+ // of any int/uint in a cast. This is close enough to true for the
+ // precision of this json parser.
}
- if constexpr (std::numeric_limits<ToType>::max() <
- std::numeric_limits<FromType>::max())
+ else
{
- if (from > std::numeric_limits<ToType>::max())
+ if (std::cmp_greater(from, std::numeric_limits<ToType>::max()))
{
BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
std::numeric_limits<FromType>::max());
return false;
}
- }
- if constexpr (std::numeric_limits<ToType>::lowest() >
- std::numeric_limits<FromType>::lowest())
- {
- if (from < std::numeric_limits<ToType>::lowest())
+ if (std::cmp_less(from, std::numeric_limits<ToType>::lowest()))
{
BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
std::numeric_limits<FromType>::lowest());
@@ -189,13 +186,26 @@
int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
if (jsonPtr == nullptr)
{
- return UnpackErrorCode::invalidType;
+ // Value wasn't int, check uint
+ uint64_t* uJsonPtr = jsonValue.get_ptr<uint64_t*>();
+ if (uJsonPtr == nullptr)
+ {
+ return UnpackErrorCode::invalidType;
+ }
+ if (!checkRange<Type>(*uJsonPtr, key))
+ {
+ return UnpackErrorCode::outOfRange;
+ }
+ value = static_cast<Type>(*uJsonPtr);
}
- if (!checkRange<Type>(*jsonPtr, key))
+ else
{
- return UnpackErrorCode::outOfRange;
+ if (!checkRange<Type>(*jsonPtr, key))
+ {
+ return UnpackErrorCode::outOfRange;
+ }
+ value = static_cast<Type>(*jsonPtr);
}
- value = static_cast<Type>(*jsonPtr);
}
else if constexpr ((std::is_unsigned_v<Type>) &&
@@ -204,13 +214,25 @@
uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
if (jsonPtr == nullptr)
{
- return UnpackErrorCode::invalidType;
+ int64_t* ijsonPtr = jsonValue.get_ptr<int64_t*>();
+ if (ijsonPtr == nullptr)
+ {
+ return UnpackErrorCode::invalidType;
+ }
+ if (!checkRange<Type>(*ijsonPtr, key))
+ {
+ return UnpackErrorCode::outOfRange;
+ }
+ value = static_cast<Type>(*ijsonPtr);
}
- if (!checkRange<Type>(*jsonPtr, key))
+ else
{
- return UnpackErrorCode::outOfRange;
+ if (!checkRange<Type>(*jsonPtr, key))
+ {
+ return UnpackErrorCode::outOfRange;
+ }
+ value = static_cast<Type>(*jsonPtr);
}
- value = static_cast<Type>(*jsonPtr);
}
else if constexpr (std::is_same_v<nlohmann::json, Type>)
diff --git a/redfish-core/src/filter_expr_executor.cpp b/redfish-core/src/filter_expr_executor.cpp
index 838fa64..e2513e1 100644
--- a/redfish-core/src/filter_expr_executor.cpp
+++ b/redfish-core/src/filter_expr_executor.cpp
@@ -152,6 +152,17 @@
{
return {*iValue};
}
+ const uint64_t* uValue = entry.get_ptr<const uint64_t*>();
+ if (uValue != nullptr)
+ {
+ // For now all values are coerced to signed
+ if (*uValue > std::numeric_limits<int64_t>::max())
+ {
+ BMCWEB_LOG_WARNING("Parsed uint is outside limits");
+ return {};
+ }
+ return {static_cast<int64_t>(*uValue)};
+ }
const std::string* strValue = entry.get_ptr<const std::string*>();
if (strValue != nullptr)
{