From 407a1cb17cfb0ba0b71e708c6e82d3f72fcdc981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 8 Mar 2024 12:56:15 +0100 Subject: [PATCH] Make type pass realize that optimization is safe Consider: f(<<_:8>>) -> ok; f(A) -> try << (ok) || <<_:ok>> <= A>> after ok end. The type analysis pass would falsely assume that the binary generator `A` would always fail, which would cause the generation of unsafe code. --- lib/compiler/src/beam_ssa_opt.erl | 2 ++ lib/compiler/src/beam_ssa_type.erl | 4 ++-- lib/compiler/test/bs_match_SUITE.erl | 27 +++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index a17dd8c8d3cc..9a80933aca99 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -1660,6 +1660,8 @@ live_opt_is([#b_set{op={succeeded,guard},dst=SuccDst,args=[Dst]}=SuccI, I = I0#b_set{op={bif,is_tuple},dst=SuccDst}, live_opt_is([I|Is], Live0, Acc); bs_start_match -> + %% This is safe in Erlang/OTP 27 and later, because match + %% contexts are now mutable sub binaries. [#b_literal{val=new},Bin] = I0#b_set.args, I = I0#b_set{op={bif,is_bitstring},args=[Bin],dst=SuccDst}, live_opt_is([I|Is], Live0, Acc); diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index a83342569370..b40fedcac76a 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -2745,8 +2745,8 @@ infer_type({bif,is_atom}, [#b_var{}=Arg], _Ts, _Ds) -> infer_type({bif,is_binary}, [#b_var{}=Arg], _Ts, _Ds) -> T = {Arg, #t_bitstring{size_unit=8}}, {[T], [T]}; -infer_type({bif,is_bitstring}, [#b_var{}=Arg], _Ts, _Ds) -> - T = {Arg, #t_bitstring{}}, +infer_type({bif,is_bitstring}, [#b_var{}=Arg], Ts, _Ds) -> + T = {Arg, beam_types:meet(concrete_type(Arg, Ts), #t_bs_matchable{})}, {[T], [T]}; infer_type({bif,is_boolean}, [#b_var{}=Arg], _Ts, _Ds) -> T = {Arg, beam_types:make_boolean()}, diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl index 5641f86d10a5..0e224951c8b4 100644 --- a/lib/compiler/test/bs_match_SUITE.erl +++ b/lib/compiler/test/bs_match_SUITE.erl @@ -55,7 +55,8 @@ trim_bs_start_match_resume/1, gh_6410/1,bs_match/1, binary_aliases/1,gh_6923/1, - bs_test_tail/1]). + bs_test_tail/1, + otp_19019/1]). -export([coverage_id/1,coverage_external_ignore/2]). @@ -98,7 +99,8 @@ groups() -> trim_bs_start_match_resume, gh_6410,bs_match,binary_aliases, gh_6923, - bs_test_tail]}]. + bs_test_tail, + otp_19019]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -3255,6 +3257,27 @@ bs_test_tail_float(<<>>) -> []. bs_test_partial_tail(<<0:8, T/binary>>) -> bs_test_partial_tail(T); bs_test_partial_tail(<<>>) -> ok. +otp_19019(_Config) -> + ok = do_otp_19019(id(<<42>>)), + <<>> = do_otp_19019(id(<<>>)), + + ok. + +do_otp_19019(<<_:8>>) -> + ok; +do_otp_19019(A) -> + try + %% The `bs_start_match` instruction would be replaced with an + %% `is_bitstring/1` test, which is in Erlang/OTP 27 and later is + %% safe even if `A` is a match context. However, the type analysis + %% pass would assume that the `is_bitstring/1` test would always + %% fail. + << (ok) || <<_:ok>> <= A>> + after + ok + end. + + %%% Utilities. id(I) -> I.