OpenVINO IR shape_size() integer-overflow sanity-check bypass PoC

Proof-of-concept files for a huntr Model Format Vulnerability report against openvinotoolkit/openvino (IR model loader).

evil.xml/evil.bin - a minimal, valid OpenVINO IR pair with a single Const layer declaring shape="4194304,4194304,4194304" (three dimensions of 2^22 each; true element product = 2^66, an exact multiple of 2^64) and size="16" matched by a real 16-byte .bin file.

shape_size() (src/core/include/openvino/core/shape.hpp:87) computes the element count via std::accumulate/std::multiplies with no overflow check, in an int64_t accumulator later cast to size_t. Because the true product is an exact multiple of 2^64, the overflow wraps to exactly 0 - which matches the tiny 16-byte buffer's byte count and lets the sanity check in set_constant_num_buffer (xml_deserialize_util.cpp:880-918) pass silently, even though the declared shape and the real backing buffer differ by dozens of orders of magnitude.

Verified live (openvino 2026.2.1-21919, pip wheel, no C++ rebuild):

import openvino as ov
core = ov.Core()
model = core.read_model("evil.xml")   # loads WITHOUT any exception
const = next(n for n in model.get_ops() if n.get_type_name() == "Constant")
const.get_output_shape(0)   # -> [4194304, 4194304, 4194304]  (full attacker control)
const.get_byte_size()       # -> 0  (same overflow, wrapped independently)

Control experiment (proving the guard is real and specifically bypassed by overflow, not just generally absent): a non-overflowing mismatched shape ("100,100", still with size="16", true product 10,000 elements = 40,000 bytes vs 16 real bytes) correctly raises "Empty weights data in bin file or bin file cannot be found!" at the exact same check (xml_deserialize_util.cpp:881) - confirming the sanity check is active and effective for ordinary mismatches, and only silently bypassed when the true product wraps around 2^64.

Reachable via the public ov.Core().read_model() API - the standard, documented way to load any OpenVINO IR model, hitting essentially every model with a numeric Const layer.

Honest limit: a crash/OOB-read via Python bindings specifically (Constant.get_data()/get_vector()) was not achieved - both of those particular accessors happen to avoid the mine via their own internal implementation quirks (numpy's own pre-allocation size check; get_vector() self-consistently reusing the same wrapped-to-0 byte count). This does not mean the underlying state mismatch is safe - it means the real danger lies in code paths that trust get_shape()/get_output_shape() to size a raw read directly (constant folding, evaluate(), internal transformations), which were not exercised in this PoC.

Impact: an attacker who can supply or influence an OpenVINO IR (.xml/.bin pair) can make the loader silently accept a Constant node with a declared shape/byte-size state that is internally inconsistent by many orders of magnitude - a sanity-check bypass that downstream code relying on get_shape() to size reads could turn into an out-of-bounds read, not just a denial of service.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support