| --- |
| license: apache-2.0 |
| tags: |
| - security-poc |
| - not-a-model |
| --- |
| |
| # OpenVINO IR reader β stack-exhaustion DoS PoC (`read_model`) |
| |
| **β οΈ This repo contains a malicious proof-of-concept model file for a security report. It is NOT a usable ML model. Do not load `userdata_50000.xml` on a machine you care about β it deliberately crashes the OpenVINO loader.** |
| |
| ## What it is |
| Two malicious IR files, each triggering an **independent unbounded-recursion site** in the IR deserializer (same missing-depth-cap class). Both have **no `.bin`** (weights are not needed). |
| - `userdata_50000.xml` (1.84 MB) β a `Parameter` whose `<rt_info>` nests `<user_data>` **50 000 deep** β `set_custom_rt_info` recursion. |
| - `nested_ti_10000.xml` (7.98 MB) β `TensorIterator` layers nested **10 000 deep** via `<body>` β `parse_function` recursion. |
|
|
| When OpenVINO parses either file, the deserializer recurses once per nesting level with **no depth limit**, exhausting the stack and crashing `read_model` with `SIGSEGV`. |
|
|
| ## Reproduce (one line each) |
| ```bash |
| pip install openvino # tested on 2026.2.1 |
| python -c "import openvino as ov; ov.Core().read_model('userdata_50000.xml')" # -> Segfault (8/8) |
| python -c "import openvino as ov; ov.Core().read_model('nested_ti_10000.xml')" # -> Segfault (3/3) |
| ``` |
| Symbolized AddressSanitizer traces: `asan-stackoverflow-evidence.txt` (Site A), `asan-parse_function-evidence.txt` (Site B). |
|
|
| ## Root cause |
| Unbounded recursion in the IR deserializer, `src/core/xml_util/src/xml_deserialize_util.cpp`: |
| - `set_custom_rt_info` (lines 134β153) β recurses per nested `<user_data>` element. |
| - `parse_function` (line 921; recursion at line 871) β recurses per nested subgraph body (`TensorIterator`/`Loop`/`If`), a second instance of the same missing-guard class. |
|
|
| Symbolized AddressSanitizer trace (from-source Debug build, HEAD `ce20c09`): |
| ``` |
| ==ERROR: AddressSanitizer: stack-overflow |
| #21 set_custom_rt_info src/core/xml_util/src/xml_deserialize_util.cpp:147 |
| #22 set_custom_rt_info src/core/xml_util/src/xml_deserialize_util.cpp:147 |
| ... (thousands of identical frames) ... |
| ``` |
|
|
| ## Regenerate the PoC |
| `fe_recursion.py` β `gen_user_data(50000)` (the `.xml` here) and `gen_nested_ti(20000)` (the subgraph-body variant). |
|
|
| ## Impact |
| Availability DoS: any service that calls `read_model`/`compile_model` on an untrusted OpenVINO IR model can be crashed by one small (~1.8 MB) `.xml`. |
|
|
| ## Fix |
| Cap recursion depth (or use an explicit worklist) in `set_custom_rt_info` and `parse_function`; reject IR whose `rt_info`/subgraph nesting exceeds a sane bound. |
|
|