Skip to content

[Core] Add nesting-depth limit to If::validate_and_infer_types (CWE-674) - #37436

Open
axinging wants to merge 1 commit into
openvinotoolkit:masterfrom
axinging:if_gh746
Open

[Core] Add nesting-depth limit to If::validate_and_infer_types (CWE-674)#37436
axinging wants to merge 1 commit into
openvinotoolkit:masterfrom
axinging:if_gh746

Conversation

@axinging

@axinging axinging commented Aug 14, 2026

Copy link
Copy Markdown

Details:

The constant-condition branch of If::validate_and_infer_types (if.cpp:109-113) calls validate_and_infer_type_body, which at multi_subgraph_base.cpp:164 invokes
body->validate_nodes_and_infer_types() with no depth counter or recursion limit. If the body contains another constant-condition If, the same path is re-entered, producing one native C++ stack frame per nesting level. A sufficiently deep chain overflows the call stack and crashes the process (SIGSEGV / EXCEPTION_STACK_OVERFLOW).

This path is distinct from the non-constant branch (:124-128): a model whose If ops all carry constant conditions never enters that branch, yet still recurses unboundedly here. The IR frontend (pugixml) imposes no XML nesting cap, making this fully exploitable via a crafted IR model.

Fix: introduce a shared ValidationDepthGuard RAII class (validation_depth_guard.hpp) with a per-op-type static thread_local counter and OV_VALIDATION_DEPTH_GUARD macro. In If::validate_and_infer_types, a single line guards both the constant and non-constant paths against nesting deeper than kMaxValidationDepth = 64.

// validation_depth_guard.hpp — reusable by Loop and other subgraph ops
OV_VALIDATION_DEPTH_GUARD(this, "If");

Tests added:

  • type_prop.if_nested_constant_condition_exceeds_max_depth_throws (ov_core_unit_tests) — directly validates the depth guard throws NodeValidationFailure at depth 1024.
  • IRFrontendTestsIf.nested_if_at_max_depth_loads (ov_ir_frontend_tests) — depth=64 equals kMaxIfValidationDepth and must load successfully.
  • IRFrontendTestsIf.nested_if_depth_limit_is_rejected (ov_ir_frontend_tests) — end-to-end: depth-1024 nested If IR is rejected via core.read_model().

Tickets:

AI Assistance:

  • AI assistance used: yes
  • Bug root cause was found by AI.

@axinging
axinging requested review from a team as code owners August 14, 2026 00:58
@github-actions github-actions Bot added category: Core OpenVINO Core (aka ngraph) category: IR FE OpenVINO IR v10 / v11 FrontEnd labels Aug 14, 2026
@sys-openvino-ci sys-openvino-ci added the ExternalIntelPR External contributor from Intel label Aug 14, 2026
@axinging
axinging marked this pull request as draft August 14, 2026 02:22
### Details:
The constant-condition branch of `If::validate_and_infer_types`
(if.cpp:109-113) calls `validate_and_infer_type_body`, which at
`multi_subgraph_base.cpp:164` invokes
`body->validate_nodes_and_infer_types()` with no depth counter or
recursion limit. If the body contains another constant-condition If,
the same path is re-entered, producing one native C++ stack frame per
nesting level. A sufficiently deep chain overflows the call stack and
crashes the process (SIGSEGV / EXCEPTION_STACK_OVERFLOW).

This path is distinct from the non-constant branch (:124-128): a
model whose If ops all carry constant conditions never enters that
branch, yet still recurses unboundedly here. The IR frontend
(pugixml) imposes no XML nesting cap, making this fully exploitable
via a crafted IR model.

Fix: introduce a shared `ValidationDepthGuard` RAII class
(`validation_depth_guard.hpp`) with a per-op-type `static
thread_local` counter and `OV_VALIDATION_DEPTH_GUARD` macro. In
`If::validate_and_infer_types`, a single line guards both the
constant and non-constant paths against nesting deeper than
`kMaxValidationDepth = 64`.

```cpp
// validation_depth_guard.hpp — reusable by Loop and other subgraph ops
OV_VALIDATION_DEPTH_GUARD(this, "If");
```

Tests added:
- `type_prop.if_nested_constant_condition_exceeds_max_depth_throws`
  (ov_core_unit_tests) — directly validates the depth guard throws
  `NodeValidationFailure` at depth 1024.
- `IRFrontendTestsIf.nested_if_at_max_depth_loads`
  (ov_ir_frontend_tests) — boundary: depth-64 nested If IR loads
  successfully.
- `IRFrontendTestsIf.nested_if_depth_limit_is_rejected`
  (ov_ir_frontend_tests) — end-to-end: depth-1024 nested If IR is
  rejected via `core.read_model()`.

### Tickets:
- *CVS-192797*

### AI Assistance:
- *AI assistance used: yes*
- *Bug root cause was found by AI.*
@github-actions github-actions Bot added the category: CPP API OpenVINO CPP API bindings label Aug 14, 2026
@axinging
axinging marked this pull request as ready for review August 14, 2026 05:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

category: Core OpenVINO Core (aka ngraph) category: CPP API OpenVINO CPP API bindings category: IR FE OpenVINO IR v10 / v11 FrontEnd ExternalIntelPR External contributor from Intel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants