JustAppSec
Back to news

HTTP/2 scheme header can exhaust BEAM atom table in plug_cowboy

2 min readPublished 27 Apr 2026Updated 27 Apr 2026Source: CVEProject (cvelistV5)

TL;DR - Any unauthenticated client can crash your entire Elixir node by sending HTTP/2 requests with unique :scheme values. plug_cowboy converts each one to a new atom. The atom table fills up, the VM hits system_limit, and the process aborts. Affects all versions from 2.0.0 to < 2.8.1. Patch to 2.8.1 or disable HTTP/2 as a short-term workaround.

What happened

plug_cowboy is the Plug adapter that runs Elixir web applications on the Cowboy HTTP server. It sits between incoming HTTP requests and your application code.

CVE-2026-32688 is a remote denial-of-service. The vulnerable call is in lib/plug/cowboy/conn.ex: Plug.Cowboy.Conn.conn/1 passes the :scheme value from :cowboy_req.scheme/1 directly into String.to_atom/1. For HTTP/2 connections, Cowboy forwards the client-supplied :scheme pseudo-header without validation. Every distinct value becomes a new atom.

Atoms in the BEAM are permanent. They are never garbage-collected. The atom table has a hard ceiling - the CVE record cites a default limit of 1,048,576. An attacker sends a stream of HTTP/2 requests, each with a fresh :scheme value, until that ceiling is hit. The VM raises system_limit and aborts, taking the entire Erlang/Elixir node with it - not just the offending request, not just one process. Everything.

ItemDetail
Affected componentplug_cowboy
TriggerHTTP/2 requests with unique :scheme pseudo-header values
Affected versions2.0.0 to < 2.8.1
Patched version2.8.1
SeverityCVSS v4.0 8.7 (High)
CVSS vectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N

This is one of the BEAM's oldest foot-guns. String.to_atom/1 on untrusted input is well-known to be dangerous, yet it keeps appearing because the failure mode is delayed and non-obvious - everything looks fine until the table is full, then nothing works at all.

Who is impacted

  • Any deployment running plug_cowboy versions >= 2.0.0 and < 2.8.1.
  • Highest risk: internet-facing services with HTTP/2 enabled on the Cowboy listener - no authentication required to trigger this.
  • Not affected: HTTP/1.1-only deployments. Cowboy derives the scheme from the listener type in that case, not from a client-supplied header, so attacker input never reaches String.to_atom/1.

What to do now

  • Upgrade plug_cowboy to 2.8.1 - this is the fix. Check your mix.lock, release artifacts, and container images.
  • If you cannot patch immediately, apply the vendor workaround to strip attacker control over scheme parsing by disabling HTTP/2:

    "Disable HTTP/2 on the Plug.Cowboy.https/3 listener by passing protocol_options: %{protocols: [:http]} in the cowboy options. This restricts the listener to HTTP/1.1, where the scheme is derived from the listener type and is not attacker-controlled."

  • Treat this as an active availability risk and work through the following:
    • Inventory every service that exposes a Cowboy listener with HTTP/2 enabled.
    • Confirm plug_cowboy versions across lockfiles, compiled releases, and running containers - not just your local source.
    • Add alerting for node crashes matching system_limit so you catch active probing before it succeeds.
    • If you see unexpected node restarts in logs predating this patch cycle, treat them as suspicious and investigate.

Related


Content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly, send corrections to [email protected]. Always consult official documentation and validate key implementation decisions before making design or security choices.

Need help?Get in touch.