Downloads · 30 days
0
Talson/mfv-openvino-ir-overflow
mfv-openvino-ir-overflow is a machine learning model from Talson. Use it for the machine learning task on the model card, and read the license before you ship it in a product.
Target format (huntr): OpenVINO ($1500 tier, "Model File Formats") Affected project: openvinotoolkit/openvino — Intel OpenVINO Toolkit (~7.8k stars, active development) Class: CWE-190 Integer Overflow → CWE-125 Out-of…
Downloads · 30 days
0
Access
Public
Updated Jun 12, 2026
Repo size
—
Likes
0
Public
Click a slice to open those files.
.md10 KB · 39%
From the Hugging Face model README
Target format (huntr): OpenVINO ($1500 tier, "Model File Formats")
Affected project: openvinotoolkit/openvino — Intel OpenVINO Toolkit (~7.8k stars, active development)
Class: CWE-190 Integer Overflow → CWE-125 Out-of-bounds Read — attacker-controlled offset and size attributes from the .xml model file overflow when added together, bypassing a bounds check. The resulting pointer computation goes out of bounds of the .bin weights buffer. Additionally, shape_size uses unsafe multiplication (safe version exists but is not used in the deserializer).
Status: TOOL-VERIFIED under AddressSanitizer (SEGV READ + heap-buffer-overflow READ). 2026-06-12.
OpenVINO IR (Intermediate Representation) models consist of:
.xml file: layer graph topology with tensor shapes, offsets, and sizes.bin file: binary weight dataThe standard load path is:
ov::Core core;
auto model = core.read_model("model.xml", "model.bin");
auto compiled = core.compile_model(model, "CPU");
auto infer_request = compiled.create_infer_request();
The .xml file is fully attacker-controlled. It specifies offset and size attributes on <data> elements that tell the deserializer where to read weight data from the .bin buffer. Loading an untrusted .xml + .bin pair causes OOB memory access during model deserialization.
| # | Bug | Primitive | Function / line | Trigger | Verified |
|---|---|---|---|---|---|
| 1 | offset + size overflow in bounds check | OOB READ (SEGV) | xml_deserialize_util.cpp:895 | offset+size attrs in XML | ✅ ASAN |
| 2 | Same overflow, string tensor path | heap-buffer-overflow READ | xml_deserialize_util.cpp:828 | offset+size attrs in XML | ✅ ASAN |
| 3 | shape_size unsafe multiplication | validation bypass | xml_deserialize_util.cpp:904 | shape attr in XML | ✅ (logic) |
| 4 | Same overflow in preprocessing | OOB READ | input_model.cpp:136 | mean offset+size in XML | same pattern |
Root cause: arithmetic on attacker-controlled XML attributes without overflow checks, despite OpenVINO having safe arithmetic functions (mul_overflow, shape_size_safe) available but unused in the IR deserializer.
xml_deserialize_util.cpp:893-897 (OpenVINO @ 7cf8606, 2026-06-12):
const auto size = static_cast<size_t>(pugixml::get_uint64_attr(dn, "size"));
const auto offset = static_cast<size_t>(pugixml::get_uint64_attr(dn, "offset"));
OPENVINO_ASSERT(m_weights->size() >= offset + size, "Incorrect weights in bin file!");
char* data = m_weights->get_ptr<char>() + offset;
Both offset and size are attacker-controlled uint64_t values from the XML <data> element. The bounds check at line 895 computes offset + size which can overflow size_t to a small value, causing the assertion to pass.
Example: offset = 0xFFFFFFFFFFFFFF00, size = 0x200
offset + size = 0x100 (256) — overflows!m_weights->size() = 1024 >= 256 → check passesdata = weights_ptr + 0xFFFFFFFFFFFFFF00 → points ~18 EB past the buffer==ERROR: AddressSanitizer: SEGV on unknown address 0x7cafdb9dff80
(pc 0x55c807f09669 bp 0x7ffe8a8e34d0 sp 0x7ffe8a8e3420 T0)
The signal is caused by a READ memory access.
#0 test_offset_size_overflow() harness_ir.cpp:96
(full log: findings/openvino_evidence/offset_overflow_segv.txt)
xml_deserialize_util.cpp:819-830:
size_t offset = static_cast<size_t>(pugixml::get_uint64_attr(dn, "offset"));
// ...
if (m_weights->size() < offset + size) // ← SAME overflow bug
OPENVINO_THROW("Incorrect weights in bin file!");
char* data = m_weights->get_ptr<char>() + offset;
auto buffer = ...unpack_string_tensor(data, size); // reads 'size' bytes from OOB
This path handles string-type tensor data. With a valid-looking offset (within the buffer) and a size that causes offset + size to wrap, the check passes but unpack_string_tensor reads size bytes from near-end-of-buffer → heap-buffer-overflow READ.
==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7d46d69e0485
READ of size 1 at 0x7d46d69e0485 thread T0
#0 test_offset_size_overflow_variant2() harness_ir.cpp:133
0x7d46d69e0485 is located 5 bytes after 1024-byte region [0x7d46d69e0080,0x7d46d69e0480)
(full log: findings/openvino_evidence/offset_overflow_heap_oob.txt)
xml_deserialize_util.cpp:904:
if (size < ((ov::shape_size(shape) * el_type.bitwidth() + 7) >> 3)) {
OPENVINO_THROW("Attribute and shape size are inconsistent...");
}
shape_size (defined at shape.hpp:87-95) uses std::accumulate with std::multiplies — no overflow check:
// shape.hpp:92-95 — THE UNSAFE VERSION
return std::accumulate(start_dim, end_dim,
typename ...:value_type{1},
std::multiplies<typename ...:value_type>());
OpenVINO has a safe version at shape_util.cpp:72-80:
// shape_util.cpp:72-80 — THE SAFE VERSION (NOT USED IN DESERIALIZER)
std::optional<size_t> shape_size_safe(const Shape& shape) {
size_t size = 1;
for (auto first = shape.cbegin(), last = shape.cend(); first != last; ++first) {
if (mul_overflow(size, *first, size)) { // uses __builtin_mul_overflow
return std::nullopt;
}
}
return std::make_optional(size);
}
With crafted shape dims like [2, 2, 2^62]:
shape_size() overflows to 00 * bitwidth + 7 >> 3 = 0size < 0 fails (any size ≥ 0) → validation passessize bytes of datashape_size() = 0 (overflowed from true value > 2^64)
Validation PASSED: xml_size (1024) >= overflowed check (0)
Shape implies 2^65 elements but buffer is only 1024 bytes.
(full log: findings/openvino_evidence/shape_size_overflow.txt)
input_model.cpp:136:
if (const_offset + const_size > weights->size()) {
OPENVINO_THROW("mean value offset and size are out of weights size range");
}
Same pattern: const_offset and const_size from XML preprocessing <mean> element, addition can overflow.
OpenVINO demonstrates inconsistent use of safe arithmetic:
| Component | Function | Safe? | Notes |
|---|---|---|---|
shape_util.cpp | shape_size_safe | ✅ | Uses mul_overflow |
memory_util.cpp | get_memory_size_safe | ✅ | Uses mul_overflow |
common_util.hpp | mul_overflow | ✅ | Uses __builtin_mul_overflow |
runtime/tensor.cpp | allocation | ✅ | Uses get_memory_size_safe |
xml_deserialize_util.cpp:895 | bounds check | ❌ | Plain addition, no overflow check |
xml_deserialize_util.cpp:828 | bounds check | ❌ | Plain addition, no overflow check |
xml_deserialize_util.cpp:904 | shape validation | ❌ | Uses shape_size not shape_size_safe |
input_model.cpp:136 | bounds check | ❌ | Plain addition, no overflow check |
The safe functions exist and are used in other code paths — the IR deserializer simply doesn't use them.
7cf8606, 2026-06-12): AFFECTED.XmlDeserializer::on_adapter implementation.openvino pip package) that call read_model are also affected.# Build harness with ASAN:
g++ -std=c++17 -fsanitize=address -g -O0 -o harness_ir harness_ir.cpp
# Test 1: offset+size overflow → SEGV (huge offset)
./harness_ir
# Test 2: offset+size overflow → heap-buffer-overflow (valid offset, huge size)
./harness_ir variant2
# Test 3: shape_size overflow (validation bypass)
./harness_ir shape
Build: poc/mfv_openvino_ir.cpp
Build flags: g++ -std=c++17 -fsanitize=address -g -O0
BUG 1+2+4: Replace plain offset + size with overflow-checked addition:
size_t sum;
OPENVINO_ASSERT(!__builtin_add_overflow(offset, size, &sum) && m_weights->size() >= sum,
"Incorrect weights in bin file!");
BUG 3: Use shape_size_safe instead of shape_size:
auto maybe_shape_size = ov::util::shape_size_safe(ov::Shape(shape.begin(), shape.end()));
OPENVINO_ASSERT(maybe_shape_size.has_value(), "Shape dimensions overflow");
if (size < ((maybe_shape_size.value() * el_type.bitwidth() + 7) >> 3)) {
OPENVINO_THROW("...");
}
General: Audit all XML-derived arithmetic in the IR frontend for consistent use of safe functions.
AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:H → 7.1 (local file, user loads model; OOB read → information disclosure + crash). Note: this is OOB READ not WRITE — exploitability for code execution is lower than the WRITE bugs in other targets, but information disclosure (memory contents leaked through model outputs) is possible.