HTTP Request/Response Smuggling in puma
Description
Puma is a web server for Ruby/Rack applications built for parallelism. Prior to version 6.4.2, puma exhibited incorrect behavior when parsing chunked transfer encoding bodies in a way that allowed HTTP request smuggling. Fixed versions limits the size of chunk extensions. Without this limit, an attacker could cause unbounded resource (CPU, network bandwidth) consumption. This vulnerability has been fixed in versions 6.4.2 and 5.6.8.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
HTTP request smuggling vulnerability in Puma due to improper chunked transfer encoding parsing, allowing resource exhaustion; fixed in versions 6.4.2 and 5.6.8.
Puma, a popular Ruby web server, is vulnerable to HTTP request smuggling caused by incorrect parsing of chunked transfer encoding bodies [1]. The root issue is that prior to versions 6.4.2 and 5.6.8, Puma did not limit the size of chunk extensions in chunked requests [2]. This allows an attacker to send crafted chunked HTTP requests with excessively long extensions, leading to unbounded CPU and network bandwidth consumption [2].
An unauthenticated attacker can exploit this remotely by sending specially crafted chunked requests to a vulnerable Puma server [2]. The attack does not require any special privileges or network position, as it targets the parsing logic directly [1]. By sending a series of requests with large chunk extensions, the attacker can cause the server to consume resources indefinitely, potentially leading to denial of service [2].
The impact includes both resource exhaustion and HTTP request smuggling, which can enable further attacks such as cache poisoning, session hijacking, or bypassing security controls [2]. The vulnerability allows an attacker to manipulate how requests are interpreted, potentially leading to unintended behavior in downstream proxies or applications [1].
The vulnerability has been fixed by introducing two limits in the chunked body parser: a maximum chunk header size of 4096 bytes and a maximum cumulative excess of 16 KB for chunk extensions [3][4]. These limits prevent unbounded resource consumption by rejecting requests that exceed these thresholds. Administrators are advised to upgrade to Puma 6.4.2 or 5.6.8 immediately [2]. No workarounds are known.
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
pumaRubyGems | >= 6.0.0, < 6.4.2 | 6.4.2 |
pumaRubyGems | < 5.6.8 | 5.6.8 |
Affected products
13- osv-coords12 versionspkg:apk/chainguard/ruby3.2-pumapkg:apk/wolfi/ruby3.2-pumapkg:deb/ubuntu/puma@5.6.5-4ubuntu2.1?arch=source&distro=manticpkg:gem/pumapkg:rpm/opensuse/rubygem-puma&distro=openSUSE%20Leap%2015.5pkg:rpm/opensuse/rubygem-puma&distro=openSUSE%20Leap%2015.6pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP2pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP3pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP4pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP5pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP6pkg:rpm/suse/rubygem-puma&distro=SUSE%20Linux%20Enterprise%20High%20Availability%20Extension%2015%20SP7
< 6.4.2-r0+ 11 more
- (no CPE)range: < 6.4.2-r0
- (no CPE)range: < 6.4.2-r0
- (no CPE)range: < 5.6.5-4ubuntu2.1
- (no CPE)range: >= 6.0.0, < 6.4.2
- (no CPE)range: < 4.3.12-150000.3.15.1
- (no CPE)range: < 5.6.9-150600.18.3.1
- (no CPE)range: < 4.3.12-150000.3.15.1
- (no CPE)range: < 4.3.12-150000.3.15.1
- (no CPE)range: < 4.3.12-150000.3.15.1
- (no CPE)range: < 4.3.12-150000.3.15.1
- (no CPE)range: < 5.6.9-150600.18.3.1
- (no CPE)range: < 5.6.9-150600.18.3.1
- puma/pumav5Range: < 5.6.8
Patches
360d5ee3734adMerge pull request from GHSA-c2f4-cvqm-65w2
2 files changed · +41 −0
lib/puma/client.rb+27 −0 modified@@ -51,6 +51,14 @@ class Client # :nodoc: CHUNK_VALID_ENDING = Const::LINE_END CHUNK_VALID_ENDING_SIZE = CHUNK_VALID_ENDING.bytesize + # The maximum number of bytes we'll buffer looking for a valid + # chunk header. + MAX_CHUNK_HEADER_SIZE = 4096 + + # The maximum amount of excess data the client sends + # using chunk size extensions before we abort the connection. + MAX_CHUNK_EXCESS = 16 * 1024 + # Content-Length header value validation CONTENT_LENGTH_VALUE_INVALID = /[^\d]/.freeze @@ -496,6 +504,7 @@ def setup_chunked_body(body) @chunked_body = true @partial_part_left = 0 @prev_chunk = "" + @excess_cr = 0 @body = Tempfile.new(Const::PUMA_TMP_BASE) @body.unlink @@ -577,6 +586,20 @@ def decode_chunk(chunk) end end + # Track the excess as a function of the size of the + # header vs the size of the actual data. Excess can + # go negative (and is expected to) when the body is + # significant. + # The additional of chunk_hex.size and 2 compensates + # for a client sending 1 byte in a chunked body over + # a long period of time, making sure that that client + # isn't accidentally eventually punished. + @excess_cr += (line.size - len - chunk_hex.size - 2) + + if @excess_cr >= MAX_CHUNK_EXCESS + raise HttpParserError, "Maximum chunk excess detected" + end + len += 2 part = io.read(len) @@ -604,6 +627,10 @@ def decode_chunk(chunk) @partial_part_left = len - part.size end else + if @prev_chunk.size + chunk.size >= MAX_CHUNK_HEADER_SIZE + raise HttpParserError, "maximum size of chunk header exceeded" + end + @prev_chunk = line return false end
test/test_puma_server.rb+14 −0 modified@@ -904,6 +904,20 @@ def test_large_chunked_request end end + def test_large_chunked_request_header + server_run(environment: :production) { |env| + [200, {}, [""]] + } + + max_chunk_header_size = Puma::Client::MAX_CHUNK_HEADER_SIZE + header = "GET / HTTP/1.1\r\nConnection: close\r\nContent-Length: 200\r\nTransfer-Encoding: chunked\r\n\r\n" + socket = send_http "#{header}1;t#{'x' * (max_chunk_header_size + 2)}" + + data = socket.read + + assert_match "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", data + end + def test_chunked_request_pause_before_value body = nil content_length = nil
bbb880ffb6deMerge pull request from GHSA-c2f4-cvqm-65w2
2 files changed · +41 −0
lib/puma/client.rb+27 −0 modified@@ -48,6 +48,14 @@ class Client CHUNK_VALID_ENDING = Const::LINE_END CHUNK_VALID_ENDING_SIZE = CHUNK_VALID_ENDING.bytesize + # The maximum number of bytes we'll buffer looking for a valid + # chunk header. + MAX_CHUNK_HEADER_SIZE = 4096 + + # The maximum amount of excess data the client sends + # using chunk size extensions before we abort the connection. + MAX_CHUNK_EXCESS = 16 * 1024 + # Content-Length header value validation CONTENT_LENGTH_VALUE_INVALID = /[^\d]/.freeze @@ -460,6 +468,7 @@ def setup_chunked_body(body) @chunked_body = true @partial_part_left = 0 @prev_chunk = "" + @excess_cr = 0 @body = Tempfile.new(Const::PUMA_TMP_BASE) @body.unlink @@ -541,6 +550,20 @@ def decode_chunk(chunk) end end + # Track the excess as a function of the size of the + # header vs the size of the actual data. Excess can + # go negative (and is expected to) when the body is + # significant. + # The additional of chunk_hex.size and 2 compensates + # for a client sending 1 byte in a chunked body over + # a long period of time, making sure that that client + # isn't accidentally eventually punished. + @excess_cr += (line.size - len - chunk_hex.size - 2) + + if @excess_cr >= MAX_CHUNK_EXCESS + raise HttpParserError, "Maximum chunk excess detected" + end + len += 2 part = io.read(len) @@ -568,6 +591,10 @@ def decode_chunk(chunk) @partial_part_left = len - part.size end else + if @prev_chunk.size + chunk.size >= MAX_CHUNK_HEADER_SIZE + raise HttpParserError, "maximum size of chunk header exceeded" + end + @prev_chunk = line return false end
test/test_puma_server.rb+14 −0 modified@@ -648,6 +648,20 @@ def test_large_chunked_request end end + def test_large_chunked_request_header + server_run(environment: :production) { |env| + [200, {}, [""]] + } + + max_chunk_header_size = Puma::Client::MAX_CHUNK_HEADER_SIZE + header = "GET / HTTP/1.1\r\nConnection: close\r\nContent-Length: 200\r\nTransfer-Encoding: chunked\r\n\r\n" + socket = send_http "#{header}1;t#{'x' * (max_chunk_header_size + 2)}" + + data = socket.read + + assert_match "HTTP/1.1 400 Bad Request\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", data + end + def test_chunked_request_pause_before_value body = nil content_length = nil
1 file changed · +10 −0
History.md+10 −0 modified@@ -1,3 +1,8 @@ +## 6.4.2 / 2024-01-08 + +* Security + * Limit the size of chunk extensions. Without this limit, an attacker could cause unbounded resource (CPU, network bandwidth) consumption. ([GHSA-c2f4-cvqm-65w2](https://github.com/puma/puma/security/advisories/GHSA-c2f4-cvqm-65w2)) + ## 6.4.1 / 2024-01-03 * Bugfixes @@ -168,6 +173,11 @@ * Ruby 3.2 will have native IO#wait_* methods, don't require io/wait ([#2903]) * Various internal API refactorings ([#2942], [#2921], [#2922], [#2955]) +## 5.6.8 / 2024-01-08 + +* Security + * Limit the size of chunk extensions. Without this limit, an attacker could cause unbounded resource (CPU, network bandwidth) consumption. ([GHSA-c2f4-cvqm-65w2](https://github.com/puma/puma/security/advisories/GHSA-c2f4-cvqm-65w2)) + ## 5.6.7 / 2023-08-18 * Security
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-c2f4-cvqm-65w2ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-21647ghsaADVISORY
- github.com/puma/puma/commit/5fc43d73b6ff193325e657a24ed76dec79133e93ghsax_refsource_MISCWEB
- github.com/puma/puma/commit/60d5ee3734adc8cee85c3f0561af392448fe19b7ghsaWEB
- github.com/puma/puma/commit/bbb880ffb6debbfdea535b4b3eb2204d49ae151dghsaWEB
- github.com/puma/puma/security/advisories/GHSA-c2f4-cvqm-65w2ghsax_refsource_CONFIRMWEB
- github.com/rubysec/ruby-advisory-db/blob/master/gems/puma/CVE-2024-21647.ymlghsaWEB
- lists.debian.org/debian-lts-announce/2024/11/msg00004.htmlghsaWEB
News mentions
0No linked articles in our index yet.