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

https://lobste.rs/rss Hits: 20
Summary

Atom Exhaustion Is Not a Footgun. It's One Third of Our CVEs. May 26, 2026 by 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. 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. 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: % 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. 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) Lint...

First seen: 2026-05-27 16:55

Last seen: 2026-05-28 12:10