From f8274691e80c7cd29d97c0e67b032ac56ed25496 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= <egil@erlang.org>
Date: Mon, 31 Aug 2015 14:52:48 +0200
Subject: compiler: Add test for corrupt register in get_map_elements

---
 lib/compiler/test/map_SUITE.erl | 58 ++++++++++++++++++++++++++++++++++++++---
 1 file changed, 55 insertions(+), 3 deletions(-)

diff --git a/lib/compiler/test/map_SUITE.erl b/lib/compiler/test/map_SUITE.erl
index abc12a359d..411b15eebe 100644
--- a/lib/compiler/test/map_SUITE.erl
+++ b/lib/compiler/test/map_SUITE.erl
@@ -63,7 +63,10 @@
 	%% errors in 17.0-rc1
 	t_update_values/1,
         t_expand_map_update/1,
-        t_export/1
+        t_export/1,
+
+	%% errors in 18
+        t_register_corruption/1
     ]).
 
 suite() -> [].
@@ -108,11 +111,13 @@ all() ->
 	t_build_and_match_nil,
 	t_build_and_match_structure,
 
-
 	%% errors in 17.0-rc1
 	t_update_values,
         t_expand_map_update,
-        t_export
+        t_export,
+
+	%% errors in 18
+        t_register_corruption
     ].
 
 groups() -> [].
@@ -1827,6 +1832,53 @@ map_guard_sequence_mixed(K1,K2,M) ->
 	#{ K1 := 1, c := 6, K2 := 8, h := 3} -> 6
     end.
 
+%% register corruption discovered in 18 due to
+%% get_map_elements might destroys registers when fail-label is taken.
+%% Only seen when patterns have two targets,
+%% specifically: we copy one register, and then jump.
+%%    {test,is_map,{f,5},[{x,1}]}.
+%%
+%%    {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
+%%    %% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7}
+%%
+%%    {move,{integer,1},{x,0}}.
+%%    {call_only,3,{f,10}}.
+%%
+%%  {label,7}.
+%%    {get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}.
+%%    %% {x,1} (src) is now corrupt
+%%
+%%    {move,{x,0},{x,1}}.
+%%    {move,{integer,2},{x,0}}.
+%%    {call_only,3,{f,10}}.
+%%
+%% Only happens in beam_block opt_move pass with two destinations.
+
+t_register_corruption(Config) when is_list(Config) ->
+    M = #{a=> <<"value">>, c=>3},
+    {3,wanted,<<"value">>} = register_corruption_bar(M,wanted),
+    {3,wanted,<<"value">>} = register_corruption_foo(wanted,M),
+    ok.
+
+register_corruption_foo(A,#{a := V1, b := V2}) ->
+    register_corruption_dummy_call(1,V1,V2);
+register_corruption_foo(A,#{b := V}) ->
+    register_corruption_dummy_call(2,A,V);
+register_corruption_foo(A,#{a := V}) ->
+    register_corruption_dummy_call(3,A,V).
+
+register_corruption_bar(M,A) ->
+    case M of
+        #{a := V1, b := V2} ->
+            register_corruption_dummy_call(1,V1,V2);
+        #{b := V} ->
+            register_corruption_dummy_call(2,A,V);
+        #{a := V} ->
+            register_corruption_dummy_call(3,A,V)
+    end.
+
+
+register_corruption_dummy_call(A,B,C) -> {A,B,C}.
 
 
 t_frequency_table(Config) when is_list(Config) ->
-- 
cgit v1.2.3


From c9bbba0db169ece606b02162057e4681b8fb1ce4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= <egil@erlang.org>
Date: Mon, 31 Aug 2015 16:07:02 +0200
Subject: compiler: Add extra checks for get_map_elements in validator

---
 lib/compiler/src/beam_validator.erl | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 942d69a756..6004f1974e 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -758,10 +758,20 @@ valfun_4(_, _) ->
 
 verify_get_map(Fail, Src, List, Vst0) ->
     assert_type(map, Src, Vst0),
-    Vst1 = branch_state(Fail, Vst0),
+    Vst1 = foldl(fun(D, Vsti) ->
+                         case is_reg_defined(D,Vsti) of
+                             true -> set_type_reg(term,D,Vsti);
+                             false -> Vsti
+                         end
+                 end, Vst0, extract_map_vals(List)),
+    Vst2 = branch_state(Fail, Vst1),
     Keys = extract_map_keys(List),
     assert_unique_map_keys(Keys),
-    verify_get_map_pair(List,Vst0,Vst1).
+    verify_get_map_pair(List,Vst0,Vst2).
+
+extract_map_vals([_Key,Val|T]) ->
+    [Val|extract_map_vals(T)];
+extract_map_vals([]) -> [].
 
 extract_map_keys([Key,_Val|T]) ->
     [Key|extract_map_keys(T)];
@@ -1093,6 +1103,17 @@ set_catch_end({y,Y}, #vst{current=#st{y=Ys0}=St}=Vst) ->
     Ys = gb_trees:update(Y, initialized, Ys0),
     Vst#vst{current=St#st{y=Ys}}.
 
+
+is_reg_defined({x,_}=Reg, Vst) -> is_type_defined_x(Reg, Vst);
+is_reg_defined({y,_}=Reg, Vst) -> is_type_defined_y(Reg, Vst);
+is_reg_defined(V, #vst{}) -> error({not_a_register, V}).
+
+is_type_defined_x({x,X}, #vst{current=#st{x=Xs}}) ->
+    gb_trees:is_defined(X,Xs).
+
+is_type_defined_y({y,Y}, #vst{current=#st{y=Ys}}) ->
+    gb_trees:is_defined(Y,Ys).
+
 assert_term(Src, Vst) ->
     get_term_type(Src, Vst),
     ok.
-- 
cgit v1.2.3


From d0784035abb22f4f385c8a8737a7b15c3741bbca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= <egil@erlang.org>
Date: Mon, 31 Aug 2015 16:07:46 +0200
Subject: compiler: Fix get_map_elements register corruption

Instruction get_map_elements might destroy target registers when the fail-label is taken.
Only seen for patterns with two, and only two, target registers.
Specifically: we copy one register, and then jump.

    foo(A,#{a := V1, b := V2}) -> ...
    foo(A,#{b := V}) -> ...

call foo(value, #{a=>whops, c=>42}).

corresponding assembler:

    {test,is_map,{f,5},[{x,1}]}.

    {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}.
    %% if 'a' exists but not 'b' {x,1} is overwritten, jump {f,7}

    {move,{integer,1},{x,0}}.
    {call_only,3,{f,10}}.
    {label,7}.

    {get_map_elements,{f,8},{x,1},{list,[{atom,b},{x,2}]}}.
    %% {x,1} (src) is read with a corrupt value

    {move,{x,0},{x,1}}.
    {move,{integer,2},{x,0}}.
    {call_only,3,{f,10}}.

The fix is to remove 'opt_moves' pass for get_map_elements instruction
in the case of two or more destinations.

Reported-by: Valery Tikhonov
---
 lib/compiler/src/beam_block.erl | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index 2def3de7f3..0321b1c07b 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -251,7 +251,9 @@ opt([{set,_,_,{line,_}}=Line1,
      {set,[D2],[{integer,Idx2},Reg],{bif,element,{f,0}}}=I2|Is])
   when Idx1 < Idx2, D1 =/= D2, D1 =/= Reg, D2 =/= Reg ->
     opt([Line2,I2,Line1,I1|Is]);
-opt([{set,Ds0,Ss,Op}|Is0]) ->	
+opt([{set,[_|_],_Ss,{get_map_elements,_F}}=I|Is]) ->
+    [I|opt(Is)];
+opt([{set,Ds0,Ss,Op}|Is0]) ->
     {Ds,Is} = opt_moves(Ds0, Is0),
     [{set,Ds,Ss,Op}|opt(Is)];
 opt([{'%live',_,_}=I|Is]) ->
-- 
cgit v1.2.3