[Core] Reject negative values when converting to unsigned#33482
[Core] Reject negative values when converting to unsigned#33482om4rrr wants to merge 9 commits intoopenvinotoolkit:masterfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR enhances type safety in ov::Any conversions by adding validation for integral type conversions that could result in data loss or unexpected wraparound. The changes prevent silent errors like converting -1 to 4294967295 when casting from signed to unsigned types.
- Adds range checking for signed-to-unsigned and unsigned-to-signed integral conversions
- Rejects negative signed values when converting to unsigned types
- Validates that values fit within target type's numeric limits before conversion
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (std::is_signed<T>::value) { | ||
| if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) { | ||
| OPENVINO_THROW("Bad cast (out of range) from ", value, " to: ", typeid(T).name()); | ||
| } | ||
| } else { | ||
| if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) { | ||
| OPENVINO_THROW("Bad cast (out of range) from ", value, " to: ", typeid(T).name()); | ||
| } |
There was a problem hiding this comment.
The if-else branches for signed vs unsigned target types perform identical checks. Both branches check if the value exceeds the maximum limit of the target type. These branches can be consolidated into a single check that applies to both signed and unsigned target types, reducing code duplication.
| if (std::is_signed<T>::value) { | |
| if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) { | |
| OPENVINO_THROW("Bad cast (out of range) from ", value, " to: ", typeid(T).name()); | |
| } | |
| } else { | |
| if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) { | |
| OPENVINO_THROW("Bad cast (out of range) from ", value, " to: ", typeid(T).name()); | |
| } | |
| if (value > static_cast<unsigned long long>(std::numeric_limits<T>::max())) { | |
| OPENVINO_THROW("Bad cast (out of range) from ", value, " to: ", typeid(T).name()); |
|
Hi @praasz |
|
please resolve conflict before we go any further |
5e2523d to
6cd2054
Compare
|
Hi @mlukasze |
c131491 to
5e9b295
Compare
|
I don't have any fundamental objections regarding rejecting negative values for all properties. |
|
Hi @olpipi could you please review it? |
I agree with @olpipi that this change is valid. |
- Use std::decay_t<> instead of typename std::decay<>::type - Add overflow check only when source type max > target type max
- Use OV_EXPECT_THROW instead of EXPECT_THROW - Use std::ignore= instead of (void) C-style cast - Keep Core tests for set_property validation
- Cover property types: uint32_t, uint64_t, int32_t - Validate input types: int, long, int64_t, short, unsigned types, string - Test corner cases: 0, -1, INT_MIN, INT64_MIN, max values, overflow - Cover num_requests, auto_batch_timeout, dynamic_quantization_group_size, key_cache_group_size, value_cache_group_size, inference_num_threads, compilation_num_threads
- Test that string "-1" behaves same as int -1 for uint32_t properties - Verify num_requests and auto_batch_timeout reject negative strings - Verify positive strings are accepted correctly
| if constexpr (sizeof(VType) > sizeof(UType) || | ||
| (sizeof(VType) == sizeof(UType) && std::is_signed_v<VType>)) { | ||
| if (static_cast<unsigned long long>(value) > | ||
| static_cast<unsigned long long>(std::numeric_limits<UType>::max())) { | ||
| OPENVINO_THROW("Value ", value, " exceeds maximum for target unsigned type"); | ||
| } | ||
| } |
There was a problem hiding this comment.
| if constexpr (sizeof(VType) > sizeof(UType) || | |
| (sizeof(VType) == sizeof(UType) && std::is_signed_v<VType>)) { | |
| if (static_cast<unsigned long long>(value) > | |
| static_cast<unsigned long long>(std::numeric_limits<UType>::max())) { | |
| OPENVINO_THROW("Value ", value, " exceeds maximum for target unsigned type"); | |
| } | |
| } | |
| if constexpr (sizeof(VType) > sizeof(UType)) { | |
| if (value > static_cast<VType>(std::numeric_limits<UType>::max())) { | |
| OPENVINO_THROW("Value ", value, " exceeds maximum for target unsigned type"); | |
| } | |
| } |
(sizeof(VType) == sizeof(UType) && std::is_signed_v<VType>) - is redundant
static_cast<unsigned long long>(value) - what about case if U = uint128_t or a custom type?
Fix
This PR prevents negative values from being accepted for
ov::hint::num_requests.Previously, passing a signed negative value could end up as a large unsigned number when stored/handled as an unsigned property. The property helper now validates inputs and rejects negative signed values early with a clear exception.
Tests
src/inference/tests/unit/core.cpp:PropertiesValidation.HintNumRequestsRejectsNegativeSigned(negative values are rejected)PropertiesValidation.HintNumRequestsAcceptsUnsigned(valid unsigned values are accepted and stored correctly)Ticket