CVE-2026-94201

Filtering an :atom attribute with unsafe_to_atom? can exhaust the BEAM atom table in Ash

Published Oct 5, 2026 CVSS 4.0 · 8.2 High EPSS < 1% KEV · Not listed
Description
Ash stores :atom-typed attributes as strings and compares them as strings. When such an attribute is referenced in a filter, the comparison value is coerced through Ash.Type.Atom. Because the type defined no coerce/2 callback, coercion fell back to the default (cast_input/2), which calls String.to_atom/1 when the attribute is configured with the unsafe_to_atom?: true constraint.

Filtering such an attribute with attacker-controlled strings therefore interned a new, permanent atom for every distinct value. Atoms are never garbage collected and the BEAM caps the atom table, so an actor who can supply filter values for a public, filterable :atom attribute declared with unsafe_to_atom?: true can exhaust the atom table and crash the node (denial of service). AshPaperTrail is a notable example: its version resources expose a public, filterable version_action_name atom attribute with unsafe_to_atom?: true by default.

The fix adds a coerce/2 to Ash.Type.Atom that never interns atoms — a comparison value is left as a string, since the type is stored and compared as a string. Setting the attribute from action input (cast_input/2, which still honors unsafe_to_atom?) is unchanged.

This issue affects ash: from 3.5.1 before 3.34.3.

Analysis

AI-generated

Technical impact

The flaw occurs because Ash stores attributes of type :atom as strings but compares them as strings. When a filter is applied to a public :atom attribute that is marked unsafe_to_atom?: true, the incoming filter value is coerced through Ash.Type.Atom using the default cast_input/2 implementation. That implementation converts the string to an Erlang atom via String.to_atom/1. Since atoms are never garbage collected, each distinct value to which a user can apply a filter generates a new permanent atom. Over time, an attacker can exhaust the BEAM atom table, causing the Erlang Virtual Machine to crash and result in a denial‑of‑service attack. The flaw affects not only Ash itself but also projects that expose such attributes, notably AshPaperTrail’s version_action_name field.

Affected Systems

All installed versions of Ash from 3.5.1 up to and including 3.34.2 are vulnerable. The issue was resolved in Ash 3.34.3 by adding a coerce/2 implementation that leaves the comparison value as a string, preventing unsafe atom internment. Users should verify the exact version they run and plan an upgrade if it lies in the affected range.

Risk and Exploitability

The CVSS score of 8.2 indicates high severity. EPSS data is currently not available, which means the likelihood of exploitation cannot be quantified but the lack of mitigation in earlier releases leaves systems susceptible. The attack vector is inferred to be network‑based, as an attacker can supply filter values through any public endpoint that accepts the :atom attribute, such as AshPaperTrail’s version resources. Once the atom table is saturated, the entire node process will terminate, causing a denial‑of‑service.

Remediation

No solution or workaround provided in the CVE record.

OpenCVE Recommended Actions

  • Upgrade Ash to version 3.34.3 or later, which implements a safe coerce/2 that prevents atom internment during filtering.
  • If an upgrade cannot be performed immediately, review the configuration of all :atom attributes and set unsafe_to_atom? to false or remove the attribute from publicly filterable endpoints.
  • As a temporary workaround, replace unsafe_to_atom? usage with explicit string comparison logic or sanitize input values to limit the number of distinct atoms created.

Generated by OpenCVE AI on October 5, 2026 at 21:00 UTC.

Tracking

Sign in to view the affected projects.

Advisories

No advisories available
History
Type Values Removed Values Added
Metrics ssvc

{'options': {'Automatable': 'no', 'Exploitation': 'none', 'Technical Impact': 'partial'}, 'version': '2.0.3'}

Type Values Removed Values Added
Description Ash stores :atom-typed attributes as strings and compares them as strings. When such an attribute is referenced in a filter, the comparison value is coerced through Ash.Type.Atom. Because the type defined no coerce/2 callback, coercion fell back to the default (cast_input/2), which calls String.to_atom/1 when the attribute is configured with the unsafe_to_atom?: true constraint. Filtering such an attribute with attacker-controlled strings therefore interned a new, permanent atom for every distinct value. Atoms are never garbage collected and the BEAM caps the atom table, so an actor who can supply filter values for a public, filterable :atom attribute declared with unsafe_to_atom?: true can exhaust the atom table and crash the node (denial of service). AshPaperTrail is a notable example: its version resources expose a public, filterable version_action_name atom attribute with unsafe_to_atom?: true by default. The fix adds a coerce/2 to Ash.Type.Atom that never interns atoms — a comparison value is left as a string, since the type is stored and compared as a string. Setting the attribute from action input (cast_input/2, which still honors unsafe_to_atom?) is unchanged. This issue affects ash: from 3.5.1 before 3.34.3.
Title Filtering an :atom attribute with unsafe_to_atom? can exhaust the BEAM atom table in Ash
First Time appeared Ash-project
Ash-project ash
Weaknesses CWE-770
CPEs cpe:2.3:a:ash-project:ash:*:*:*:*:*:*:*:*
Vendors & Products Ash-project
Ash-project ash
References
Metrics cvssV4_0

{'score': 8.2, 'vector': 'CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N'}

Weaknesses
  • CWE-770

    Allocation of Resources Without Limits or Throttling

Sources

Status
PUBLISHED
Assigner
EEF
Published
Updated
Oct 6, 2026, 1:41 PM
Reserved
Sep 24, 2026, 9:15 AM
Status
Deferred
Published
Oct 5, 2026, 8:17 PM
Modified
Oct 6, 2026, 3:03 PM