File 0429-compiler-destructive-update-Fix-crash-caused-by-patc.patch of Package erlang
From d470827be095a3b2d24ddd1b2bc145d2cf82c14d Mon Sep 17 00:00:00 2001
From: Isabell Huang <isabell@erlang.org>
Date: Thu, 5 Jun 2025 13:49:34 +0200
Subject: [PATCH] compiler destructive update: Fix crash caused by patch order
Fix https://github.com/erlang/otp/issues/9903.
In `beam_ssa_destructive_update:patch_is/5`, OTP 28 uses
`lists:splitwith/2`, which may deliver a patch list where a more
specific patch is placed before a less specific patch. Therefore,
in the later `merge_patches/2`, the merging logic needs to be
commutative and takes care of all possible orders of patches.
---
.../src/beam_ssa_destructive_update.erl | 6 ++++++
.../tuple_inplace_checks.erl | 17 ++++++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/lib/compiler/src/beam_ssa_destructive_update.erl b/lib/compiler/src/beam_ssa_destructive_update.erl
index 01c2ffbde3..a84ae85b32 100644
--- a/lib/compiler/src/beam_ssa_destructive_update.erl
+++ b/lib/compiler/src/beam_ssa_destructive_update.erl
@@ -951,6 +951,8 @@ merge_arg_patches([{Idx,_Lit,E1}|Patches], Acc) ->
merge_arg_patches([], Acc) ->
Acc.
+%% Merge two patches. The merging should be commutative since we do not
+%% normalize patch order before merging.
merge_patches({tuple_element,I,E0,D0}, {tuple_element,I,E1,D1}) ->
{tuple_element, I, merge_patches(E0, E1), max(D0,D1)};
merge_patches({tuple_element,IA,EA,_}, {tuple_element,IB,EB,_}) ->
@@ -959,6 +961,10 @@ merge_patches({tuple_element,IA,EA,_}, {tuple_elements,Es}) ->
{tuple_elements,[{IA,EA}|Es]};
merge_patches({tuple_elements,Es}, {tuple_element,IA,EA,_}) ->
{tuple_elements,[{IA,EA}|Es]};
+merge_patches(Patch, {self,heap_tuple}) ->
+ %% If we find anything more specific than a heap_tuple, the more
+ %% specific patch subsumes the heap_tuple
+ Patch;
merge_patches({self,heap_tuple}, Other) ->
%% We're already patching this element in Other and as it will
%% force the term onto the heap, we can ignore the new patch.
diff --git a/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl b/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl
index b437566b78..74af653f5b 100644
--- a/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl
+++ b/lib/compiler/test/beam_ssa_check_SUITE_data/tuple_inplace_checks.erl
@@ -23,12 +23,13 @@
-export([do0a/0, do0b/2, different_sizes/2, ambiguous_inits/1,
update_record0/0, fc/0, track_update_record/1,
gh8124_a/0, gh8124_b/0, tuple_set_a/1, tuple_set_b/0,
- failure_to_patch_list/0, erierl1208/0]).
+ failure_to_patch_list/0, erierl1208/0, gh_9903/0]).
-record(r, {a=0,b=0,c=0,tot=0}).
-record(r1, {a}).
-record(r2, {b}).
-record(r3, {c}).
+-record(r4, {a=0,b= <<>>}).
do0a() ->
Ls = ex:f(),
@@ -251,6 +252,20 @@ erierl1208() ->
R1 = #r1{a=A=#r2{b=B}} = erierl1208_inner(),
R1#r1{a = A#r2{b= B#r3{c= <<"new value">>}}}.
+%% Check that the following code can't crash the compiler.
+gh_9903() ->
+ State = case lists:member(abc, []) of
+ true -> #r4{a=1};
+ false -> #r4{}
+ end,
+ gh_9903_inner1(<<>>, State).
+
+gh_9903_inner1(<<B/binary>>, S) ->
+ gh_9903_inner2(B, size(B), S#r4{a=1}).
+
+gh_9903_inner2(<<B1/binary>>, _, #r4{b=B2}) ->
+ <<B2/binary, B1/binary>>.
+
%% Example which provides a get_tuple_element instruction with a tuple
%% typed as a tuple set.
tuple_set_a(Something) ->
--
2.43.0