{"id":"CVE-2026-119434","aliases":["EEF-CVE-2026-119434","EUVD-2026-117783","GHSA-p9j8-93gx-5h3x"],"status":"published","title":"Decompression bomb in ezstd:decompress_using_ddict/2 allows BEAM memory exhaustion via unbounded output buffer","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","severity":{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"medium","assessor":"cna","from":"cve"},"epss":{"day":"2026-11-12","score":0.00178,"percentile":0.07423,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":false,"fix_available":true},"cwes":["CWE-409"],"published":"2026-11-05T20:15:56Z","modified":"2026-11-07T02:48:23Z","fixes":[{"ecosystem":"Hex","package":"ezstd","fixed_in":["1.2.5"],"sources":["osv"]}],"affected":[{"source":"osv","ecosystem":"Hex","package":"ezstd","ranges":[{"type":"SEMVER","events":[{"introduced":"1.0.5"},{"fixed":"1.2.5"}]}],"versions":["1.0.5","1.0.6","1.0.7","1.0.8","1.0.9","1.1.0","1.2.0","1.2.1","1.2.2","1.2.3","1.2.4"]}],"products":[{"vendor":"silviucpp","product":"ezstd"}],"references":[{"url":"https://github.com/silviucpp/ezstd/releases/tag/v1.2.5","tags":["release-notes","related","advisory","web"]},{"url":"https://cna.erlef.org/cves/CVE-2026-119434.html","tags":["related","third-party-advisory","advisory","web"]},{"url":"https://osv.dev/vulnerability/EEF-CVE-2026-119434","tags":["related","web"]},{"url":"https://github.com/silviucpp/ezstd/commit/6730128b1d35eca02a2028671421471ff651f423","tags":["related","fix","web"]},{"url":"https://github.com/silviucpp/ezstd/commit/e34f4a4f6dc18d990d1cece2d5fceef8e934a195","tags":["patch","fix"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-119434","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-p9j8-93gx-5h3x"},{"url":"https://github.com","tags":["web"]},{"url":"https://repo.hex.pm","tags":["web"]},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/119xxx/CVE-2026-119434.json","tags":["advisory"]},{"url":"https://github.com/silviucpp/ezstd","tags":["package"]},{"url":"https://hex.pm/packages/ezstd","tags":["package"]}],"sources":{"cve":[{"id":"CVE-2026-119434","title":"Decompression bomb in ezstd:decompress_using_ddict/2 allows BEAM memory exhaustion via unbounded output buffer","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","status":"published","published":"2026-11-05T20:15:56.311Z","modified":"2026-11-05T20:45:33.49Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"medium","assessor":"cna"},{"version":"3.1","score":6.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"medium","assessor":"adp"}],"cwes":["CWE-409"],"updated_at":"2026-11-05T21:25:05.838392Z"}],"euvd":[{"id":"EUVD-2026-117783","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","status":"published","published":"2026-11-05T20:15:56Z","modified":"2026-11-05T20:45:33Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"medium"}],"updated_at":"2026-11-06T00:02:37.63677Z"}],"ghsa":[{"id":"GHSA-p9j8-93gx-5h3x","title":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd...","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","status":"published","published":"2026-11-05T21:34:54Z","modified":"2026-11-05T21:35:00Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium"},{"version":"3.1","score":6.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"medium"}],"cwes":["CWE-409"],"updated_at":"2026-11-05T21:56:38.142385Z"}],"nvd":[{"id":"CVE-2026-119434","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","status":"published","published":"2026-11-05T21:18:41.21Z","modified":"2026-11-05T21:18:41.21Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X","level":"medium","assessor":"secondary"},{"version":"3.1","score":6.2,"vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","level":"medium","assessor":"secondary"}],"cwes":["CWE-409"],"updated_at":"2026-11-05T23:57:23.655479Z"}],"osv":[{"id":"CVE-2026-119434","title":"Decompression bomb in ezstd:decompress_using_ddict/2 allows BEAM memory exhaustion via unbounded output buffer","description":"Improper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to ezstd:decompress_using_ddict/2 to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\nzstd_nif_decompress_using_ddict in c_src/ezstd_nif.cc reads the frame's declared content size with ZSTD_getFrameContentSize and passes it directly to enif_make_new_binary, without the 1 GiB MAX_BUFFER_SIZE check that ezstd:decompress/1 applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by ezstd:decompress/1 but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.","status":"published","published":"2026-11-05T20:15:56.311Z","modified":"2026-11-07T07:02:53.761986812Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-409"],"updated_at":"2026-11-07T03:08:01.41647Z"},{"id":"EEF-CVE-2026-119434","title":"Decompression bomb in ezstd:decompress_using_ddict/2 allows BEAM memory exhaustion via unbounded output buffer","description":"## Summary\n\nImproper Handling of Highly Compressed Data (Data Amplification) vulnerability in silviucpp ezstd allows an attacker who supplies a zstd frame to `ezstd:decompress_using_ddict/2` to exhaust memory and terminate the Erlang VM via a small frame that declares a very large content size.\n\n`zstd_nif_decompress_using_ddict` in `c_src/ezstd_nif.cc` reads the frame's declared content size with `ZSTD_getFrameContentSize` and passes it directly to `enif_make_new_binary`, without the 1 GiB `MAX_BUFFER_SIZE` check that `ezstd:decompress/1` applies before allocating. A valid 65,550-byte frame of RLE blocks declaring 2 GiB is rejected by `ezstd:decompress/1` but is fully allocated and decompressed by the dictionary path. The allocation happens inside a NIF, so when it exceeds the memory available to the VM the operating system kills the whole node rather than the calling process.\n\nThis issue affects ezstd: from 1.0.5 before 1.2.5.\n\n## Details\n\n**1. Declared size is attacker controlled.** A zstd frame header carries an optional Frame Content Size field. `zstd_nif_decompress_using_ddict` in `c_src/ezstd_nif.cc` reads it with `ZSTD_getFrameContentSize` and rejects only the two sentinel values `ZSTD_CONTENTSIZE_UNKNOWN` and `ZSTD_CONTENTSIZE_ERROR`.\n\n**2. Unbounded allocation.** The size is passed directly to `enif_make_new_binary`, so a frame declaring 2 GiB makes the NIF request a 2 GiB binary. The sibling `zstd_nif_decompress` rejects any declared size above `MAX_BUFFER_SIZE` (1 GiB) before allocating; the dictionary path omitted that check from the commit that introduced it.\n\n**3. Amplification.** A frame built from RLE blocks, each encoding 128 KiB of a repeated byte in four bytes, declares and genuinely decompresses to 2 GiB from 65,550 bytes of input, a ratio of about 32,760:1. `ZSTD_decompress_usingDDict` then fills the buffer. Because this happens inside a NIF, exceeding the memory available to the VM results in the operating system killing the whole BEAM process rather than an Erlang-level exception.\n\n## Proof of concept\n\n1. Build ezstd 1.2.4 and start a VM under an external memory limit, for example a container limited to 256 MiB.\n2. Create any decompression dictionary with `ezstd:create_ddict/1`; its content does not matter because the frame carries no dictionary ID.\n3. Construct a zstd frame: the magic number, a frame header descriptor selecting an 8-byte content size, a window descriptor, a little-endian content size of 2 GiB, then 16,383 RLE blocks of 128 KiB each (block header `0x02 0x00 0x10` followed by one payload byte) and a final RLE block with the last-block bit set. The frame is 65,550 bytes.\n4. Call `ezstd:decompress/1` on the frame: it returns `{error, \u003c\u003c\"frame too large or invalid\"\u003e\u003e}`.\n5. Call `ezstd:decompress_using_ddict/2` on the same frame with the dictionary: the VM is killed by the operating system (exit status 137).\n6. Repeat on 1.2.5: the call returns `{error, \u003c\u003c\"frame too large or invalid\"\u003e\u003e}` and the VM continues.\n\n## Impact\n\nAn attacker who can submit a zstd frame to a service that decompresses it with a fixed dictionary can crash the entire Erlang VM with a single request of about 65 KiB. Every application and process hosted in that VM is taken down, not only the one that called the decompressor.\n\n## Workarounds\n\nReplace calls to `ezstd:decompress_using_ddict/2` with the streaming API: create a context with `ezstd:create_decompression_context/1` using a small output buffer, attach the dictionary with `ezstd:select_ddict/2`, and decompress with `ezstd:decompress_streaming/2`. The streaming path decompresses into the caller-sized buffer and gives up with `{error, decompressor_stuck}` after 1000 chunks, so a 64 KiB buffer caps a single call at 64 MiB of output. Treat that error as a rejected input. Legitimate frames whose output exceeds 1000 times the buffer size fail the same way, so size the buffer for the largest expected payload.","status":"published","published":"2026-11-05T20:15:56.311Z","modified":"2026-11-06T01:02:57.349514299Z","cvss":[{"version":"4.0","score":6.9,"vector":"CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","level":"medium"}],"cwes":["CWE-409"],"updated_at":"2026-11-06T01:32:47.895286Z"}]}}
