Skip to main content
Vulnerability Database/CVE-2026-94201

CVE-2026-94201: Ash Framework Atom DOS Vulnerability

CVE-2026-94201 is a denial of service vulnerability in Ash Framework that allows attackers to exhaust the atom table by filtering atom-typed attributes with unsafe_to_atom enabled. This article covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-94201 Overview

The Ash framework for Elixir contains an atom exhaustion vulnerability in Ash.Type.Atom. When an :atom-typed attribute is declared with the unsafe_to_atom?: true constraint and exposed as a filterable field, Ash coerces attacker-supplied filter values through String.to_atom/1. Each distinct value interns a new atom in the BEAM atom table, which is capped and never garbage collected. An unauthenticated attacker can exhaust the atom table and crash the Erlang node, causing denial of service [CWE-770]. AshPaperTrail is directly impacted because its version resources expose a public, filterable version_action_name atom attribute with unsafe_to_atom?: true by default.

Critical Impact

Unauthenticated attackers can crash BEAM nodes by submitting unique filter values against public :atom attributes, resulting in denial of service for applications built on vulnerable Ash versions.

Affected Products

  • Ash framework (Elixir) versions >= 3.5.1 and < 3.34.3
  • AshPaperTrail (version resources with default version_action_name configuration)
  • Downstream Elixir applications exposing public, filterable :atom attributes with unsafe_to_atom?: true

Discovery Timeline

  • 2026-10-05 - CVE-2026-94201 published to NVD
  • 2026-10-06 - Last updated in NVD database

Technical Details for CVE-2026-94201

Vulnerability Analysis

Ash stores :atom-typed attributes as strings and compares them as strings at the database layer. When a filter references such an attribute, Ash coerces the comparison value through Ash.Type.Atom. The type did not implement a coerce/2 callback, so coercion fell back to the default behavior that delegates to cast_input/2. For attributes configured with unsafe_to_atom?: true, cast_input/2 calls String.to_atom/1 on the supplied value.

The BEAM virtual machine limits the atom table to roughly 1,048,576 entries by default and does not garbage collect atoms. An attacker who submits a stream of unique filter values against a vulnerable attribute permanently consumes an atom slot per request. Once the table is exhausted, the node crashes with system_limit, taking down all workloads running on that BEAM instance.

Root Cause

The root cause is a missing coerce/2 callback on Ash.Type.Atom. Because filter coercion reused the input-casting path, user-controlled filter strings were interned as atoms whenever the resource opted into unsafe_to_atom?: true. The stored and compared representation is already a string, so interning was never required for the comparison itself.

Attack Vector

Exploitation requires only network access to an endpoint that exposes a filter on a public :atom attribute with unsafe_to_atom?: true. No authentication or user interaction is needed. AshPaperTrail deployments are the canonical target because the version_action_name attribute on version resources ships with this configuration by default.

text
# Patch 1 — add the unsafe_to_atom? constraint declaration
# lib/ash/type/atom.ex
     one_of: [
       type: :any,
       doc: "Allows constraining the value of an atom to a pre-defined list"
+    ],
+    unsafe_to_atom?: [
+      type: :boolean,
+      default: false,
+      doc:
+        "Allows casting to atoms that don't yet exist in the system. See https://security.erlef.org/secure_coding_and_deployment_hardening/atom_exhaustion.html for more."
     ]
   ]

# Patch 2 — add coerce/2 so filter values are never interned
+  @impl true
+  def coerce(value, _constraints) when is_binary(value), do: {:ok, value}
+
+  def coerce(value, _constraints) when is_atom(value), do: {:ok, value}
+
+  def coerce(_value, _constraints), do: :error
+
   @impl true
   def cast_input(value, _) when is_atom(value) do
     {:ok, value}

Source: GitHub Commit 2970ba3 and GitHub Commit 5282f3f

Detection Methods for CVE-2026-94201

Indicators of Compromise

  • Rapid growth of the BEAM atom table reported by :erlang.system_info(:atom_count) relative to :atom_limit.
  • Node crashes with system_limit errors referencing atom table exhaustion in erl_crash.dump or supervisor logs.
  • High-volume HTTP requests varying only the value of a filter parameter targeting an :atom field such as version_action_name.

Detection Strategies

  • Audit Ash resources for attributes declared as Ash.Type.Atom with unsafe_to_atom?: true that are also exposed through filterable public actions or APIs.
  • Instrument runtime telemetry to alert when atom_count exceeds a baseline threshold or grows at an abnormal rate.
  • Enable request-level logging that captures distinct filter values on affected endpoints for retrospective analysis.

Monitoring Recommendations

  • Export BEAM VM metrics (atom_count, atom_limit, process_count) to a central observability platform and alert on sustained growth.
  • Monitor web application firewall logs for high-cardinality query parameters hitting Ash-backed JSON:API or GraphQL endpoints.
  • Correlate node restart events with upstream request patterns to identify atom-exhaustion probing attempts.

How to Mitigate CVE-2026-94201

Immediate Actions Required

  • Upgrade the ash dependency to version 3.34.3 or later as the primary remediation.
  • Review all Ash resources for Ash.Type.Atom attributes with unsafe_to_atom?: true and set the constraint to false wherever dynamic atoms are not strictly required.
  • Restrict or remove public filter exposure on affected attributes until the patched release is deployed.

Patch Information

The fix is delivered in Ash 3.34.3. Commit 5282f3f adds a coerce/2 implementation on Ash.Type.Atom that returns binary and atom values unchanged, ensuring filter comparisons never intern new atoms. Commit 2970ba3 formally introduces the unsafe_to_atom? constraint with a default of false. Setting attributes from action input via cast_input/2 still honors unsafe_to_atom? and is unchanged. See the CNA advisory for CVE-2026-94201 and the OSV record EEF-CVE-2026-94201 for full details.

Workarounds

  • Set unsafe_to_atom?: false on all Ash.Type.Atom attributes exposed to untrusted input and constrain accepted values with one_of.
  • Disable filtering on affected attributes in public APIs (for example, remove version_action_name from AshPaperTrail version resource filters) until upgrading.
  • Place a reverse proxy or WAF rule in front of the application to rate-limit requests that vary filter parameters at high cardinality.
bash
# Update the Ash dependency in mix.exs, then fetch and compile
# mix.exs
# {:ash, "~> 3.34.3"}

mix deps.update ash
mix deps.get
mix compile --force

# Verify the installed version
mix deps | grep ash

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.