diff options
author | Björn Gustavsson <[email protected]> | 2018-12-05 14:01:38 +0100 |
---|---|---|
committer | Björn Gustavsson <[email protected]> | 2018-12-05 14:07:04 +0100 |
commit | b3bd35ccd0004d59e478308df3bc85c351557f3c (patch) | |
tree | dd3c7bab42456c42404e9429c6c85b83a7c52aac /lib/compiler | |
parent | 8caf018e460ca1efbe0df41aa042cf1d25c62dd3 (diff) | |
download | otp-b3bd35ccd0004d59e478308df3bc85c351557f3c.tar.gz otp-b3bd35ccd0004d59e478308df3bc85c351557f3c.tar.bz2 otp-b3bd35ccd0004d59e478308df3bc85c351557f3c.zip |
Fix unsafe optimization of stack trace building
The `sys_core_fold` pass of the compiler would optimize
away the building of the stacktrace in code such as:
try
...
catch
C:R:Stk ->
erlang:raise(C, {R,Stk}, Stk)
end
That optimization is unsafe and would cause a crash in a later compiler
pass.
Diffstat (limited to 'lib/compiler')
-rw-r--r-- | lib/compiler/src/sys_core_fold.erl | 20 | ||||
-rw-r--r-- | lib/compiler/test/trycatch_SUITE.erl | 26 |
2 files changed, 39 insertions, 7 deletions
diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 3a65b40fa5..1681d97efb 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -2635,12 +2635,20 @@ opt_build_stacktrace(#c_let{vars=[#c_var{name=Cooked}], #c_call{module=#c_literal{val=erlang}, name=#c_literal{val=raise}, args=[Class,Exp,#c_var{name=Cooked}]} -> - %% The stacktrace is only used in a call to erlang:raise/3. - %% There is no need to build the stacktrace. Replace the - %% call to erlang:raise/3 with the the raw_raise/3 instruction, - %% which will use a raw stacktrace. - #c_primop{name=#c_literal{val=raw_raise}, - args=[Class,Exp,RawStk]}; + case core_lib:is_var_used(Cooked, #c_cons{hd=Class,tl=Exp}) of + true -> + %% Not safe. The stacktrace is used in the class or + %% reason. + Let; + false -> + %% The stacktrace is only used in the last + %% argument for erlang:raise/3. There is no need + %% to build the stacktrace. Replace the call to + %% erlang:raise/3 with the the raw_raise/3 + %% instruction, which will use a raw stacktrace. + #c_primop{name=#c_literal{val=raw_raise}, + args=[Class,Exp,RawStk]} + end; #c_let{vars=[#c_var{name=V}],arg=Arg,body=B0} when V =/= Cooked -> case core_lib:is_var_used(Cooked, Arg) of false -> diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl index 1b7ef4ddb0..8f9cd9ab1e 100644 --- a/lib/compiler/test/trycatch_SUITE.erl +++ b/lib/compiler/test/trycatch_SUITE.erl @@ -1189,7 +1189,8 @@ bad_raise(Expr) -> test_raise(Expr) -> test_raise_1(Expr), test_raise_2(Expr), - test_raise_3(Expr). + test_raise_3(Expr), + test_raise_4(Expr). test_raise_1(Expr) -> erase(exception), @@ -1263,5 +1264,28 @@ do_test_raise_3(Expr) -> erlang:raise(exit, {exception,C,E}, Stk) end. +test_raise_4(Expr) -> + try + do_test_raise_4(Expr) + catch + exit:{exception,C,E,Stk}:Stk -> + try + Expr() + catch + C:E:S -> + [StkTop|_] = S, + [StkTop|_] = Stk + end + end. + +do_test_raise_4(Expr) -> + try + Expr() + catch + C:E:Stk -> + %% Here the stacktrace must be built. + erlang:raise(exit, {exception,C,E,Stk}, Stk) + end. + id(I) -> I. |