Skip to content

Commit 55e885f

Browse files
author
José Valim
committed
Ensure we don't leak erlang terms on failure, closes #2303
1 parent f92d839 commit 55e885f

File tree

5 files changed

+46
-18
lines changed

5 files changed

+46
-18
lines changed

lib/elixir/src/elixir_errors.erl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,18 @@ parse_error(Meta, File, <<"syntax error before: ">>, <<"'end'">>) ->
5555
raise(Meta, File, 'Elixir.SyntaxError', <<"unexpected token: end">>);
5656

5757
%% Binaries are wrapped in [<<...>>], so we need to unwrap them
58-
parse_error(Meta, File, Error, <<"[<<", Token/binary>>) when is_binary(Error) ->
59-
Rest = binary_part(Token, 0, byte_size(Token) - 3),
60-
Message = <<Error / binary, Rest / binary >>,
61-
raise(Meta, File, 'Elixir.SyntaxError', Message);
58+
parse_error(Meta, File, Error, <<"[", _/binary>> = Full) when is_binary(Error) ->
59+
Rest =
60+
case binary:split(Full, <<"<<">>) of
61+
[Lead, Token] ->
62+
case binary:split(Token, <<">>">>) of
63+
[Part, _] when Lead == <<$[>> -> Part;
64+
_ -> <<$">>
65+
end;
66+
[_] ->
67+
<<$">>
68+
end,
69+
raise(Meta, File, 'Elixir.SyntaxError', <<Error/binary, Rest/binary >>);
6270

6371
%% Everything else is fine as is
6472
parse_error(Meta, File, Error, Token) when is_binary(Error), is_binary(Token) ->

lib/elixir/src/elixir_parser.yrl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ Nonterminals
2323
.
2424

2525
Terminals
26-
identifier kw_identifier kw_identifier_string bracket_identifier
26+
identifier kw_identifier kw_identifier_safe kw_identifier_unsafe bracket_identifier
2727
paren_identifier do_identifier block_identifier
2828
fn 'end' aliases
29-
number signed_number atom atom_string bin_string list_string sigil
29+
number signed_number atom atom_safe atom_unsafe bin_string list_string sigil
3030
dot_call_op op_identifier
3131
comp_op at_op unary_op and_op or_op arrow_op match_op in_op in_match_op type_op
3232
dual_op add_op mult_op exp_op two_op pipe_op stab_op when_op assoc_op
@@ -208,7 +208,8 @@ access_expr -> max_expr : '$1'.
208208

209209
%% Aliases and properly formed calls. Used by map_expr.
210210
max_expr -> atom : ?exprs('$1').
211-
max_expr -> atom_string : build_atom_string('$1').
211+
max_expr -> atom_safe : build_quoted_atom('$1', true).
212+
max_expr -> atom_unsafe : build_quoted_atom('$1', false).
212213
max_expr -> parens_call call_args_parens : build_identifier('$1', '$2').
213214
max_expr -> parens_call call_args_parens call_args_parens : build_nested_parens('$1', '$2', '$3').
214215
max_expr -> dot_alias : '$1'.
@@ -428,8 +429,10 @@ call_args_parens -> open_paren call_args_parens_base ',' kw close_paren : revers
428429

429430
kw_eol -> kw_identifier : ?exprs('$1').
430431
kw_eol -> kw_identifier eol : ?exprs('$1').
431-
kw_eol -> kw_identifier_string : build_atom_string('$1').
432-
kw_eol -> kw_identifier_string eol : build_atom_string('$1').
432+
kw_eol -> kw_identifier_safe : build_quoted_atom('$1', true).
433+
kw_eol -> kw_identifier_safe eol : build_quoted_atom('$1', true).
434+
kw_eol -> kw_identifier_unsafe : build_quoted_atom('$1', false).
435+
kw_eol -> kw_identifier_unsafe eol : build_quoted_atom('$1', false).
433436

434437
kw_base -> kw_eol container_expr : [{'$1', '$2'}].
435438
kw_base -> kw_base ',' kw_eol container_expr : [{'$3', '$4'}|'$1'].
@@ -513,7 +516,6 @@ Erlang code.
513516
-define(line(Node), element(2, Node)).
514517
-define(exprs(Node), element(3, Node)).
515518
-define(rearrange_uop(Op), (Op == 'not' orelse Op == '!')).
516-
-define(is_atom_string(Atom), (Atom == atom_string orelse Atom == kw_identifier_string)).
517519

518520
%% The following directive is needed for (significantly) faster
519521
%% compilation of the generated .erl file by the HiPE compiler
@@ -633,9 +635,9 @@ build_list_string({list_string, Line, Args}) ->
633635
Meta = meta(Line),
634636
{{'.', Meta, ['Elixir.String', to_char_list]}, Meta, [{'<<>>', Meta, string_parts(Args)}]}.
635637

636-
build_atom_string({Atom, _Line, Safe, [H]}) when ?is_atom_string(Atom) andalso is_binary(H) ->
638+
build_quoted_atom({_, _Line, [H]}, Safe) when is_binary(H) ->
637639
Op = binary_to_atom_op(Safe), erlang:Op(H, utf8);
638-
build_atom_string({Atom, Line, Safe, Args}) when ?is_atom_string(Atom) ->
640+
build_quoted_atom({_, Line, Args}, Safe) ->
639641
Meta = meta(Line),
640642
{{'.', Meta, [erlang, binary_to_atom_op(Safe)]}, Meta, [{'<<>>', Meta, string_parts(Args)}, utf8]}.
641643

lib/elixir/src/elixir_tokenizer.erl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,11 @@ tokenize([$:,H|T] = Original, Line, Scope, Tokens) when ?is_quote(H) ->
238238
case elixir_interpolation:extract(Line, Scope, true, T, H) of
239239
{NewLine, Parts, Rest} ->
240240
Unescaped = unescape_tokens(Parts),
241-
ExistingAtomsOnly = Scope#elixir_tokenizer.existing_atoms_only,
242-
tokenize(Rest, NewLine, Scope, [{atom_string, Line, ExistingAtomsOnly, Unescaped}|Tokens]);
241+
Key = case Scope#elixir_tokenizer.existing_atoms_only of
242+
true -> atom_safe;
243+
false -> atom_unsafe
244+
end,
245+
tokenize(Rest, NewLine, Scope, [{Key, Line, Unescaped}|Tokens]);
243246
{error, Reason} ->
244247
interpolation_error(Reason, Original, Tokens, " (for atom starting at line ~B)", [Line])
245248
end;
@@ -504,8 +507,11 @@ handle_strings(T, Line, H, Scope, Tokens) ->
504507
interpolation_error(Reason, [H|T], Tokens, " (for string starting at line ~B)", [Line]);
505508
{NewLine, Parts, [$:|Rest]} when ?is_space(hd(Rest)) ->
506509
Unescaped = unescape_tokens(Parts),
507-
ExistingAtomsOnly = Scope#elixir_tokenizer.existing_atoms_only,
508-
tokenize(Rest, NewLine, Scope, [{kw_identifier_string, Line, ExistingAtomsOnly, Unescaped}|Tokens]);
510+
Key = case Scope#elixir_tokenizer.existing_atoms_only of
511+
true -> kw_identifier_safe;
512+
false -> kw_identifier_unsafe
513+
end,
514+
tokenize(Rest, NewLine, Scope, [{Key, Line, Unescaped}|Tokens]);
509515
{NewLine, Parts, Rest} ->
510516
Token = {string_type(H), Line, unescape_tokens(Parts)},
511517
tokenize(Rest, NewLine, Scope, [Token|Tokens])

lib/elixir/src/elixir_translator.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ translate({{'.', _, [Left, Right]}, Meta, Args}, S)
238238
{atom, Line, TRight}},
239239
{map_field_assoc, Line,
240240
{atom, Line, term},
241-
{atom, Line, TVar}}]},
241+
TVar}]},
242242

243243
%% TODO There is a bug in dialyzer that makes it fail on
244244
%% empty maps. We work around the bug below by using

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@ defmodule Kernel.ErrorsTest do
1515
'\end\nlol\nbarbecue'
1616
end
1717

18-
test :invalid_string do
18+
test :invalid_quoted_token do
1919
assert_compile_fail SyntaxError,
2020
"nofile:1: syntax error before: \"world\"",
2121
'"hello" "world"'
22+
23+
assert_compile_fail SyntaxError,
24+
"nofile:1: syntax error before: foo",
25+
'Foo.:foo'
26+
27+
assert_compile_fail SyntaxError,
28+
"nofile:1: syntax error before: \"foo\"",
29+
'Foo.:"foo\#{:bar}"'
30+
31+
assert_compile_fail SyntaxError,
32+
"nofile:1: syntax error before: \"",
33+
'Foo.:"\#{:bar}"'
2234
end
2335

2436
test :invalid_or_reserved_codepoint do

0 commit comments

Comments
 (0)