Introduction
A roughly 100 KB JSON file can pin a jq process at 100% CPU for minutes, and a 2 MB file can stall it for hours. The cause, tracked as CVE-2026-40164, is a hardcoded MurmurHash3 seed that ships in every version of jq. Anyone can use that seed to precompute hash collisions offline, which turns ordinary JSON processing into a quadratic-time operation.
jq is a lightweight, zero-dependency command-line JSON processor written in portable C, often described as "sed for JSON." Stephen Dolan created it in 2012, and it has since become a standard utility in software engineering, embedded in CI/CD pipelines, web service backends, and data processing scripts on Linux, macOS, and Windows. Because jq is everywhere, the attack surface for this vulnerability is broad and often invisible.
Technical Information
Root Cause: Deterministic Hashing with a Public Seed
The problem sits at line 1200 of src/jv.c, where jq defines a compile-time constant seed for its MurmurHash3 implementation:
static const uint32_t HASH_SEED = 0x432A9843;
jq uses this seed for every JSON object hash table operation. Because the seed is hardcoded and visible in the public source code, an adversary can replicate the exact hash function locally and brute-force sets of keys that all land in the same hash bucket. Many modern language runtimes (Python 3.3+, Perl, Ruby, PHP) randomize their hash seeds at process startup specifically to prevent this class of attack. jq did not.
Collision Impact: O(1) to O(n²)
When jq loads a JSON object, it inserts the object's keys into a hash table. Under normal conditions, lookups are O(1). When all keys collide into a single bucket, each lookup becomes O(n), because the implementation has to walk a linear chain. Any jq expression that touches the object's keys (which is nearly all of them) then performs O(n) lookups across O(n) keys, for O(n²) total work.
The advisory's benchmarks show the quadratic scaling:
| Collision Key Count | Approximate Payload Size | Processing Time |
|---|---|---|
| 1,000 keys | 20 KB | 1 second |
| 10,000 keys | 200 KB | 100 seconds |
| 100,000 keys | 2 MB | 3 hours |
This is a textbook algorithmic complexity attack (CWE-407), made worse by a reversible hash with a known seed (CWE-328). A 200 KB payload, trivial to send over any network, costs the target 100 seconds of CPU time.
Attack Flow
-
Offline key generation. The attacker implements MurmurHash3 with the known seed
0x432A9843in any language, then enumerates candidate strings and keeps only those whose hash matches a chosen target value. -
Payload construction. The attacker assembles a JSON object from the colliding keys. 10,000 colliding keys (~200 KB) are enough to stall each jq invocation for roughly 100 seconds, which is a meaningful denial of service.
-
Delivery. The attacker sends the crafted JSON to any endpoint or pipeline that processes it with jq. Common vectors include:
- API responses consumed by
curl | jqpatterns in CI/CD pipelines - JSON payloads submitted to web services that use jq for server-side processing
- Data feeds ingested by ETL scripts
- API responses consumed by
-
Impact. The jq process enters a tight CPU loop. Nothing crashes. The process just stops responding until the quadratic computation finishes. In a pipeline, that can block deployments, stall monitoring, or exhaust worker pools.
Comparison to Previous jq Vulnerabilities
The advisory notes that this attack is far more practical than existing heap overflow issues in jq, which required payloads of about 1.43 GB. CVE-2026-40164 causes comparable or greater disruption with a payload four orders of magnitude smaller.
The Fix
Commit 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 replaces the static seed with a random seed initialized at runtime from system entropy. The suggested patch to src/jv.c is:
- static const uint32_t HASH_SEED = 0x432A9843; + static uint32_t hash_seed; + __attribute__((constructor)) + static void init_hash_seed(void) { + #if defined(HAVE_ARC4RANDOM) + hash_seed = arc4random(); + #elif defined(HAVE_GETENTROPY) + if (getentropy(&hash_seed, sizeof(hash_seed)) != 0) + hash_seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); + #else + int fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { read(fd, &hash_seed, 4); close(fd); } + else hash_seed = (uint32_t)getpid() ^ (uint32_t)time(NULL); + #endif + }
With this change, each jq process gets a unique seed, which makes offline collision precomputation infeasible.
Affected Systems and Versions
All versions of jq are affected. The advisory lists no version range boundary. The hardcoded seed has been in src/jv.c since the hash table was first implemented. At the time of the advisory, there is no officially patched release. The fix is available only as commit 0c7d133c3c7e37c00b6d46b658a02244fdd3c784 in the jqlang/jq repository.
Any deployment that passes untrusted or externally sourced JSON through jq is vulnerable. This includes but is not limited to:
- CI/CD pipelines using
curl | jqto consume external API responses - Web services that invoke jq for server-side JSON transformation
- Data processing and ETL scripts that ingest feeds from third-party sources
Vendor Security History
The jqlang organization maintains jq on GitHub and has a history of fixing security issues promptly. The jq 1.8.1 release on July 1, 2025, included fixes for multiple security vulnerabilities:
- CVE-2025-49014: Heap use after free
- CVE-2024-23337: Signed integer overflow
Given that record, an official release with the hash seed randomization will likely follow. Organizations should watch the jq releases page for updates.



