From f8274691e80c7cd29d97c0e67b032ac56ed25496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= 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?= 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?= 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