GHSA-4w98-xf39-23gp CVE-2026-32873
Loop with Unreachable Exit Condition ('Infinite Loop') in ewe
- Severity
- 7.5 / 10
- High · CVSS 3.1
- Exploit chance (EPSS)
- 0.61%
- Chance of exploitation in the next 30 days. Higher than 49.19% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-825 Expired Pointer Dereference
- CWE-835 Loop with Unreachable Exit Condition ('Infinite Loop')
- Fix
3.0.5- Upgrade ewe to this version or later
Description
Summary
ewe's handle_trailers function contains a bug where rejected trailer headers (forbidden or undeclared) cause an infinite loop. The function recurses with the original unparsed buffer instead of advancing past the rejected header, re-parsing the same header forever. Each malicious request permanently wedges a BEAM process at 100% CPU with no timeout or escape.
Impact
When handle_trailers (ewe/internal/http1.gleam:493) encounters a trailer that is either not in the declared trailer set or is blocked by is_forbidden_trailer, three code paths (lines 520, 523, 526) recurse with the original buffer rest instead of Buffer(header_rest, 0):
// Line 523 — uses `rest` (original buffer), not `Buffer(header_rest, 0)` (remaining)
False -> handle_trailers(req, set, rest)
This causes decoder.decode_packet to re-parse the same header on every iteration, producing an infinite loop. The BEAM process never yields, never times out, and never terminates.
Any ewe application that calls ewe.read_body on chunked requests is affected. This is exploitable by any unauthenticated remote client. There is no application-level workaround — the infinite loop is triggered inside read_body before control returns to application code.
Proof of Concept
Send a chunked request with a forbidden trailer (host) to trigger the infinite loop:
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: host\r\n\r\n4\r\ntest\r\n0\r\nhost: evil.example.com\r\n\r\n' | nc -w 3 localhost 8080
This will hang (no response) until the nc timeout. The server-side handler process is stuck forever.
Exhaust server resources with concurrent requests:
for i in $(seq 1 50); do
printf 'POST / HTTP/1.1\r\nHost: localhost:8080\r\nTransfer-Encoding: chunked\r\nTrailer: host\r\n\r\n4\r\ntest\r\n0\r\nhost: evil.example.com\r\n\r\n' | nc -w 1 localhost 8080 &
done
Open the Erlang Observer (observer:start()) and sort the Processes tab by Reductions to see the stuck processes with continuously climbing reduction counts.
Vulnerable Code
All three False/Error branches in handle_trailers have the same bug:
// ewe/internal/http1.gleam, lines 493–531
fn handle_trailers(
req: Request(BitArray),
set: Set(String),
rest: Buffer,
) -> Request(BitArray) {
case decoder.decode_packet(HttphBin, rest) {
Ok(Packet(HttpEoh, _)) -> req
Ok(Packet(HttpHeader(idx, field, value), header_rest)) -> {
// ... field name parsing ...
case field_name {
Ok(field_name) -> {
case
set.contains(set, field_name) && !is_forbidden_trailer(field_name)
{
True -> {
case bit_array.to_string(value) {
Ok(value) -> {
request.set_header(req, field_name, value)
|> handle_trailers(set, Buffer(header_rest, 0)) // correct
}
Error(Nil) -> handle_trailers(req, set, rest) // BUG: line 520
}
}
False -> handle_trailers(req, set, rest) // BUG: line 523
}
}
Error(Nil) -> handle_trailers(req, set, rest) // BUG: line 526
}
}
_ -> req
}
}
Affected versions
Hex
| Package | Vulnerable | Fixed in |
|---|---|---|
| ewe | >= 0.8.0, < 3.0.5 |
3.0.5 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
ewe Hex → 3.0.5
- mix
{:ewe, "~> 3.0.5"} # then: mix deps.update ewe
Severity breakdown
CVSS 3.1 7.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Weakness
CWE-825: Expired Pointer Dereference
The product dereferences a pointer that contains a location for memory that was previously valid, but is no longer valid.
How to prevent it
- Architecture and Design: Choose a language that provides automatic memory management.
- Implementation: When freeing pointers, be sure to set them to NULL once they are freed. However, the utilization of multiple or complex data structures may lower the usefulness of this strategy.
CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')
The product contains an iteration or loop with an exit condition that cannot be reached, i.e., an infinite loop.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-x2w3-23jr-hrpf ewe Has Improper Neutralization of CRLF Sequences in HTTP Headers (HTTP Request/Response Splitting) | Medium | Also affects ewe |
| GHSA-9w88-79f8-m3vp Permissive List of Allowed Inputs in ewe | Medium | Also affects ewe |
| GHSA-phwj-rprq-35pp Nokogiri: Possible Use-After-Free when setting an attribute value via `Nokogiri::XML::Attr#value=` or `#content=` | High | Same weakness, CWE-825 |
| GHSA-5r4x-w6p5-222q ImageMagick: Use-After-Free in MSL decoder. | Medium | Same weakness, CWE-825 |
| GHSA-532v-xpq5-8h95 Electron: Use-after-free in offscreen child window paint callback | High | Same weakness, CWE-825 |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor