Skip to content

[Core] Reject negative values when converting to unsigned#33482

Open
om4rrr wants to merge 9 commits intoopenvinotoolkit:masterfrom
om4rrr:fix-negative-unsigned
Open

[Core] Reject negative values when converting to unsigned#33482
om4rrr wants to merge 9 commits intoopenvinotoolkit:masterfrom
om4rrr:fix-negative-unsigned

Conversation

@om4rrr
Copy link

@om4rrr om4rrr commented Jan 6, 2026

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

  • Added unit tests in src/inference/tests/unit/core.cpp:
    • PropertiesValidation.HintNumRequestsRejectsNegativeSigned (negative values are rejected)
    • PropertiesValidation.HintNumRequestsAcceptsUnsigned (valid unsigned values are accepted and stored correctly)

Ticket

Copilot AI review requested due to automatic review settings January 6, 2026 15:28
@om4rrr om4rrr requested a review from a team as a code owner January 6, 2026 15:28
@github-actions github-actions bot added category: Core OpenVINO Core (aka ngraph) category: CPP API OpenVINO CPP API bindings labels Jan 6, 2026
@sys-openvino-ci sys-openvino-ci added the ExternalPR External contributor label Jan 6, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines 1017 to 1024
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());
}
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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());

Copilot uses AI. Check for mistakes.
@om4rrr om4rrr requested a review from a team as a code owner January 15, 2026 02:53
@github-actions github-actions bot added category: inference OpenVINO Runtime library - Inference and removed category: Core OpenVINO Core (aka ngraph) labels Jan 15, 2026
@om4rrr
Copy link
Author

om4rrr commented Jan 15, 2026

Hi @praasz
I aligned the fix with your note about Any::as being a cast-only API.
I reverted the Any-level change and added validation at the ov::hint::num_requests property layer instead, so negative values are rejected instead of wrapping.
I added unit tests in src/inference/tests/unit/core.cpp to cover both the negative case and a valid unsigned value.

@mlukasze
Copy link
Contributor

please resolve conflict before we go any further

@om4rrr om4rrr force-pushed the fix-negative-unsigned branch from 5e2523d to 6cd2054 Compare January 15, 2026 15:52
@om4rrr om4rrr requested a review from praasz January 15, 2026 15:57
@om4rrr
Copy link
Author

om4rrr commented Jan 15, 2026

Hi @mlukasze
I have updated the PR to address the merge conflict and ensured the tests are passing. When you have time, could you please review the PR?

@om4rrr om4rrr requested a review from praasz January 16, 2026 15:08
@om4rrr om4rrr force-pushed the fix-negative-unsigned branch from c131491 to 5e9b295 Compare January 19, 2026 18:49
@praasz praasz self-assigned this Jan 20, 2026
@olpipi
Copy link
Contributor

olpipi commented Jan 22, 2026

I don't have any fundamental objections regarding rejecting negative values for all properties.
Please add tests to check if converting from "-1" as string shows the same behavior as from int.
And please check if binary size is not increased dramatically after adding implementation into .hpp file.

@om4rrr
Copy link
Author

om4rrr commented Jan 22, 2026

Hi @olpipi
I added coverage to ensure negative values are rejected consistently for ov::hint::num_requests, including "-1" passed as a string, and verified core.set_property(..., ov::hint::num_requests(...)) rejects both -1 and "-1".

could you please review it?

@praasz
Copy link
Contributor

praasz commented Jan 23, 2026

Hi @olpipi I added coverage to ensure negative values are rejected consistently for ov::hint::num_requests, including "-1" passed as a string, and verified core.set_property(..., ov::hint::num_requests(...)) rejects both -1 and "-1".

could you please review it?

I agree with @olpipi that this change is valid.
Just make more detailed tests for different property types and input value type to check corner case of conversion and remove the validation if it is not required to avoid dead code which will be reported by static analysis tool.

@praasz praasz added this to the 2026.1 milestone Jan 23, 2026
om4rrr and others added 5 commits January 23, 2026 16:45
- 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
@om4rrr om4rrr requested a review from praasz January 23, 2026 15:19
Comment on lines +163 to +169
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");
}
}
Copy link
Contributor

@olpipi olpipi Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: CPP API OpenVINO CPP API bindings category: inference OpenVINO Runtime library - Inference ExternalPR External contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Good First Issue][Bug][Core]: negative values are not checked in property with unsigned value type

5 participants