File 2881-beam_validator-Improve-stack-frame-validation.patch of Package erlang
From 468839f127dc9f41bff14fd921c9666cb270eb2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20H=C3=B6gberg?= <john@erlang.org>
Date: Tue, 20 Dec 2022 15:38:05 +0100
Subject: [PATCH 1/2] beam_validator: Improve stack frame validation
---
lib/compiler/src/beam_validator.erl | 43 +++++++++++++++++++---
lib/compiler/test/beam_validator_SUITE.erl | 8 ++--
2 files changed, 42 insertions(+), 9 deletions(-)
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index b99bcd16ab..fe5b711edf 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -186,7 +186,7 @@ validate_0([{function, Name, Arity, Entry, Code} | Fs], Module, Level, Ft) ->
%%
%% Note that this may be 0 if there's a frame without saved values,
%% such as on a body-recursive call.
- numy=none :: none | undecided | index(),
+ numy=none :: none | {undecided,index()} | index(),
%% Available heap size.
h=0,
%% Available heap size for funs (aka lambdas).
@@ -2996,7 +2996,7 @@ merge_states_1(StA, StB, Counter0) ->
RecvSt = merge_receive_state(RecvStA, RecvStB),
MsPos = merge_ms_positions(MsPosA, MsPosB, Vs),
Fragile = merge_fragility(FragA, FragB),
- NumY = merge_stk(NumYA, NumYB),
+ NumY = merge_stk(YsA, YsB, NumYA, NumYB),
Ct = merge_ct(CtA, CtB),
St = #st{xs=Xs,ys=Ys,vs=Vs,fragile=Fragile,numy=NumY,
@@ -3134,8 +3134,38 @@ merge_ms_positions_1([], _MsPosA, _MsPosB, _Vs, Acc) ->
merge_receive_state(Same, Same) -> Same;
merge_receive_state(_, _) -> undecided.
-merge_stk(S, S) -> S;
-merge_stk(_, _) -> undecided.
+merge_stk(_, _, S, S) ->
+ S;
+merge_stk(YsA, YsB, StkA, StkB) ->
+ merge_stk_undecided(YsA, YsB, StkA, StkB).
+
+merge_stk_undecided(YsA, YsB, {undecided, StkA}, {undecided, StkB}) ->
+ %% We're merging two branches with different stack sizes. This is only okay
+ %% if we're about to throw an exception, in which case all Y registers must
+ %% be initialized on both paths.
+ ok = merge_stk_verify_init(StkA - 1, YsA),
+ ok = merge_stk_verify_init(StkB - 1, YsB),
+
+ {undecided, min(StkA, StkB)};
+merge_stk_undecided(YsA, YsB, StkA, StkB) when is_integer(StkA) ->
+ merge_stk_undecided(YsA, YsB, {undecided, StkA}, StkB);
+merge_stk_undecided(YsA, YsB, StkA, StkB) when is_integer(StkB) ->
+ merge_stk_undecided(YsA, YsB, StkA, {undecided, StkB});
+merge_stk_undecided(YsA, YsB, none, StkB) ->
+ merge_stk_undecided(YsA, YsB, {undecided, 0}, StkB);
+merge_stk_undecided(YsA, YsB, StkA, none) ->
+ merge_stk_undecided(YsA, YsB, StkA, {undecided, 0}).
+
+merge_stk_verify_init(-1, _Ys) ->
+ ok;
+merge_stk_verify_init(Y, Ys) ->
+ Reg = {y, Y},
+ case Ys of
+ #{ Reg := TagOrVRef } when TagOrVRef =/= uninitialized ->
+ merge_stk_verify_init(Y - 1, Ys);
+ #{} ->
+ error({unsafe_stack, Reg, Ys})
+ end.
merge_ct(S, S) -> S;
merge_ct(Ct0, Ct1) -> merge_ct_1(Ct0, Ct1).
@@ -3154,8 +3184,9 @@ verify_y_init(#vst{current=#st{numy=NumY,ys=Ys}}=Vst) when is_integer(NumY) ->
true = NumY > HighestY, %Assertion.
verify_y_init_1(NumY - 1, Vst),
ok;
-verify_y_init(#vst{current=#st{numy=undecided,ys=Ys}}=Vst) ->
+verify_y_init(#vst{current=#st{numy={undecided,MinSlots},ys=Ys}}=Vst) ->
HighestY = maps:fold(fun({y,Y}, _, Acc) -> max(Y, Acc) end, -1, Ys),
+ true = MinSlots > HighestY, %Assertion.
verify_y_init_1(HighestY, Vst);
verify_y_init(#vst{}) ->
ok.
@@ -3188,7 +3219,7 @@ verify_live_1(X, Vst) when is_integer(X) ->
verify_no_ct(#vst{current=#st{numy=none}}) ->
ok;
-verify_no_ct(#vst{current=#st{numy=undecided}}) ->
+verify_no_ct(#vst{current=#st{numy={undecided,_}}}) ->
error(unknown_size_of_stackframe);
verify_no_ct(#vst{current=St}=Vst) ->
case collect_try_catch_tags(St#st.numy - 1, Vst, []) of
diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl
index 8cbc5e9d7a..5a00a9ecf7 100644
--- a/lib/compiler/test/beam_validator_SUITE.erl
+++ b/lib/compiler/test/beam_validator_SUITE.erl
@@ -182,9 +182,11 @@ call_without_stack(Config) when is_list(Config) ->
merge_undefined(Config) when is_list(Config) ->
Errors = do_val(merge_undefined, Config),
[{{t,undecided,2},
- {{call_ext,2,{extfunc,debug,filter,2}},
- 22,
- {allocated,undecided}}},
+ {{label,11},
+ 19,
+ {unsafe_stack,{y,1},
+ #{{y,0} := uninitialized,
+ {y,1} := uninitialized}}}},
{{t,uninitialized,2},
{{call_ext,2,{extfunc,io,format,2}},
17,
--
2.35.3