Atom Exhaustion Is Not a Footgun. It's One Third of Our CVEs

Atom Exhaustion Is Not a Footgun. It’s One Third of Our CVEs

原子耗尽并非“自找麻烦”,它占了我们 CVE 的三分之一

May 26, 2026 by Jonatan Männchen 2026 年 5 月 26 日,作者:Jonatan Männchen

35.8% of CVEs published by the Erlang Ecosystem Foundation CNA fall into the category of uncontrolled resource consumption. In the BEAM ecosystem, a large share of those are caused by one recurring issue: atom exhaustion. You can find the current distribution on the EEF CNA’s Common Weaknesses page. Erlang 生态基金会(EEF)CNA 发布的 CVE 中,有 35.8% 属于资源不受控消耗类漏洞。在 BEAM 生态系统中,其中很大一部分是由一个反复出现的问题引起的:原子耗尽(Atom Exhaustion)。你可以在 EEF CNA 的“常见弱点”页面上查看当前的分布情况。

Atom exhaustion is a denial-of-service vulnerability. Atoms are not garbage collected and are stored in a global atom table, and once it fills up, the VM crashes. Creating atoms from non-finite values, especially user-supplied input, is therefore a latent DoS waiting to happen. 原子耗尽是一种拒绝服务(DoS)漏洞。原子不会被垃圾回收,而是存储在全局原子表中,一旦表被填满,虚拟机(VM)就会崩溃。因此,从非有限值(尤其是用户提供的输入)创建原子,是一种潜在的、随时可能触发的 DoS 风险。

This is not limited to obvious calls such as binary_to_atom/1, list_to_atom/1, String.to_atom/1, or List.to_atom/1. Some dangerous patterns are less obvious: 这不仅限于像 binary_to_atom/1list_to_atom/1String.to_atom/1List.to_atom/1 这样明显的调用。一些危险的模式往往不那么显眼:

% Erlang: dynamic atom creation through interpolation
list_to_atom("field_" ++ UserInput)

# Elixir: decoding JSON with atom keys
Jason.decode(json, keys: :atoms)

# Elixir: dynamic atom creation through interpolation
:"field_#{user_input}"

What makes this class of vulnerability persistent is not carelessness. It often appears in code where the input was assumed to be controlled or finite. URI schemes are a good example: it may feel like there are only a few schemes to handle, but if the value comes from external input, the set is no longer guaranteed to be finite. 导致这类漏洞持续存在的原因并非粗心大意。它通常出现在开发者认为输入是受控或有限的代码中。URI 方案就是一个很好的例子:人们可能觉得只有几种方案需要处理,但如果该值来自外部输入,那么其集合就不再保证是有限的了。

Creating atoms from input is unsafe unless the set of possible values is finite, known, and enforced. The safest approach is to avoid creating new atoms at runtime entirely. Prefer explicit lookup tables when the accepted values are known: 除非可能值的集合是有限的、已知的且受到强制约束,否则从输入创建原子是不安全的。最安全的方法是完全避免在运行时创建新原子。当可接受的值已知时,优先使用显式的查找表:

% Erlang
case Scheme of
    <<"http">> -> http;
    <<"https">> -> https;
    _ -> error
end

When a lookup table is not practical, use the safer existing-atom variants, which will raise an error instead of creating a new atom: 当查找表不切实际时,请使用更安全的“现有原子”变体,它们会抛出错误而不是创建新原子:

% Erlang
binary_to_existing_atom(Value)
list_to_existing_atom(Value)

# Elixir
String.to_existing_atom(value)
List.to_existing_atom(value)

Linters can help catch these patterns before they become vulnerabilities. For Elixir projects, consider enabling Credo’s Credo.Check.Warning.UnsafeToAtom, which flags unsafe calls to String.to_atom/1, List.to_atom/1, Module.concat/1,2, and Jason.decode/2 with keys: :atoms. The check is disabled by default. 代码检查工具(Linters)可以在这些模式演变成漏洞之前将其捕获。对于 Elixir 项目,请考虑启用 Credo 的 Credo.Check.Warning.UnsafeToAtom,它会标记对 String.to_atom/1List.to_atom/1Module.concat/1,2 以及带有 keys: :atomsJason.decode/2 的不安全调用。该检查默认是禁用的。

If you maintain an Erlang or Elixir project, search your codebase for atom creation from binaries, strings, JSON keys, URI components, headers, and configuration values. This is one of the easiest vulnerability classes to fix before it becomes a CVE. For more detailed guidance, see the EEF Security Working Group’s guide on preventing atom exhaustion. 如果你维护着 Erlang 或 Elixir 项目,请在代码库中搜索从二进制数据、字符串、JSON 键、URI 组件、头部信息和配置值中创建原子的行为。这是在漏洞演变为 CVE 之前最容易修复的漏洞类别之一。如需更详细的指导,请参阅 EEF 安全工作组关于防止原子耗尽的指南。