Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/elixir/lib/code/formatter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1457,7 +1457,7 @@ defmodule Code.Formatter do
metadata = [
file: state.file,
line: meta[:line],
sigil: String.to_atom(name),
sigil: String.to_unsafe_atom(name),
modifiers: modifiers,
opening_delimiter: opening_delimiter
]
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/code/identifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ defmodule Code.Identifier do
with "-" <> rest <- Atom.to_string(atom),
[trailing | reversed] = rest |> String.split("/") |> Enum.reverse(),
[arity, _inner, _count, ""] <- String.split(trailing, "-") do
{reversed |> Enum.reverse() |> Enum.join("/") |> String.to_atom(), arity}
{reversed |> Enum.reverse() |> Enum.join("/") |> String.to_unsafe_atom(), arity}
else
_ -> :error
end
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/code/normalizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ defmodule Code.Normalizer do
"Elixir." <> segments ->
segments
|> String.split(".")
|> Enum.map(&String.to_atom/1)
|> Enum.map(&String.to_unsafe_atom/1)
end

{:__aliases__, meta, segments}
Expand Down Expand Up @@ -559,7 +559,7 @@ defmodule Code.Normalizer do
atom
|> Atom.to_string()
|> maybe_escape_literal(state)
|> String.to_atom()
|> String.to_unsafe_atom()
end

defp maybe_escape_literal(term, _) do
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/code/typespec.ex
Original file line number Diff line number Diff line change
Expand Up @@ -399,10 +399,10 @@ defmodule Code.Typespec do
defp erl_to_ex_var(var) do
case Atom.to_string(var) do
<<"_", c::utf8, rest::binary>> ->
String.to_atom("_#{String.downcase(<<c::utf8>>)}#{rest}")
String.to_unsafe_atom("_#{String.downcase(<<c::utf8>>)}#{rest}")

<<c::utf8, rest::binary>> ->
String.to_atom("#{String.downcase(<<c::utf8>>)}#{rest}")
String.to_unsafe_atom("#{String.downcase(<<c::utf8>>)}#{rest}")
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/enum.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,7 @@ defmodule Enum do
)

# Avoid warnings about Dict
dict_module = String.to_atom("Dict")
dict_module = String.to_unsafe_atom("Dict")

reduce(reverse(enumerable), dict, fn entry, categories ->
dict_module.update(categories, fun.(entry), [entry], &[entry | &1])
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/exception.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,7 @@ defmodule SystemLimitError do

For example, this can happen if you try to create an atom that is too large:

iex> String.to_atom(String.duplicate("a", 100_000))
iex> String.to_unsafe_atom(String.duplicate("a", 100_000))
** (SystemLimitError) a system limit has been reached
"""

Expand Down Expand Up @@ -1807,7 +1807,7 @@ defmodule UndefinedFunctionError do

defp load_module({name, _path, _loaded?}) do
name
|> List.to_atom()
|> List.to_unsafe_atom()
|> Code.ensure_loaded()
end

Expand Down
10 changes: 5 additions & 5 deletions lib/elixir/lib/hash_dict.ex
Original file line number Diff line number Diff line change
Expand Up @@ -265,13 +265,13 @@ defimpl Enumerable, for: HashDict do

def reduce(dict, acc, fun) do
# Avoid warnings about HashDict being deprecated.
module = String.to_atom("HashDict")
module = String.to_unsafe_atom("HashDict")
module.reduce(dict, acc, fun)
end

def member?(dict, {key, value}) do
# Avoid warnings about HashDict being deprecated.
module = String.to_atom("HashDict")
module = String.to_unsafe_atom("HashDict")
{:ok, match?({:ok, ^value}, module.fetch(dict, key))}
end

Expand All @@ -281,7 +281,7 @@ defimpl Enumerable, for: HashDict do

def count(dict) do
# Avoid warnings about HashDict being deprecated.
module = String.to_atom("HashDict")
module = String.to_unsafe_atom("HashDict")
{:ok, module.size(dict)}
end

Expand All @@ -296,7 +296,7 @@ defimpl Collectable, for: HashDict do

def into(original) do
# Avoid warnings about HashDict being deprecated.
module = String.to_atom("HashDict")
module = String.to_unsafe_atom("HashDict")

collector_fun = fn
dict, {:cont, {key, value}} -> module.put(dict, key, value)
Expand All @@ -315,7 +315,7 @@ defimpl Inspect, for: HashDict do

def inspect(dict, opts) do
# Avoid warnings about HashDict being deprecated.
module = String.to_atom("HashDict")
module = String.to_unsafe_atom("HashDict")
concat(["#HashDict<", Inspect.List.inspect(module.to_list(dict), opts), ">"])
end
end
10 changes: 5 additions & 5 deletions lib/elixir/lib/hash_set.ex
Original file line number Diff line number Diff line change
Expand Up @@ -279,19 +279,19 @@ defimpl Enumerable, for: HashSet do

def reduce(set, acc, fun) do
# Avoid warnings about HashSet being deprecated.
module = String.to_atom("HashSet")
module = String.to_unsafe_atom("HashSet")
module.reduce(set, acc, fun)
end

def member?(set, term) do
# Avoid warnings about HashSet being deprecated.
module = String.to_atom("HashSet")
module = String.to_unsafe_atom("HashSet")
{:ok, module.member?(set, term)}
end

def count(set) do
# Avoid warnings about HashSet being deprecated.
module = String.to_atom("HashSet")
module = String.to_unsafe_atom("HashSet")
{:ok, module.size(set)}
end

Expand All @@ -306,7 +306,7 @@ defimpl Collectable, for: HashSet do

def into(original) do
# Avoid warnings about HashSet being deprecated.
module = String.to_atom("HashSet")
module = String.to_unsafe_atom("HashSet")

collector_fun = fn
set, {:cont, term} -> module.put(set, term)
Expand All @@ -325,7 +325,7 @@ defimpl Inspect, for: HashSet do

def inspect(set, opts) do
# Avoid warnings about HashSet being deprecated.
module = String.to_atom("HashSet")
module = String.to_unsafe_atom("HashSet")
concat(["#HashSet<", Inspect.List.inspect(module.to_list(set), opts), ">"])
end
end
10 changes: 5 additions & 5 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5248,12 +5248,12 @@ defmodule Kernel do
# defmodule Alias nested
defp alias_defmodule({:__aliases__, _, [h | t]}, _module, env) when is_atom(h) do
module = :elixir_aliases.concat([env.module, h])
alias = String.to_atom("Elixir." <> Atom.to_string(h))
alias = String.to_unsafe_atom("Elixir." <> Atom.to_string(h))
opts = [as: alias, warn: false]

case t do
[] -> {module, module, opts}
_ -> {String.to_atom(Enum.join([module | t], ".")), module, opts}
_ -> {String.to_unsafe_atom(Enum.join([module | t], ".")), module, opts}
end
end

Expand Down Expand Up @@ -6897,7 +6897,7 @@ defmodule Kernel do
defp maybe_atomize_calendar(<<alias, _::binary>> = last_part, string)
when alias >= ?A and alias <= ?Z do
string = binary_part(string, 0, byte_size(string) - byte_size(last_part) - 1)
{String.to_atom("Elixir." <> last_part), string}
{String.to_unsafe_atom("Elixir." <> last_part), string}
end

defp maybe_atomize_calendar(_last_part, string) do
Expand Down Expand Up @@ -7008,7 +7008,7 @@ defmodule Kernel do

case mod do
?s -> parts
?a -> :lists.map(&String.to_atom/1, parts)
?a -> :lists.map(&String.to_unsafe_atom/1, parts)
?c -> :lists.map(&String.to_charlist/1, parts)
end

Expand All @@ -7017,7 +7017,7 @@ defmodule Kernel do

case mod do
?s -> parts
?a -> quote(do: :lists.map(&String.to_atom/1, unquote(parts)))
?a -> quote(do: :lists.map(&String.to_unsafe_atom/1, unquote(parts)))
?c -> quote(do: :lists.map(&String.to_charlist/1, unquote(parts)))
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/kernel/cli.ex
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ defmodule Kernel.CLI do
end

defp process_command({:rpc_eval, node, expr}, _config) when is_list(expr) do
node = List.to_atom(node)
node = List.to_unsafe_atom(node)

# Explicitly connect the node in case the rpc node was started with --sname/--name undefined.
_ = :net_kernel.connect_node(node)
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/kernel/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ defmodule Kernel.Utils do
{new_var, acc}

%{} ->
generated = String.to_atom("arg" <> Integer.to_string(map_size(acc) + 1))
generated = String.to_unsafe_atom("arg" <> Integer.to_string(map_size(acc) + 1))
new_var = Macro.unique_var(generated, module)
{new_var, Map.put(acc, pair, {new_var, var})}
end
Expand Down
13 changes: 9 additions & 4 deletions lib/elixir/lib/list.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1024,6 +1024,11 @@ defmodule List do
:lists.suffix(suffix, list)
end

@doc deprecated: "Use to_existing_atom/1 or to_unsafe_atom/1 instead"
def to_atom(charlist) do
:erlang.list_to_atom(charlist)
end

@doc """
Converts a charlist to an atom.

Expand All @@ -1034,15 +1039,15 @@ defmodule List do

## Examples

iex> List.to_atom(~c"Elixir")
iex> List.to_unsafe_atom(~c"Elixir")
:Elixir

iex> List.to_atom(~c"🌢 Elixir")
iex> List.to_unsafe_atom(~c"🌢 Elixir")
:"🌢 Elixir"

"""
@spec to_atom(charlist) :: atom
def to_atom(charlist) do
@spec to_unsafe_atom(charlist) :: atom
def to_unsafe_atom(charlist) do
:erlang.list_to_atom(charlist)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/macro.ex
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ defmodule Macro do

defp generate_arguments(amount, context, fun)
when is_integer(amount) and amount > 0 and is_atom(context) do
for id <- 1..amount, do: fun.(String.to_atom("arg" <> Integer.to_string(id)), context)
for id <- 1..amount, do: fun.(String.to_unsafe_atom("arg" <> Integer.to_string(id)), context)
end

@doc """
Expand Down
4 changes: 2 additions & 2 deletions lib/elixir/lib/module.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1148,7 +1148,7 @@ defmodule Module do
defp simplify_var(var, guess_priority) do
case Atom.to_string(var) do
"_" -> {:_, [], guess_priority}
"_" <> rest -> {String.to_atom(rest), [], guess_priority}
"_" <> rest -> {String.to_unsafe_atom(rest), [], guess_priority}
_ -> {var, [], nil}
end
end
Expand All @@ -1159,7 +1159,7 @@ defmodule Module do
rescue
ArgumentError -> module
else
module_name -> String.to_atom(Macro.underscore(List.last(module_name)))
module_name -> String.to_unsafe_atom(Macro.underscore(List.last(module_name)))
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/module/types/descr.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1237,7 +1237,7 @@ defmodule Module.Types.Descr do
"Elixir." <> segments ->
segments
|> String.split(".")
|> Enum.map(&String.to_atom/1)
|> Enum.map(&String.to_unsafe_atom/1)
end

{:__aliases__, [], segments}
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/option_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ defmodule OptionParser do
|> to_existing_key(allow_nonexistent_atoms?)
end

defp to_existing_key(option, true), do: String.to_atom(option)
defp to_existing_key(option, true), do: String.to_unsafe_atom(option)

defp to_existing_key(option, false) do
try do
Expand Down
6 changes: 4 additions & 2 deletions lib/elixir/lib/protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,9 @@ defmodule Protocol do
type_args = :lists.map(fn _ -> quote(do: term) end, :lists.seq(2, arity))
type_args = [quote(do: t) | type_args]

to_var = fn pos -> Macro.var(String.to_atom("arg" <> Integer.to_string(pos)), __MODULE__) end
to_var = fn pos ->
Macro.var(String.to_unsafe_atom("arg" <> Integer.to_string(pos)), __MODULE__)
end

call_args = :lists.map(to_var, :lists.seq(2, arity))
call_args = [quote(do: term) | call_args]
Expand Down Expand Up @@ -1228,7 +1230,7 @@ defmodule Protocol do
end

def __concat__(left, right) when is_binary(right) do
String.to_atom(ensure_prefix(Atom.to_string(left)) <> "." <> right)
String.to_unsafe_atom(ensure_prefix(Atom.to_string(left)) <> "." <> right)
end

defp ensure_prefix("Elixir." <> _ = left), do: left
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/record/extractor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule Record.Extractor do
defp from_lib_file(file) do
[app | path] = :filename.split(String.to_charlist(file))

case :code.lib_dir(List.to_atom(app)) do
case :code.lib_dir(List.to_unsafe_atom(app)) do
{:error, _} ->
raise ArgumentError, "lib file #{file} could not be found"

Expand Down
11 changes: 8 additions & 3 deletions lib/elixir/lib/string.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,11 @@ defmodule String do
end
end

@doc deprecated: "Use to_existing_atom/1 or to_unsafe_atom/1 instead"
def to_atom(string) when is_binary(string) do
:erlang.binary_to_atom(string, :utf8)
end

@doc """
Converts a string to an existing atom or creates a new one.

Expand All @@ -2979,12 +2984,12 @@ defmodule String do

## Examples

iex> String.to_atom("my_atom")
iex> String.to_unsafe_atom("my_atom")
:my_atom

"""
@spec to_atom(String.t()) :: atom
def to_atom(string) when is_binary(string) do
@spec to_unsafe_atom(String.t()) :: atom
def to_unsafe_atom(string) when is_binary(string) do
:erlang.binary_to_atom(string, :utf8)
end

Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/lib/uri.ex
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ defmodule URI do

{{key, value}, rest} ->
# Avoid warnings about Dict being deprecated
dict_module = String.to_atom("Dict")
dict_module = String.to_unsafe_atom("Dict")
decode_query_into_dict(rest, dict_module.put(dict, key, value), encoding)
end
end
Expand Down
6 changes: 3 additions & 3 deletions lib/elixir/pages/anti-patterns/code-anti-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ Picture yourself implementing code that converts string values into atoms. These
```elixir
defmodule MyRequestHandler do
def parse(%{"status" => status, "message" => message} = _payload) do
%{status: String.to_atom(status), message: message}
%{status: String.to_unsafe_atom(status), message: message}
end
end
```
Expand All @@ -165,11 +165,11 @@ iex> MyRequestHandler.parse(%{"status" => "ok", "message" => "all good"})
%{status: :ok, message: "all good"}
```

When we use the `String.to_atom/1` function to dynamically create an atom, it essentially gains potential access to create arbitrary atoms in our system, causing us to lose control over adhering to the limits established by the BEAM. This issue could be exploited by someone to create enough atoms to shut down a system.
When we use the `String.to_unsafe_atom/1` function to dynamically create an atom, it essentially gains potential access to create arbitrary atoms in our system, causing us to lose control over adhering to the limits established by the BEAM. This issue could be exploited by someone to create enough atoms to shut down a system.

#### Refactoring

To eliminate this anti-pattern, developers must either perform explicit conversions by mapping strings to atoms or replace the use of `String.to_atom/1` with `String.to_existing_atom/1`. An explicit conversion could be done as follows:
To eliminate this anti-pattern, developers must either perform explicit conversions by mapping strings to atoms or replace the use of `String.to_unsafe_atom/1` with `String.to_existing_atom/1`. An explicit conversion could be done as follows:

```elixir
defmodule MyRequestHandler do
Expand Down
Loading
Loading