summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorErlang/OTP <otp@erlang.org>2023-05-04 17:25:03 +0200
committerErlang/OTP <otp@erlang.org>2023-05-04 17:25:03 +0200
commit5612d3d5fedda6822f7d08beacdecc12993cbf0d (patch)
treec49fb6cddbb536fe3c6607dae6c4778726a13e28
parentc487c0ea418315edd4a8614854e82c5359f4b8f8 (diff)
parentf8c3c6f9e4783f77f3010c4f57d009a27c6af67a (diff)
downloaderlang-5612d3d5fedda6822f7d08beacdecc12993cbf0d.tar.gz
Merge branch 'rickard/creation-fix/25.3.1/OTP-18570' into maint-25
* rickard/creation-fix/25.3.1/OTP-18570: [erts] ensure no mix of external and internal identifiers
-rw-r--r--erts/emulator/beam/dist.c13
-rw-r--r--erts/emulator/beam/erl_node_tables.c12
-rw-r--r--erts/emulator/beam/erl_node_tables.h8
-rw-r--r--erts/emulator/test/distribution_SUITE.erl127
4 files changed, 155 insertions, 5 deletions
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index 9a760fd97f..1ed152fccb 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -4906,6 +4906,19 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
success = (!ERTS_PROC_IS_EXITING(net_kernel)
& !ERTS_PROC_GET_DIST_ENTRY(net_kernel));
if (success) {
+ /*
+ * Ensure we don't use a nodename-creation pair with
+ * external identifiers existing in the system.
+ */
+ while (!0) {
+ ErlNode *nep;
+ if (creation < 4)
+ creation = 4;
+ nep = erts_find_node(BIF_ARG_1, creation);
+ if (!nep || erts_node_refc(nep) == 0)
+ break;
+ creation++;
+ }
inc_no_nodes();
erts_set_this_node(BIF_ARG_1, (Uint32) creation);
erts_this_dist_entry->creation = creation;
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 2bec8ff20e..c2d9b0967d 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -885,6 +885,18 @@ erts_node_table_info(fmtfn_t to, void *to_arg)
erts_rwmtx_runlock(&erts_node_table_rwmtx);
}
+ErlNode *erts_find_node(Eterm sysname, Uint32 creation)
+{
+ ErlNode *res;
+ ErlNode ne;
+ ne.sysname = sysname;
+ ne.creation = creation;
+
+ erts_rwmtx_rlock(&erts_node_table_rwmtx);
+ res = hash_get(&erts_node_table, (void *) &ne);
+ erts_rwmtx_runlock(&erts_node_table_rwmtx);
+ return res;
+}
ErlNode *erts_find_or_insert_node(Eterm sysname, Uint32 creation, Eterm book)
{
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index c12198a23c..56696586c6 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -255,6 +255,7 @@ void erts_set_dist_entry_not_connected(DistEntry *);
void erts_set_dist_entry_pending(DistEntry *);
void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint64);
ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
+ErlNode *erts_find_node(Eterm, Uint32);
void erts_schedule_delete_node(ErlNode *);
void erts_set_this_node(Eterm, Uint32);
Uint erts_node_table_size(void);
@@ -282,6 +283,7 @@ ERTS_GLB_INLINE void erts_deref_node_entry__(ErlNode *np, Eterm term, char *file
ERTS_GLB_INLINE erts_aint_t erts_ref_node_entry(ErlNode *np, int min_val, Eterm term);
ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np, Eterm term);
#endif
+ERTS_GLB_INLINE erts_aint_t erts_node_refc(ErlNode *np);
ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep);
ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep);
@@ -332,6 +334,12 @@ erts_deref_node_entry(ErlNode *np, Eterm term)
erts_schedule_delete_node(np);
}
+ERTS_GLB_INLINE erts_aint_t
+erts_node_refc(ErlNode *np)
+{
+ return erts_refc_read(&np->refc, 0);
+}
+
#endif
ERTS_GLB_INLINE void
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index c1a31c6abf..ceedd26eb8 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -84,7 +84,9 @@
dyn_node_name_monitor/1,
async_dist_flag/1,
async_dist_port_dctrlr/1,
- async_dist_proc_dctrlr/1]).
+ async_dist_proc_dctrlr/1,
+ creation_selection/1,
+ creation_selection_test/1]).
%% Internal exports.
-export([sender/3, receiver2/2, dummy_waiter/0, dead_process/0,
@@ -118,7 +120,7 @@ all() ->
start_epmd_false, no_epmd, epmd_module, system_limit,
hopefull_data_encoding, hopefull_export_fun_bug,
huge_iovec, is_alive, dyn_node_name_monitor_node, dyn_node_name_monitor,
- {group, async_dist}].
+ {group, async_dist}, creation_selection].
groups() ->
[{bulk_send, [], [bulk_send_small, bulk_send_big, bulk_send_bigbig]},
@@ -520,9 +522,6 @@ nodes2(Config) when is_list(Config) ->
end,
ok.
-id(X) ->
- X.
-
%% Test optimistic distribution flags toward pending connections (DFLAG_DIST_HOPEFULLY)
optimistic_dflags(Config) when is_list(Config) ->
{ok, PeerSender, _Sender} = ?CT_PEER(#{connection => 0, args => ["-setcookie", "NONE"]}),
@@ -3594,8 +3593,126 @@ async_dist_test(Node) ->
ok.
+creation_selection(Config) when is_list(Config) ->
+ register(creation_selection_test_supervisor, self()),
+ Name = atom_to_list(?FUNCTION_NAME) ++ "-"
+ ++ integer_to_list(erlang:system_time()),
+ Host = hostname(),
+ Cmd = lists:append(
+ [ct:get_progname(),
+ " -noshell",
+ " -setcookie ", atom_to_list(erlang:get_cookie()),
+ " -pa ", filename:dirname(code:which(?MODULE)),
+ " -s ", atom_to_list(?MODULE), " ",
+ " creation_selection_test ", atom_to_list(node()), " ",
+ atom_to_list(net_kernel:longnames()), " ", Name, " ", Host]),
+ ct:pal("Node command: ~p~n", [Cmd]),
+ Port = open_port({spawn, Cmd}, [exit_status]),
+ Node = list_to_atom(lists:append([Name, "@", Host])),
+ ok = receive_creation_selection_info(Port, Node).
+
+receive_creation_selection_info(Port, Node) ->
+ receive
+ {creation_selection_test, Node, Creations, InvalidCreation,
+ ClashResolvedCreation} = Msg ->
+ ct:log("Test result: ~p~n", [Msg]),
+ %% Verify that creation values are created as expected. The
+ %% list of creations is in reverse start order...
+ MaxC = (1 bsl 32) - 1,
+ MinC = 4,
+ StartOrderCreations = lists:reverse(Creations),
+ InvalidCreation = lists:foldl(fun (C, C) when is_integer(C),
+ MinC =< C,
+ C =< MaxC ->
+ %% Return next expected
+ %% creation...
+ if C == MaxC -> MinC;
+ true -> C+1
+ end
+ end,
+ hd(StartOrderCreations),
+ StartOrderCreations),
+ false = lists:member(ClashResolvedCreation, [InvalidCreation
+ | Creations]),
+ receive
+ {Port, {exit_status, 0}} ->
+ Port ! {self(), close},
+ ok;
+ {Port, {exit_status, EStat}} ->
+ ct:fail({"node exited abnormally: ", EStat})
+ end;
+ {Port, {exit_status, EStat}} ->
+ ct:fail({"node prematurely exited: ", EStat});
+ {Port, {data, Data}} ->
+ ct:log("~ts", [Data]),
+ receive_creation_selection_info(Port, Node)
+ end,
+ ok.
+
+creation_selection_test([TestSupNode, LongNames, Name, Host]) ->
+ try
+ StartArgs = [Name,
+ case LongNames of
+ true -> longnames;
+ false -> shortnames
+ end],
+ Node = list_to_atom(lists:append([atom_to_list(Name),
+ "@", atom_to_list(Host)])),
+ GoDistributed = fun (F) ->
+ {ok, _} = net_kernel:start(StartArgs),
+ Node = node(),
+ Creation = erlang:system_info(creation),
+ _ = F(Creation),
+ net_kernel:stop(),
+ Creation
+ end,
+ %% We start multiple times to verify that the creation values
+ %% we get from epmd are delivered in sequence. This is a
+ %% must for the test case such as it is written now, but can be
+ %% changed. If changed, this test case must be updated...
+ {Creations,
+ LastCreation} = lists:foldl(fun (_, {Cs, _LC}) ->
+ CFun = fun (X) -> X end,
+ C = GoDistributed(CFun),
+ {[C|Cs], C}
+ end, {[], 0}, lists:seq(1, 5)),
+ %% We create a pid with the creation that epmd will offer us the next
+ %% time we start the distribution and then start the distribution
+ %% once more. The node should avoid this creation, since this would
+ %% cause external identifiers in the system with same
+ %% nodename/creation pair as used by the local node, which in turn
+ %% would cause these identifers not to work as expected. That is, the
+ %% node should silently reject this creation and chose another one when
+ %% starting the distribution.
+ InvalidCreation = LastCreation+1,
+ Pid = erts_test_utils:mk_ext_pid({Node, InvalidCreation}, 4711, 0),
+ true = erts_debug:size(Pid) > 0, %% External pid
+ ResultFun = fun (ClashResolvedCreation) ->
+ pong = net_adm:ping(TestSupNode),
+ Msg = {creation_selection_test, node(), Creations,
+ InvalidCreation, ClashResolvedCreation},
+ {creation_selection_test_supervisor, TestSupNode}
+ ! Msg,
+ %% Wait a bit so the message have time to get
+ %% through before we take down the distribution...
+ receive after 500 -> ok end
+ end,
+ _ = GoDistributed(ResultFun),
+ %% Ensure Pid is not garbage collected before starting the
+ %% distribution...
+ _ = id(Pid),
+ erlang:halt(0)
+ catch
+ Class:Reason:StackTrace ->
+ erlang:display({Class, Reason, StackTrace}),
+ erlang:halt(17)
+ end.
+
%%% Utilities
+id(X) ->
+ X.
+
wait_until(Fun) ->
wait_until(Fun, 24*60*60*1000).