aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--OTP_VERSION2
-rw-r--r--erts/doc/src/notes.xml48
-rw-r--r--erts/emulator/beam/beam_load.c10
-rw-r--r--erts/emulator/beam/erl_bif_lists.c15
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c1
-rw-r--r--erts/emulator/beam/erl_process.h2
-rw-r--r--erts/emulator/nifs/common/socket_nif.c100
-rw-r--r--erts/emulator/test/socket_SUITE.erl481
-rw-r--r--erts/emulator/test/system_info_SUITE.erl53
-rw-r--r--erts/vsn.mk2
-rw-r--r--lib/compiler/src/beam_ssa_bsm.erl6
-rw-r--r--lib/compiler/src/beam_ssa_opt.erl66
-rw-r--r--lib/compiler/src/beam_ssa_pre_codegen.erl62
-rw-r--r--lib/compiler/test/beam_ssa_SUITE.erl26
-rw-r--r--lib/compiler/test/bs_match_SUITE.erl19
-rw-r--r--lib/compiler/test/match_SUITE.erl21
-rw-r--r--lib/dialyzer/doc/src/notes.xml16
-rw-r--r--lib/dialyzer/src/dialyzer.erl48
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl107
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/hipe/doc/src/hipe_app.xml4
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl41
-rw-r--r--lib/inets/doc/src/notes.xml18
-rw-r--r--lib/inets/src/http_client/httpc.erl8
-rw-r--r--lib/inets/test/httpc_SUITE.erl13
-rw-r--r--lib/inets/vsn.mk2
-rw-r--r--lib/ssl/doc/src/notes.xml31
-rw-r--r--lib/ssl/src/tls_connection.erl2
-rw-r--r--lib/ssl/src/tls_handshake_1_3.erl4
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl4
-rw-r--r--lib/ssl/vsn.mk2
-rw-r--r--lib/stdlib/test/lists_SUITE.erl11
-rw-r--r--make/otp_version_tickets16
-rw-r--r--otp_versions.table1
34 files changed, 982 insertions, 262 deletions
diff --git a/OTP_VERSION b/OTP_VERSION
index 758704512b..8869803b66 100644
--- a/OTP_VERSION
+++ b/OTP_VERSION
@@ -1 +1 @@
-22.0.4
+22.0.5
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 128def3934..13208fb7f6 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,54 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ An invalid value test caused the socket:setopt(Socket,
+ ip, add_membership, ip_mreq()) to fail with badarg. The
+ same for drop_membership.</p>
+ <p>
+ Own Id: OTP-15908 Aux Id: ERL-980 </p>
+ </item>
+ <item>
+ <p>
+ Fixed bug causing VM crash when doing textual dump of a
+ process containing an unhandled monitor down signal.
+ Textual process dumps can be done with
+ <c>erlang:system_info(procs)</c>, trace feature
+ <c>process_dump</c>, Erlang shell break menu and a
+ crashdump. Bug exist since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15909 Aux Id: ERL-979 </p>
+ </item>
+ <item>
+ <p><c>lists:subtract/2</c> would produce incorrect
+ results for some inputs on 64-bit platforms.</p>
+ <p>
+ Own Id: OTP-15938 Aux Id: ERL-986 </p>
+ </item>
+ <item>
+ <p>Fixed a bug in the loader that was similar to
+ <c>OTP-15938</c>, yielding incorrect code for some inputs
+ on 64-bit platforms.</p>
+ <p>
+ Own Id: OTP-15939</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug causing scheduler threads in rare cases to
+ block spinnning indefinitely. Bug exists since OTP 21.0.</p>
+ <p>
+ Own Id: OTP-15941 Aux Id: PR-2313 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.4.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index 941c3ebbbe..35f2ea6688 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -4612,7 +4612,15 @@ typedef struct SortGenOpArg {
static int
genopargtermcompare(SortGenOpArg* a, SortGenOpArg* b)
{
- return CMP_TERM(a->term, b->term);
+ Sint res = CMP_TERM(a->term, b->term);
+
+ if (res < 0) {
+ return -1;
+ } else if (res > 0) {
+ return 1;
+ }
+
+ return 0;
}
static int
diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c
index b23fa77f5f..fa2edfef1e 100644
--- a/erts/emulator/beam/erl_bif_lists.c
+++ b/erts/emulator/beam/erl_bif_lists.c
@@ -413,12 +413,25 @@ typedef struct {
#define ERTS_RBT_GET_LEFT(T) ((T)->left)
#define ERTS_RBT_SET_LEFT(T, L) ((T)->left = (L))
#define ERTS_RBT_GET_KEY(T) ((T)->key)
-#define ERTS_RBT_CMP_KEYS(KX, KY) CMP_TERM(KX, KY)
+#define ERTS_RBT_CMP_KEYS(KX, KY) subtract_term_cmp((KX), (KY))
#define ERTS_RBT_WANT_LOOKUP_INSERT
#define ERTS_RBT_WANT_LOOKUP
#define ERTS_RBT_WANT_DELETE
#define ERTS_RBT_UNDEF
+/* erl_rbtree expects comparisons to return an int */
+static int subtract_term_cmp(Eterm a, Eterm b) {
+ Sint res = CMP_TERM(a, b);
+
+ if (res < 0) {
+ return -1;
+ } else if (res > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+
#include "erl_rbtree.h"
static int subtract_continue(Process *p, ErtsSubtractContext *context);
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index f58a606d57..55e469b553 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -4051,6 +4051,7 @@ erts_proc_sig_signal_size(ErtsSignal *sig)
case ERTS_MON_TYPE_DIST_PROC:
case ERTS_MON_TYPE_NODE:
size = erts_monitor_size((ErtsMonitor *) sig);
+ break;
default:
ERTS_INTERNAL_ERROR("Unexpected sig type");
break;
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 6118c671ee..cdad72443f 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -2352,6 +2352,8 @@ erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq)
old_rqint);
if (act_rqint == old_rqint)
return !0;
+
+ old_rqint = act_rqint;
}
}
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index ee3b9f2a98..dc0847fa39 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -8860,35 +8860,67 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env,
#endif
// It must be a map
- if (!IS_MAP(env, eVal))
+ if (!IS_MAP(env, eVal)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "value *not* a map\r\n") );
return enif_make_badarg(env);
+ }
// It must have atleast two attributes
- if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2))
+ if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "invalid map value: %T\r\n", eVal) );
return enif_make_badarg(env);
+ }
- if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr))
+ if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed get multiaddr (map) attribute\r\n") );
return enif_make_badarg(env);
+ }
- if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface))
+ if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed get interface (map) attribute\r\n") );
return enif_make_badarg(env);
+ }
if ((xres = esock_decode_ip4_address(env,
eMultiAddr,
- &mreq.imr_multiaddr)) != NULL)
+ &mreq.imr_multiaddr)) != NULL) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) );
return esock_make_error_str(env, xres);
+ }
if ((xres = esock_decode_ip4_address(env,
eInterface,
- &mreq.imr_interface)) != NULL)
+ &mreq.imr_interface)) != NULL) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed decode interface %T: %s\r\n", eInterface, xres) );
return esock_make_error_str(env, xres);
+ }
res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq));
- if (res != 0)
- result = esock_make_error_errno(env, sock_errno());
- else
+ if (res != 0) {
+ int save_errno = sock_errno();
+
+ result = esock_make_error_errno(env, save_errno);
+
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed setopt: %T (%d)\r\n", result, save_errno) );
+
+ } else {
result = esock_atom_ok;
+ }
return result;
}
@@ -9492,33 +9524,65 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env,
#endif
// It must be a map
- if (!IS_MAP(env, eVal))
+ if (!IS_MAP(env, eVal)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "value *not* a map\r\n") );
return enif_make_badarg(env);
+ }
// It must have atleast two attributes
- if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2))
+ if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "invalid map value: %T\r\n", eVal) );
return enif_make_badarg(env);
+ }
- if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr))
+ if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "failed get multiaddr (map) attribute\r\n") );
return enif_make_badarg(env);
+ }
- if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface))
+ if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "failed get interface (map) attribute\r\n") );
return enif_make_badarg(env);
+ }
if ((xres = esock_decode_ip6_address(env,
eMultiAddr,
- &mreq.ipv6mr_multiaddr)) != NULL)
+ &mreq.ipv6mr_multiaddr)) != NULL) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "failed decode multiaddr %T: %s\r\n", eMultiAddr, xres) );
return esock_make_error_str(env, xres);
+ }
- if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface))
+ if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) {
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ip_update_membership -> "
+ "failed decode interface %T: %s\r\n", eInterface, xres) );
return esock_make_error(env, esock_atom_einval);
+ }
res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq));
- if (res != 0)
- result = esock_make_error_errno(env, sock_errno());
- else
+ if (res != 0) {
+ int save_errno = sock_errno();
+
+ result = esock_make_error_errno(env, save_errno);
+
+ SSDBG( descP,
+ ("SOCKET", "nsetopt_lvl_ipv6_update_membership -> "
+ "failed setopt: %T (%d)\r\n", result, save_errno) );
+
+ } else {
result = esock_atom_ok;
+ }
return result;
}
diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl
index e3545ccbf9..c6b5c31503 100644
--- a/erts/emulator/test/socket_SUITE.erl
+++ b/erts/emulator/test/socket_SUITE.erl
@@ -79,6 +79,7 @@
api_opt_simple_otp_options/1,
api_opt_simple_otp_rcvbuf_option/1,
api_opt_simple_otp_controlling_process/1,
+ api_opt_ip_add_drop_membership/1,
%% *** API Operation Timeout ***
api_to_connect_tcp4/1,
@@ -511,6 +512,8 @@ groups() ->
[{api, [], api_cases()},
{api_basic, [], api_basic_cases()},
{api_options, [], api_options_cases()},
+ {api_options_otp, [], api_options_otp_cases()},
+ {api_options_ip, [], api_options_ip_cases()},
{api_op_with_timeout, [], api_op_with_timeout_cases()},
{socket_closure, [], socket_closure_cases()},
{sc_ctrl_proc_exit, [], sc_cp_exit_cases()},
@@ -596,11 +599,22 @@ api_basic_cases() ->
api_options_cases() ->
[
+ {group, api_options_otp},
+ {group, api_options_ip}
+ ].
+
+api_options_otp_cases() ->
+ [
api_opt_simple_otp_options,
api_opt_simple_otp_rcvbuf_option,
api_opt_simple_otp_controlling_process
].
+api_options_ip_cases() ->
+ [
+ api_opt_ip_add_drop_membership
+ ].
+
api_op_with_timeout_cases() ->
[
api_to_connect_tcp4,
@@ -3173,7 +3187,7 @@ api_opt_simple_otp_controlling_process(suite) ->
api_opt_simple_otp_controlling_process(doc) ->
[];
api_opt_simple_otp_controlling_process(_Config) when is_list(_Config) ->
- ?TT(?SECS(5)),
+ ?TT(?SECS(30)),
tc_try(api_opt_simple_otp_controlling_process,
fun() -> api_opt_simple_otp_controlling_process() end).
@@ -3418,6 +3432,321 @@ api_opt_simple_otp_controlling_process() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Tests that the add_mambership and drop_membership ip options work.
+%% We create one server and two clients. The server only send messages,
+%% the clients only receives messages.
+%% An UDP datagram is forbidden (RFC 1122) from having a source address
+%% that is a multicast address (or a broadcast address).
+%% So, the server create a socket "for sending" and the clients sockets
+%% "for receiving".
+%% Sending socket: Bound to the local address (and any port).
+%% When sending, the dest will be the multicast address
+%% and port of the receiving socket.
+%% Receiving socket: Bound to the multicast address and port.
+api_opt_ip_add_drop_membership(suite) ->
+ [];
+api_opt_ip_add_drop_membership(doc) ->
+ ["OTP-15908 (ERL-980)"];
+api_opt_ip_add_drop_membership(_Config) when is_list(_Config) ->
+ ?TT(?SECS(30)),
+ tc_try(api_opt_ip_add_drop_membership,
+ fun() ->
+ has_ip_add_membership_support(),
+ has_ip_drop_membership_support(),
+ has_ip_multicast_support()
+ end,
+ fun() -> api_opt_ip_add_drop_membership() end).
+
+
+api_opt_ip_add_drop_membership() ->
+ Set = fun(S, Key, Val) ->
+ socket:setopt(S, ip, Key, Val)
+ end,
+ AddMembership = fun(S, Val) -> Set(S, add_membership, Val) end,
+ DropMembership = fun(S, Val) -> Set(S, drop_membership, Val) end,
+
+ ServerSeq =
+ [
+ %% *** Wait for start order part ***
+ #{desc => "await start",
+ cmd => fun(State) ->
+ {Tester, MSA} = ?SEV_AWAIT_START(),
+ {ok, State#{tester => Tester, msa => MSA}}
+ end},
+ #{desc => "monitor tester",
+ cmd => fun(#{tester := Tester} = _State) ->
+ _MRef = erlang:monitor(process, Tester),
+ ok
+ end},
+
+ %% *** Init part ***
+ #{desc => "which local address",
+ cmd => fun(#{domain := Domain} = State) ->
+ LAddr = which_local_addr(Domain),
+ LSA = #{family => Domain, addr => LAddr},
+ {ok, State#{local_sa => LSA}}
+ end},
+ #{desc => "create socket",
+ cmd => fun(#{domain := Domain} = State) ->
+ case socket:open(Domain, dgram, udp) of
+ {ok, Sock} ->
+ {ok, State#{sock => Sock}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "make recv socket reuse addr",
+ cmd => fun(#{sock := Sock} = _State) ->
+ case socket:setopt(Sock, socket, reuseaddr, true) of
+ ok ->
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Failed set reuseaddr: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "bind recv socket to multicast address",
+ cmd => fun(#{sock := Sock, msa := MSA} = State) ->
+ case socket:bind(Sock, MSA) of
+ {ok, Port} ->
+ ?SEV_IPRINT("bound to:"
+ "~n ~p", [Port]),
+ {ok, State#{msa => MSA#{port => Port}}};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (init)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, init),
+ ok
+ end},
+
+ %% The actual test
+ #{desc => "await continue (add_membership)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, add_membership)
+ end},
+ #{desc => "add membership",
+ cmd => fun(#{sock := Sock,
+ msa := #{addr := MAddr},
+ local_sa := #{addr := Addr}} = State) ->
+ MReq = #{multiaddr => MAddr,
+ interface => Addr},
+ ?SEV_IPRINT("try add membership to:"
+ "~n ~p", [MReq]),
+ case AddMembership(Sock, MReq) of
+ ok ->
+ ?SEV_IPRINT("membership added"),
+ {ok, State#{mreq => MReq}};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Failed adding membership to: "
+ "~n ~p"
+ "~n Reason: ~p",
+ [MReq, Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (add-membership)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, add_membership),
+ ok
+ end},
+
+ #{desc => "await continue (drop_membership)",
+ cmd => fun(#{tester := Tester} = _State) ->
+ ?SEV_AWAIT_CONTINUE(Tester, tester, drop_membership)
+ end},
+ #{desc => "drop membership",
+ cmd => fun(#{sock := Sock,
+ mreq := MReq} = State) ->
+ ?SEV_IPRINT("try drop membership from:"
+ "~n ~p", [MReq]),
+ case DropMembership(Sock, MReq) of
+ ok ->
+ ?SEV_IPRINT("membership dropped"),
+ {ok, maps:remove(mreq, State)};
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Failed drop membership from: "
+ "~n ~p"
+ "~n Reason: ~p",
+ [MReq, Reason]),
+ ERROR
+ end
+ end},
+ #{desc => "announce ready (drop-membership)",
+ cmd => fun(#{tester := Tester}) ->
+ ?SEV_ANNOUNCE_READY(Tester, drop_membership),
+ ok
+ end},
+
+
+ %% Termination
+ #{desc => "await terminate (from tester)",
+ cmd => fun(#{tester := Tester} = State) ->
+ case ?SEV_AWAIT_TERMINATE(Tester, tester) of
+ ok ->
+ {ok, maps:remove(tester, State)};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end},
+ #{desc => "close socket",
+ cmd => fun(#{sock := Sock} = State) ->
+ socket:close(Sock),
+ {ok, maps:remove(sock, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ TesterSeq =
+ [
+ %% *** Init part ***
+ #{desc => "monitor server",
+ cmd => fun(#{server := Server} = _State) ->
+ _MRef = erlang:monitor(process, Server),
+ ok
+ end},
+
+ %% Start the server
+ #{desc => "order server start",
+ cmd => fun(#{server := Pid, msa := MSA} = _State) ->
+ ?SEV_ANNOUNCE_START(Pid, MSA),
+ ok
+ end},
+ #{desc => "await server ready (init)",
+ cmd => fun(#{server := Pid} = _State) ->
+ case ?SEV_AWAIT_READY(Pid, server, init) of
+ ok ->
+ ok;
+ {error, Reason} = ERROR ->
+ ?SEV_EPRINT("Start of server failed: "
+ "~n ~p", [Reason]),
+ ERROR
+ end
+ end},
+
+
+ %% *** The actual test ***
+ #{desc => "order server to continue (add-membership)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, add_membership),
+ ok
+ end},
+ #{desc => "await server ready (add-membership)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, add_membership)
+ end},
+
+ #{desc => "order server to continue (drop-membership)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_CONTINUE(Server, drop_membership),
+ ok
+ end},
+ #{desc => "await server ready (drop-membership)",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_AWAIT_READY(Server, server, drop_membership)
+ end},
+
+ ?SEV_SLEEP(?SECS(1)),
+
+ %% *** Termination ***
+ #{desc => "order server terminate",
+ cmd => fun(#{server := Server} = _State) ->
+ ?SEV_ANNOUNCE_TERMINATE(Server),
+ ok
+ end},
+ #{desc => "await server termination",
+ cmd => fun(#{server := Server} = State) ->
+ ?SEV_AWAIT_TERMINATION(Server),
+ {ok, maps:remove(server, State)}
+ end},
+
+ %% *** We are done ***
+ ?SEV_FINISH_NORMAL
+ ],
+
+
+ i("get multicast address"),
+ Domain = inet,
+ MAddr = which_ip_multicast_address(),
+ MSA = #{family => Domain, addr => MAddr},
+
+ i("start server evaluator"),
+ ServerInitState = #{domain => Domain},
+ Server = ?SEV_START("server", ServerSeq, ServerInitState),
+
+ i("start tester evaluator"),
+ TesterInitState = #{domain => Domain,
+ msa => MSA,
+ server => Server#ev.pid},
+ Tester = ?SEV_START("tester", TesterSeq, TesterInitState),
+
+ i("await evaluator(s)"),
+ ok = ?SEV_AWAIT_FINISH([Tester, Server]).
+
+
+
+which_ip_multicast_address() ->
+ which_multicast_address(inet).
+
+which_multicast_address(Domain) ->
+ case os:type() of
+ {unix, linux} ->
+ WhichMAddr = fun([_, _, MAddr]) -> MAddr end,
+ which_multicast_address2(Domain, WhichMAddr);
+
+ {unix, sunos} ->
+ WhichMAddr = fun([_, MAddr, _]) -> MAddr end,
+ which_multicast_address2(Domain, WhichMAddr);
+
+ Type ->
+ not_supported({multicast, Type})
+ end.
+
+%% Note that the 'netstat -g' table looks different on linux and SunOS
+%% Linux: IfName - RefCnt - Group
+%% SunOS: IfName - Group - RefCnt
+
+which_multicast_address2(Domain, WhichMAddr) ->
+ IfName = which_local_host_ifname(Domain),
+ NetstatGroupsStr = os:cmd("netstat -g | grep " ++ IfName),
+ NetstatGroups0 = string:tokens(NetstatGroupsStr, [$\n]),
+ NetstatGroups = [string:tokens(G, [$ ]) || G <- NetstatGroups0],
+ MAddrs = [WhichMAddr(NetstatGroup) || NetstatGroup <-
+ NetstatGroups],
+ which_multicast_address3(Domain, MAddrs).
+
+which_multicast_address3(_Domain, []) ->
+ not_supported({multicast, no_valid_addrs});
+which_multicast_address3(Domain, [MAddrStr|MAddrs]) ->
+ %% Even on linux some of these are not actually addresses, but
+ %% "host names", such as all-systems.mcast.net. But both
+ %% address strings, such as "224.0.0.251" and host name strings
+ %% gets translated into an address by the inet:inet:getaddr/2.
+ case inet:getaddr(MAddrStr, Domain) of
+ {ok, MAddr} ->
+ MAddr;
+ {error, _} ->
+ which_multicast_address3(Domain, MAddrs)
+ end.
+
+which_local_host_ifname(Domain) ->
+ case which_local_host_info(Domain) of
+ {ok, {Name, _Addr, _Flags}} ->
+ Name;
+ {error, Reason} ->
+ not_supported({multicast, Reason})
+ end.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% %%
%% API OPERATIONS WITH TIMEOUT %%
@@ -11304,7 +11633,7 @@ traffic_ping_pong_send_and_receive_udp2(InitState) ->
LSA = #{family => Domain, addr => LAddr},
{ok, State#{local_sa => LSA}}
end},
- #{desc => "create listen socket",
+ #{desc => "create socket",
cmd => fun(#{domain := Domain} = State) ->
case socket:open(Domain, dgram, udp) of
{ok, Sock} ->
@@ -17899,47 +18228,146 @@ local_host() ->
end.
+
+
%% This gets the local address (not 127.0...)
%% We should really implement this using the (new) net module,
%% but until that gets the necessary functionality...
which_local_addr(Domain) ->
- case inet:getifaddrs() of
- {ok, IFL} ->
- which_addr(Domain, IFL);
+ case which_local_host_info(Domain) of
+ {ok, {_Name, _Flags, Addr}} ->
+ Addr;
{error, Reason} ->
?FAIL({inet, getifaddrs, Reason})
end.
-which_addr(_Domain, []) ->
+%% which_addr(_Domain, []) ->
+%% ?FAIL(no_address);
+%% which_addr(Domain, [{"lo" ++ _, _}|IFL]) ->
+%% which_addr(Domain, IFL);
+%% which_addr(Domain, [{"docker" ++ _, _}|IFL]) ->
+%% which_addr(Domain, IFL);
+%% which_addr(Domain, [{"br-" ++ _, _}|IFL]) ->
+%% which_addr(Domain, IFL);
+%% which_addr(Domain, [{_Name, IFO}|IFL]) ->
+%% case which_addr2(Domain, IFO) of
+%% {ok, Addr} ->
+%% Addr;
+%% {error, no_address} ->
+%% which_addr(Domain, IFL)
+%% end;
+%% which_addr(Domain, [_|IFL]) ->
+%% which_addr(Domain, IFL).
+
+%% which_addr2(_Domain, []) ->
+%% {error, no_address};
+%% which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) ->
+%% {ok, Addr};
+%% which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) ->
+%% {ok, Addr};
+%% which_addr2(Domain, [_|IFO]) ->
+%% which_addr2(Domain, IFO).
+
+
+%% Returns the interface (name), flags and address (not 127...)
+%% of the local host.
+which_local_host_info(Domain) ->
+ case inet:getifaddrs() of
+ {ok, IFL} ->
+ which_local_host_info(Domain, IFL);
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+which_local_host_info(_Domain, []) ->
?FAIL(no_address);
-which_addr(Domain, [{"lo" ++ _, _}|IFL]) ->
- which_addr(Domain, IFL);
-which_addr(Domain, [{_Name, IFO}|IFL]) ->
- case which_addr2(Domain, IFO) of
- {ok, Addr} ->
- Addr;
- {error, no_address} ->
- which_addr(Domain, IFL)
+which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) ->
+ which_local_host_info(Domain, IFL);
+which_local_host_info(Domain, [{Name, IFO}|IFL]) ->
+ case which_local_host_info2(Domain, IFO) of
+ {ok, {Flags, Addr}} ->
+ {ok, {Name, Flags, Addr}};
+ {error, _} ->
+ which_local_host_info(Domain, IFL)
end;
-which_addr(Domain, [_|IFL]) ->
- which_addr(Domain, IFL).
+which_local_host_info(Domain, [_|IFL]) ->
+ which_local_host_info(Domain, IFL).
+
+which_local_host_info2(Domain, IFO) ->
+ case lists:keysearch(flags, 1, IFO) of
+ {value, {flags, Flags}} ->
+ which_local_host_info2(Domain, IFO, Flags);
+ false ->
+ {error, no_flags}
+ end.
-which_addr2(_Domain, []) ->
+which_local_host_info2(_Domain, [], _Flags) ->
{error, no_address};
-which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 4) ->
- {ok, Addr};
-which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) when (size(Addr) =:= 8) ->
- {ok, Addr};
-which_addr2(Domain, [_|IFO]) ->
- which_addr2(Domain, IFO).
+which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags)
+ when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) ->
+ {ok, {Flags, Addr}};
+which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags)
+ when (size(Addr) =:= 8) andalso
+ (element(1, Addr) =/= 0) andalso
+ (element(1, Addr) =/= 16#fe80) ->
+ {ok, {Flags, Addr}};
+which_local_host_info2(Domain, [_|IFO], Flags) ->
+ which_local_host_info2(Domain, IFO, Flags).
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%% Here are all the *general* test case condition functions.
+
+%% We also need (be able) to figure out the the multicast address,
+%% which we only support for some platforms (linux and sunos).
+%% We don't do that here, but since we can only do that (find a
+%% multicast address) for specific platforms, we check that we are
+%% on of those platforms here.
+has_ip_multicast_support() ->
+ case os:type() of
+ {unix, OsName} when (OsName =:= linux) orelse
+ (OsName =:= sunos) ->
+ case which_local_host_info(inet) of
+ {ok, {_Name, Flags, _Addr}} ->
+ case lists:member(multicast, Flags) of
+ true ->
+ ok;
+ false ->
+ not_supported(multicast)
+ end;
+ {error, Reason} ->
+ not_supported({multicast, Reason})
+ end;
+ Type ->
+ not_supported({multicast, Type})
+ end.
+has_ip_add_membership_support() ->
+ has_socket_option_ip_support(add_membership).
+has_ip_drop_membership_support() ->
+ has_socket_option_ip_support(drop_membership).
+has_socket_option_ip_support(Opt) ->
+ has_socket_option_support(ip, Opt).
+
+has_socket_option_support(Level, Option) ->
+ case socket:supports(options, Level, Option) of
+ true ->
+ ok;
+ false ->
+ not_supported({options, Level, Option})
+ end.
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%% Here are all the *general* test vase condition functions.
%% The idea is that this function shall test if the test host has
%% support for IPv6. If not, there is no point in running IPv6 tests.
@@ -17957,6 +18385,9 @@ has_support_ipv6() ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+not_supported(What) ->
+ skip({not_supported, What}).
+
not_yet_implemented() ->
skip("not yet implemented").
diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl
index 55b1162cfb..b48be3dd04 100644
--- a/erts/emulator/test/system_info_SUITE.erl
+++ b/erts/emulator/test/system_info_SUITE.erl
@@ -37,10 +37,13 @@
-export([process_count/1, system_version/1, misc_smoke_tests/1,
heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1,
+ procs_bug/1,
ets_count/1, atom_count/1, system_logger/1]).
-export([init/1, handle_event/2, handle_call/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 2}}].
@@ -48,8 +51,20 @@ suite() ->
all() ->
[process_count, system_version, misc_smoke_tests,
ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count,
+ procs_bug,
system_logger].
+
+init_per_testcase(procs_bug, Config) ->
+ procs_bug(init_per_testcase, Config);
+init_per_testcase(_, Config) ->
+ Config.
+
+end_per_testcase(procs_bug, Config) ->
+ procs_bug(end_per_testcase, Config);
+end_per_testcase(_, _) ->
+ ok.
+
%%%
%%% The test cases -------------------------------------------------------------
%%%
@@ -654,3 +669,41 @@ handle_call(Msg, State) ->
handle_event(Event, State) ->
State ! {report_handler, Event},
{ok, State}.
+
+
+%% OTP-15909: Provoke bug that would cause VM crash
+%% if doing system_info(procs) when process have queued exit/down signals.
+procs_bug(init_per_testcase, Config) ->
+ %% Use single scheduler and process prio to starve monitoring processes
+ %% from handling their received DOWN signals.
+ OldSchedOnline = erlang:system_flag(schedulers_online,1),
+ [{schedulers_online, OldSchedOnline} | Config];
+procs_bug(end_per_testcase, Config) ->
+ erlang:system_flag(schedulers_online,
+ proplists:get_value(schedulers_online, Config)),
+ ok.
+
+procs_bug(Config) when is_list(Config) ->
+ {Monee,_} = spawn_opt(fun () -> receive die -> ok end end,
+ [monitor,{priority,max}]),
+ Papa = self(),
+ Pids = [begin
+ P = spawn_opt(fun () ->
+ erlang:monitor(process, Monee),
+ Papa ! {self(),ready},
+ receive "nada" -> no end
+ end,
+ [link, {priority,normal}]),
+ {P, ready} = receive M -> M end,
+ P
+ end
+ || _ <- lists:seq(1,10)],
+ process_flag(priority,high),
+ Monee ! die,
+ {'DOWN',_,process,Monee,normal} = receive M -> M end,
+
+ %% This call did crash VM as Pids have pending DOWN signals.
+ erlang:system_info(procs),
+ process_flag(priority,normal),
+ [begin unlink(P), exit(P, kill) end || P <- Pids],
+ ok.
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 40a9685e9d..f06fd08540 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.4.3
+VSN = 10.4.4
# Port number 4365 in 4.2
# Port number 4366 in 4.3
diff --git a/lib/compiler/src/beam_ssa_bsm.erl b/lib/compiler/src/beam_ssa_bsm.erl
index 382e6f635e..1ac9e6a3bb 100644
--- a/lib/compiler/src/beam_ssa_bsm.erl
+++ b/lib/compiler/src/beam_ssa_bsm.erl
@@ -683,8 +683,12 @@ aca_copy_successors(Lbl0, Blocks0, Counter0) ->
Lbl = maps:get(Lbl0, BRs),
{Lbl, Blocks, Counter}.
+aca_cs_build_brs([?BADARG_BLOCK=Lbl | Path], Counter, Acc) ->
+ %% ?BADARG_BLOCK is a marker and not an actual block, so renaming it will
+ %% break exception handling.
+ aca_cs_build_brs(Path, Counter, Acc#{ Lbl => Lbl });
aca_cs_build_brs([Lbl | Path], Counter0, Acc) ->
- aca_cs_build_brs(Path, Counter0 + 1, maps:put(Lbl, Counter0, Acc));
+ aca_cs_build_brs(Path, Counter0 + 1, Acc#{ Lbl => Counter0 });
aca_cs_build_brs([], Counter, Acc) ->
{Acc, Counter}.
diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl
index 90c0d3cf16..229edc6a1d 100644
--- a/lib/compiler/src/beam_ssa_opt.erl
+++ b/lib/compiler/src/beam_ssa_opt.erl
@@ -904,8 +904,7 @@ cse_suitable(#b_set{}) -> false.
}).
ssa_opt_float({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
- NonGuards0 = float_non_guards(Linear0),
- NonGuards = gb_sets:from_list(NonGuards0),
+ NonGuards = non_guards(Linear0),
Blocks = maps:from_list(Linear0),
Fs = #fs{non_guards=NonGuards,bs=Blocks},
{Linear,Count} = float_opt(Linear0, Count0, Fs),
@@ -916,15 +915,6 @@ float_blk_is_in_guard(#b_blk{last=#b_br{fail=F}}, #fs{non_guards=NonGuards}) ->
float_blk_is_in_guard(#b_blk{}, #fs{}) ->
false.
-float_non_guards([{L,#b_blk{is=Is}}|Bs]) ->
- case Is of
- [#b_set{op=landingpad}|_] ->
- [L|float_non_guards(Bs)];
- _ ->
- float_non_guards(Bs)
- end;
-float_non_guards([]) -> [?BADARG_BLOCK].
-
float_opt([{L,Blk}|Bs0], Count0, Fs) ->
case float_blk_is_in_guard(Blk, Fs) of
true ->
@@ -1774,35 +1764,44 @@ opt_bs_put_split_int_1(Int, L, R) ->
%%%
ssa_opt_tuple_size({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) ->
- {Linear,Count} = opt_tup_size(Linear0, Count0, []),
+ %% This optimization is only safe in guards, as prefixing tuple_size with
+ %% an is_tuple check prevents it from throwing an exception.
+ NonGuards = non_guards(Linear0),
+ {Linear,Count} = opt_tup_size(Linear0, NonGuards, Count0, []),
{St#st{ssa=Linear,cnt=Count}, FuncDb}.
-opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], Count0, Acc0) ->
+opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], NonGuards, Count0, Acc0) ->
case {Is,Last} of
{[#b_set{op={bif,'=:='},dst=Bool,args=[#b_var{}=Tup,#b_literal{val=Arity}]}],
#b_br{bool=Bool}} when is_integer(Arity), Arity >= 0 ->
- {Acc,Count} = opt_tup_size_1(Tup, L, Count0, Acc0),
- opt_tup_size(Bs, Count, [{L,Blk}|Acc]);
+ {Acc,Count} = opt_tup_size_1(Tup, L, NonGuards, Count0, Acc0),
+ opt_tup_size(Bs, NonGuards, Count, [{L,Blk}|Acc]);
{_,_} ->
- opt_tup_size(Bs, Count0, [{L,Blk}|Acc0])
+ opt_tup_size(Bs, NonGuards, Count0, [{L,Blk}|Acc0])
end;
-opt_tup_size([], Count, Acc) ->
+opt_tup_size([], _NonGuards, Count, Acc) ->
{reverse(Acc),Count}.
-opt_tup_size_1(Size, EqL, Count0, [{L,Blk0}|Acc]) ->
- case Blk0 of
- #b_blk{is=Is0,last=#b_br{bool=Bool,succ=EqL,fail=Fail}} ->
- case opt_tup_size_is(Is0, Bool, Size, []) of
- none ->
+opt_tup_size_1(Size, EqL, NonGuards, Count0, [{L,Blk0}|Acc]) ->
+ #b_blk{is=Is0,last=Last} = Blk0,
+ case Last of
+ #b_br{bool=Bool,succ=EqL,fail=Fail} ->
+ case gb_sets:is_member(Fail, NonGuards) of
+ true ->
{[{L,Blk0}|Acc],Count0};
- {PreIs,TupleSizeIs,Tuple} ->
- opt_tup_size_2(PreIs, TupleSizeIs, L, EqL,
- Tuple, Fail, Count0, Acc)
+ false ->
+ case opt_tup_size_is(Is0, Bool, Size, []) of
+ none ->
+ {[{L,Blk0}|Acc],Count0};
+ {PreIs,TupleSizeIs,Tuple} ->
+ opt_tup_size_2(PreIs, TupleSizeIs, L, EqL,
+ Tuple, Fail, Count0, Acc)
+ end
end;
- #b_blk{} ->
+ _ ->
{[{L,Blk0}|Acc],Count0}
end;
-opt_tup_size_1(_, _, Count, Acc) ->
+opt_tup_size_1(_, _, _, Count, Acc) ->
{Acc,Count}.
opt_tup_size_2(PreIs, TupleSizeIs, PreL, EqL, Tuple, Fail, Count0, Acc) ->
@@ -2241,6 +2240,19 @@ gcd(A, B) ->
X -> gcd(B, X)
end.
+non_guards(Linear) ->
+ gb_sets:from_list(non_guards_1(Linear)).
+
+non_guards_1([{L,#b_blk{is=Is}}|Bs]) ->
+ case Is of
+ [#b_set{op=landingpad}|_] ->
+ [L | non_guards_1(Bs)];
+ _ ->
+ non_guards_1(Bs)
+ end;
+non_guards_1([]) ->
+ [?BADARG_BLOCK].
+
rel2fam(S0) ->
S1 = sofs:relation(S0),
S = sofs:rel2fam(S1),
diff --git a/lib/compiler/src/beam_ssa_pre_codegen.erl b/lib/compiler/src/beam_ssa_pre_codegen.erl
index 9af72afca7..a2e687930b 100644
--- a/lib/compiler/src/beam_ssa_pre_codegen.erl
+++ b/lib/compiler/src/beam_ssa_pre_codegen.erl
@@ -156,7 +156,9 @@ passes(Opts) ->
%% Allocate registers.
?PASS(linear_scan),
?PASS(frame_size),
- ?PASS(turn_yregs)],
+ ?PASS(turn_yregs),
+
+ ?PASS(assert_no_critical_edges)],
[P || P <- Ps, P =/= ignore].
function(#b_function{anno=Anno,args=Args,bs=Blocks0,cnt=Count0}=F0,
@@ -1321,10 +1323,11 @@ fix_receives_1([{L,Blk}|Ls], Blocks0, Count0) ->
LoopExit = find_loop_exit(Rm, Blocks0),
Defs0 = beam_ssa:def([L], Blocks0),
CommonUsed = recv_common(Defs0, LoopExit, Blocks0),
- {Blocks1,Count1} = recv_fix_common(CommonUsed, LoopExit, Rm,
- Blocks0, Count0),
+ {Blocks1,Count1} = recv_crit_edges(Rm, LoopExit, Blocks0, Count0),
+ {Blocks2,Count2} = recv_fix_common(CommonUsed, LoopExit, Rm,
+ Blocks1, Count1),
Defs = ordsets:subtract(Defs0, CommonUsed),
- {Blocks,Count} = fix_receive(Rm, Defs, Blocks1, Count1),
+ {Blocks,Count} = fix_receive(Rm, Defs, Blocks2, Count2),
fix_receives_1(Ls, Blocks, Count);
#b_blk{} ->
fix_receives_1(Ls, Blocks0, Count0)
@@ -1341,6 +1344,57 @@ recv_common(Defs, Exit, Blocks) ->
Def = ordsets:subtract(Defs, ExitDefs),
ordsets:intersection(Def, ExitUsed).
+%% recv_crit_edges([RemoveMessageLabel], LoopExit,
+%% Blocks0, Count0) -> {Blocks,Count}.
+%%
+%% Adds dummy blocks on all conditional jumps to the exit block so that
+%% recv_fix_common/5 can insert phi nodes without having to worry about
+%% critical edges.
+
+recv_crit_edges(_Rms, none, Blocks0, Count0) ->
+ {Blocks0, Count0};
+recv_crit_edges(Rms, Exit, Blocks0, Count0) ->
+ Ls = beam_ssa:rpo(Rms, Blocks0),
+ rce_insert_edges(Ls, Exit, Count0, Blocks0).
+
+rce_insert_edges([L | Ls], Exit, Count0, Blocks0) ->
+ Successors = beam_ssa:successors(map_get(L, Blocks0)),
+ case member(Exit, Successors) of
+ true when Successors =/= [Exit] ->
+ {Blocks, Count} = rce_insert_edge(L, Exit, Count0, Blocks0),
+ rce_insert_edges(Ls, Exit, Count, Blocks);
+ _ ->
+ rce_insert_edges(Ls, Exit, Count0, Blocks0)
+ end;
+rce_insert_edges([], _Exit, Count, Blocks) ->
+ {Blocks, Count}.
+
+rce_insert_edge(L, Exit, Count, Blocks0) ->
+ #b_blk{last=Last0} = FromBlk0 = map_get(L, Blocks0),
+
+ ToExit = #b_br{bool=#b_literal{val=true},succ=Exit,fail=Exit},
+
+ FromBlk = FromBlk0#b_blk{last=rce_reroute_terminator(Last0, Exit, Count)},
+ EdgeBlk = #b_blk{anno=#{},is=[],last=ToExit},
+
+ Blocks = Blocks0#{ Count => EdgeBlk, L => FromBlk },
+ {Blocks, Count + 1}.
+
+rce_reroute_terminator(#b_br{succ=Exit}=Last, Exit, New) ->
+ rce_reroute_terminator(Last#b_br{succ=New}, Exit, New);
+rce_reroute_terminator(#b_br{fail=Exit}=Last, Exit, New) ->
+ rce_reroute_terminator(Last#b_br{fail=New}, Exit, New);
+rce_reroute_terminator(#b_br{}=Last, _Exit, _New) ->
+ Last;
+rce_reroute_terminator(#b_switch{fail=Exit}=Last, Exit, New) ->
+ rce_reroute_terminator(Last#b_switch{fail=New}, Exit, New);
+rce_reroute_terminator(#b_switch{list=List0}=Last, Exit, New) ->
+ List = [if
+ Lbl =:= Exit -> {Arg, New};
+ Lbl =/= Exit -> {Arg, Lbl}
+ end || {Arg, Lbl} <- List0],
+ Last#b_switch{list=List}.
+
%% recv_fix_common([CommonVar], LoopExit, [RemoveMessageLabel],
%% Blocks0, Count0) -> {Blocks,Count}.
%% Handle variables alwys defined in a receive and used
diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl
index a741ebbdf9..256bb95559 100644
--- a/lib/compiler/test/beam_ssa_SUITE.erl
+++ b/lib/compiler/test/beam_ssa_SUITE.erl
@@ -190,6 +190,12 @@ recv(_Config) ->
self() ! {[self(),r1],{2,99,<<"data">>}},
{Parent,r1,<<1:32,2:8,99:8,"data">>} = tricky_recv_4(),
+ %% Test tricky_recv_5/0.
+ self() ! 1,
+ a = tricky_recv_5(),
+ self() ! 2,
+ b = tricky_recv_5(),
+
ok.
sync_wait_mon({Pid, Ref}, Timeout) ->
@@ -295,6 +301,26 @@ tricky_recv_4() ->
end,
id({Pid,R,Request}).
+%% beam_ssa_pre_codegen would accidentally create phi nodes on critical edges
+%% when fixing up receives; the call to id/2 can either succeed or land in the
+%% catch block, and we added a phi node to its immediate successor.
+tricky_recv_5() ->
+ try
+ receive
+ X=1 ->
+ id(42),
+ a;
+ X=2 ->
+ b
+ end,
+ case X of
+ 1 -> a;
+ 2 -> b
+ end
+ catch
+ _:_ -> c
+ end.
+
maps(_Config) ->
{'EXIT',{{badmatch,#{}},_}} = (catch maps_1(any)),
ok.
diff --git a/lib/compiler/test/bs_match_SUITE.erl b/lib/compiler/test/bs_match_SUITE.erl
index d97f49c56e..145a50f4ad 100644
--- a/lib/compiler/test/bs_match_SUITE.erl
+++ b/lib/compiler/test/bs_match_SUITE.erl
@@ -44,7 +44,8 @@
beam_bsm/1,guard/1,is_ascii/1,non_opt_eq/1,
expression_before_match/1,erl_689/1,restore_on_call/1,
restore_after_catch/1,matches_on_parameter/1,big_positions/1,
- matching_meets_apply/1,bs_start_match2_defs/1]).
+ matching_meets_apply/1,bs_start_match2_defs/1,
+ exceptions_after_match_failure/1]).
-export([coverage_id/1,coverage_external_ignore/2]).
@@ -80,7 +81,8 @@ groups() ->
beam_bsm,guard,is_ascii,non_opt_eq,
expression_before_match,erl_689,restore_on_call,
matches_on_parameter,big_positions,
- matching_meets_apply,bs_start_match2_defs]}].
+ matching_meets_apply,bs_start_match2_defs,
+ exceptions_after_match_failure]}].
init_per_suite(Config) ->
@@ -2005,4 +2007,17 @@ do_matching_meets_apply(_Bin, {Handler, State}) ->
%% Another case of the above.
Handler:abs(State).
+%% Exception handling was broken on the failure path of bs_start_match as
+%% beam_ssa_bsm accidentally cloned and renamed the ?BADARG_BLOCK.
+exceptions_after_match_failure(_Config) ->
+ {'EXIT', {badarith, _}} = (catch do_exceptions_after_match_failure(atom)),
+ ok = do_exceptions_after_match_failure(<<0, 1, "gurka">>),
+ ok = do_exceptions_after_match_failure(2.0).
+
+do_exceptions_after_match_failure(<<_A, _B, "gurka">>) ->
+ ok;
+do_exceptions_after_match_failure(Other) ->
+ Other / 2.0,
+ ok.
+
id(I) -> I.
diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl
index aac9de278d..bc74ec4984 100644
--- a/lib/compiler/test/match_SUITE.erl
+++ b/lib/compiler/test/match_SUITE.erl
@@ -25,7 +25,8 @@
match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1,
selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1,
coverage/1,grab_bag/1,literal_binary/1,
- unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1]).
+ unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1,
+ tuple_size_in_try/1]).
-include_lib("common_test/include/ct.hrl").
@@ -41,7 +42,8 @@ groups() ->
shortcut_boolean,letify_guard,selectify,deselectify,
underscore,match_map,map_vars_used,coverage,
grab_bag,literal_binary,unary_op,eq_types,
- match_after_return,match_right_tuple]}].
+ match_after_return,match_right_tuple,
+ tuple_size_in_try]}].
init_per_suite(Config) ->
@@ -922,4 +924,19 @@ match_right_tuple_1(T) ->
force_succ_regs(_A, B) -> B.
+tuple_size_in_try(Config) when is_list(Config) ->
+ %% The tuple_size optimization was applied outside of guards, causing
+ %% either the emulator or compiler to crash.
+ ok = tsit(gurka),
+ ok = tsit(gaffel).
+
+tsit(A) ->
+ try
+ id(ignored),
+ 1 = tuple_size(A),
+ error
+ catch
+ _:_ -> ok
+ end.
+
id(I) -> I.
diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml
index 0930f79840..b72453aac7 100644
--- a/lib/dialyzer/doc/src/notes.xml
+++ b/lib/dialyzer/doc/src/notes.xml
@@ -32,6 +32,22 @@
<p>This document describes the changes made to the Dialyzer
application.</p>
+<section><title>Dialyzer 4.0.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p> Make sure Dialyzer does not crash if the formatting
+ of results fails. Instead of crashing, an unformatted
+ version of the results is returned. </p>
+ <p>
+ Own Id: OTP-15922 Aux Id: PR-2240, ERL-949 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Dialyzer 4.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index d4fe064edd..c1bc5ff597 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -605,11 +605,9 @@ ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]).
%% Functions that parse type strings, literal strings, and contract
%% strings. Return strings formatted by erl_pp.
-%% If lib/hipe/cerl/erl_types.erl is compiled with DEBUG=true,
-%% the contents of opaque types are showed inside brackets.
-%% Since erl_parse:parse_form() cannot handle the bracket syntax,
-%% no indentation is done.
-%%-define(DEBUG , true).
+%% Note we always have to catch any error when trying to parse
+%% the syntax because other BEAM languages may not emit an
+%% Erlang AST that transforms into valid Erlang Source Code.
-define(IND, 10).
@@ -620,13 +618,15 @@ con(M, F, Src, I) ->
sig(Src, false) ->
Src;
sig(Src, true) ->
- Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])),
- {ok, Tokens, _EndLocation} = erl_scan:string(Str),
- exec(fun() ->
- {ok, {attribute, _, spec, {_MFA, Types}}} =
- erl_parse:parse_form(Tokens),
- indentation(?IND) ++ pp_spec(Types)
- end, Src).
+ try
+ Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])),
+ {ok, Tokens, _EndLocation} = erl_scan:string(Str),
+ {ok, {attribute, _, spec, {_MFA, Types}}} =
+ erl_parse:parse_form(Tokens),
+ indentation(?IND) ++ pp_spec(Types)
+ catch
+ _:_ -> Src
+ end.
%% Argument(list)s are a mix of types and Erlang code. Note: sometimes
%% (contract_range, call_without_opaque, opaque_type_test), the initial
@@ -681,21 +681,15 @@ ts(Src) ->
[C1|Src1] = Src, % $< (product) or $( (arglist)
[C2|RevSrc2] = lists:reverse(Src1),
Src2 = lists:reverse(RevSrc2),
- exec(fun() ->
- Types = parse_types_and_literals(Src2),
- CommaInd = [$, | Ind],
- (indentation(?IND-1) ++
- [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++
- [C2])
- end, Src).
-
--ifdef(DEBUG).
-exec(F, R) ->
- try F() catch _:_ -> R end.
--else.
-exec(F, _) ->
- F().
--endif.
+ try
+ Types = parse_types_and_literals(Src2),
+ CommaInd = [$, | Ind],
+ (indentation(?IND-1) ++
+ [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++
+ [C2])
+ catch
+ _:_ -> Src
+ end.
indentation(I) ->
[$\n | lists:duplicate(I, $\s)].
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index f887f661bd..5e680062fb 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -320,12 +320,6 @@ report_analysis_start(#options{analysis_type = Type,
end
end.
-report_native_comp(#options{report_mode = ReportMode}) ->
- case ReportMode of
- quiet -> ok;
- _ -> io:format(" Compiling some key modules to native code...")
- end.
-
report_elapsed_time(T1, T2, #options{report_mode = ReportMode}) ->
case ReportMode of
quiet -> ok;
@@ -375,7 +369,6 @@ do_analysis(Options) ->
do_analysis(Files, Options, Plt, PltInfo) ->
assert_writable(Options#options.output_plt),
- hipe_compile(Files, Options),
report_analysis_start(Options),
State0 = new_state(),
State1 = init_output(State0, Options),
@@ -484,106 +477,6 @@ expand_dependent_modules_1([Mod|Mods], Included, ModDeps) ->
expand_dependent_modules_1([], Included, _ModDeps) ->
Included.
--define(MIN_PARALLELISM, 7).
--define(MIN_FILES_FOR_NATIVE_COMPILE, 20).
-
--spec hipe_compile([file:filename()], #options{}) -> 'ok'.
-
-hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
- NoNative = (get(dialyzer_options_native) =:= false),
- FewFiles = (length(Files) < ?MIN_FILES_FOR_NATIVE_COMPILE),
- case NoNative orelse FewFiles orelse ErlangMode of
- true -> ok;
- false ->
- case erlang:system_info(hipe_architecture) of
- undefined -> ok;
- _ ->
- Mods = [lists, dict, digraph, digraph_utils, ets,
- gb_sets, gb_trees, ordsets, sets, sofs,
- cerl, erl_types, cerl_trees, erl_bif_types,
- dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
- dialyzer_codeserver, dialyzer_contracts,
- dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
- dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig,
- dialyzer_worker],
- report_native_comp(Options),
- {T1, _} = statistics(wall_clock),
- Cache = (get(dialyzer_options_native_cache) =/= false),
- native_compile(Mods, Cache),
- {T2, _} = statistics(wall_clock),
- report_elapsed_time(T1, T2, Options)
- end
- end.
-
-native_compile(Mods, Cache) ->
- case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
- true ->
- Parent = self(),
- Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods],
- lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
- false ->
- lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods)
- end.
-
-hc(Mod, Cache) ->
- {module, Mod} = code:ensure_loaded(Mod),
- case code:is_module_native(Mod) of
- true -> ok;
- false ->
- %% io:format(" ~w", [Mod]),
- case Cache of
- false ->
- {ok, Mod} = hipe:c(Mod),
- ok;
- true ->
- hc_cache(Mod)
- end
- end.
-
-hc_cache(Mod) ->
- CacheBase = cache_base_dir(),
- %% Use HiPE architecture, version and erts checksum in directory name,
- %% to avoid clashes between incompatible binaries.
- HipeArchVersion =
- lists:concat(
- [erlang:system_info(hipe_architecture), "-",
- hipe:version(), "-",
- hipe:erts_checksum()]),
- CacheDir = filename:join(CacheBase, HipeArchVersion),
- OrigBeamFile = code:which(Mod),
- {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile),
- CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])),
- ok = filelib:ensure_dir(CachedBeamFile),
- ModBin =
- case filelib:is_file(CachedBeamFile) of
- true ->
- {ok, BinFromFile} = file:read_file(CachedBeamFile),
- BinFromFile;
- false ->
- {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]),
- ok = file:write_file(CachedBeamFile, CompiledBin),
- CompiledBin
- end,
- code:unstick_dir(filename:dirname(OrigBeamFile)),
- {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin),
- true = code:is_module_native(Mod),
- ok.
-
-cache_base_dir() ->
- %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
- %% If XDG_CACHE_HOME is set to an absolute path, use it as base.
- XdgCacheHome = os:getenv("XDG_CACHE_HOME"),
- CacheHome =
- case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of
- true ->
- XdgCacheHome;
- false ->
- %% Otherwise, the default is $HOME/.cache.
- {ok, [[Home]]} = init:get_argument(home),
- filename:join(Home, ".cache")
- end,
- filename:join([CacheHome, "dialyzer_hipe_cache"]).
-
new_state() ->
#cl_state{}.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 466bbfd0f2..7a44daf683 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 4.0.1
+DIALYZER_VSN = 4.0.2
diff --git a/lib/hipe/doc/src/hipe_app.xml b/lib/hipe/doc/src/hipe_app.xml
index 61d92fdffe..5ac445ac58 100644
--- a/lib/hipe/doc/src/hipe_app.xml
+++ b/lib/hipe/doc/src/hipe_app.xml
@@ -66,6 +66,10 @@
<item><p>The HiPE compiler will crash on modules containing binary
matching.</p>
</item>
+ <tag>try/catch</tag>
+ <item><p>The HiPE compiler will crash on modules containing 'try' or
+ 'catch'.</p>
+ </item>
<tag>Stack traces</tag>
<item><p>Stack traces returned from <seealso marker="erts:erlang#get_stacktrace/0">
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
index 8e7e56b6c4..995c961e09 100644
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ b/lib/hipe/icode/hipe_beam_to_icode.erl
@@ -557,32 +557,21 @@ trans_fun([{move,Src,Dst}|Instructions], Env) ->
Dst1 = mk_var(Dst),
Src1 = trans_arg(Src),
[hipe_icode:mk_move(Dst1,Src1) | trans_fun(Instructions,Env)];
-%%--- catch --- ITS PROCESSING IS POSTPONED
-trans_fun([{'catch',N,{_,EndLabel}}|Instructions], Env) ->
- NewContLbl = mk_label(new),
- [{'catch',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)];
-%%--- catch_end --- ITS PROCESSING IS POSTPONED
-trans_fun([{catch_end,_N}=I|Instructions], Env) ->
- [I | trans_fun(Instructions,Env)];
-%%--- try --- ITS PROCESSING IS POSTPONED
-trans_fun([{'try',N,{_,EndLabel}}|Instructions], Env) ->
- NewContLbl = mk_label(new),
- [{'try',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)];
-%%--- try_end ---
-trans_fun([{try_end,_N}|Instructions], Env) ->
- [hipe_icode:mk_end_try() | trans_fun(Instructions,Env)];
-%%--- try_case --- ITS PROCESSING IS POSTPONED
-trans_fun([{try_case,_N}=I|Instructions], Env) ->
- [I | trans_fun(Instructions,Env)];
-%%--- try_case_end ---
-trans_fun([{try_case_end,Arg}|Instructions], Env) ->
- BadArg = trans_arg(Arg),
- ErrVar = mk_var(new),
- Vs = [mk_var(new)],
- Atom = hipe_icode:mk_move(ErrVar,hipe_icode:mk_const(try_clause)),
- Tuple = hipe_icode:mk_primop(Vs,mktuple,[ErrVar,BadArg]),
- Fail = hipe_icode:mk_fail(Vs,error),
- [Atom,Tuple,Fail | trans_fun(Instructions,Env)];
+%%
+%% try/catch -- THESE ARE KNOWN TO MISCOMPILE, SEE OTP-15949
+%%
+trans_fun([{'catch'=Name,_,_}|_], _Env) ->
+ nyi(Name);
+trans_fun([{catch_end=Name,_}|_], _Env) ->
+ nyi(Name);
+trans_fun([{'try'=Name,_,_}|_], _Env) ->
+ nyi(Name);
+trans_fun([{try_end=Name,_}|_], _Env) ->
+ nyi(Name);
+trans_fun([{try_case=Name,_}|_], _Env) ->
+ nyi(Name);
+trans_fun([{try_case_end=Name,_}|_], _Env) ->
+ nyi(Name);
%%--- raise ---
trans_fun([{raise,{f,0},[Reg1,Reg2],{x,0}}|Instructions], Env) ->
V1 = trans_arg(Reg1),
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml
index 03bd1d8042..45533c4f4b 100644
--- a/lib/inets/doc/src/notes.xml
+++ b/lib/inets/doc/src/notes.xml
@@ -33,7 +33,23 @@
<file>notes.xml</file>
</header>
- <section><title>Inets 7.0.8</title>
+ <section><title>Inets 7.0.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a regression in http client that causes a crash when
+ request URI has no scheme.</p>
+ <p>
+ Own Id: OTP-15930 Aux Id: ERL-969 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Inets 7.0.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl
index 24a205ced9..9967488f61 100644
--- a/lib/inets/src/http_client/httpc.erl
+++ b/lib/inets/src/http_client/httpc.erl
@@ -317,7 +317,7 @@ store_cookies(SetCookieHeaders, Url, Profile)
{error, Bad, _} ->
{error, {parse_failed, Bad}};
URI ->
- Scheme = scheme_to_atom(maps:get(scheme, URI, '')),
+ Scheme = scheme_to_atom(maps:get(scheme, URI, undefined)),
Host = maps:get(host, URI, ""),
Port = maps:get(port, URI, default_port(Scheme)),
Path = uri_string:recompose(#{path => maps:get(path, URI, "")}),
@@ -536,7 +536,7 @@ handle_request(Method, Url,
BracketedHost = proplists:get_value(ipv6_host_with_brackets,
Options),
- Scheme = scheme_to_atom(maps:get(scheme, URI, '')),
+ Scheme = scheme_to_atom(maps:get(scheme, URI, undefined)),
Userinfo = maps:get(userinfo, URI, ""),
Host = http_util:maybe_add_brackets(maps:get(host, URI, ""), BracketedHost),
Port = maps:get(port, URI, default_port(Scheme)),
@@ -591,8 +591,8 @@ scheme_to_atom("http") ->
http;
scheme_to_atom("https") ->
https;
-scheme_to_atom('') ->
- '';
+scheme_to_atom(undefined) ->
+ throw({error, {no_scheme}});
scheme_to_atom(Scheme) ->
throw({error, {bad_scheme, Scheme}}).
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index d4b33ae2c6..1d37e71847 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -106,7 +106,8 @@ real_requests()->
streaming_error,
inet_opts,
invalid_headers,
- invalid_body
+ invalid_body,
+ no_scheme
].
real_requests_esi() ->
@@ -1231,6 +1232,16 @@ invalid_body(Config) ->
ok
end.
+
+%%-------------------------------------------------------------------------
+
+no_scheme(_Config) ->
+ {error,{bad_scheme,"ftp"}} = httpc:request("ftp://foobar"),
+ {error,{no_scheme}} = httpc:request("//foobar"),
+ {error,{no_scheme}} = httpc:request("foobar"),
+ ok.
+
+
%%-------------------------------------------------------------------------
remote_socket_close(Config) when is_list(Config) ->
URL = url(group_name(Config), "/just_close.html", Config),
diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk
index 5dbec9e7b3..d948204618 100644
--- a/lib/inets/vsn.mk
+++ b/lib/inets/vsn.mk
@@ -19,6 +19,6 @@
# %CopyrightEnd%
APPLICATION = inets
-INETS_VSN = 7.0.8
+INETS_VSN = 7.0.9
PRE_VSN =
APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)"
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 29bf5fc4e7..bb41b7e9ea 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -27,6 +27,37 @@
</header>
<p>This document describes the changes made to the SSL application.</p>
+<section><title>SSL 9.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix handling of certificate decoding problems in TLS 1.3
+ similarly as in TLS 1.2.</p>
+ <p>
+ Own Id: OTP-15900</p>
+ </item>
+ <item>
+ <p>
+ Hibernation now works as expected in all cases, was
+ accidently broken by optimization efforts.</p>
+ <p>
+ Own Id: OTP-15910</p>
+ </item>
+ <item>
+ <p>
+ Fix interoperability problems with openssl when the TLS
+ 1.3 server is configured wirh the option
+ signature_algs_cert.</p>
+ <p>
+ Own Id: OTP-15913</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>SSL 9.3.3</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 8e850b48a4..f317cb169e 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -258,7 +258,7 @@ next_event(StateName, Record, State) ->
next_event(StateName, no_record, State0, Actions) ->
case next_record(StateName, State0) of
{no_record, State} ->
- {next_state, StateName, State, Actions};
+ ssl_connection:hibernate_after(StateName, State, Actions);
{Record, State} ->
next_event(StateName, Record, State, Actions)
end;
diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl
index 8a4ad922e1..53f9adbbd3 100644
--- a/lib/ssl/src/tls_handshake_1_3.erl
+++ b/lib/ssl/src/tls_handshake_1_3.erl
@@ -111,7 +111,7 @@ add_signature_algorithms_cert(Extensions, undefined) ->
Extensions;
add_signature_algorithms_cert(Extensions, SignAlgsCert) ->
Extensions#{signature_algorithms_cert =>
- #signature_algorithms{signature_scheme_list = SignAlgsCert}}.
+ #signature_algorithms_cert{signature_scheme_list = SignAlgsCert}}.
filter_tls13_algs(undefined) -> undefined;
@@ -802,7 +802,7 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHand
CertDbHandle, CertDbRef)
end
catch
- error:{badmatch,{asn1, Asn1Reason}} ->
+ error:{badmatch,{error, {asn1, Asn1Reason}}} ->
%% ASN-1 decode of certificate somehow failed
{error, {certificate_unknown, {failed_to_decode_certificate, Asn1Reason}}};
error:OtherReason ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 4f6ea6c886..f5ce3a2b65 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -3660,7 +3660,7 @@ hibernate(Config) ->
ssl_test_lib:check_result(Server, ok, Client, ok),
- timer:sleep(1500),
+ ct:sleep(1500),
{current_function, {erlang, hibernate, 3}} =
process_info(Pid, current_function),
@@ -3696,6 +3696,8 @@ hibernate_right_away(Config) ->
[{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
ssl_test_lib:check_result(Server1, ok, Client1, ok),
+
+ ct:sleep(1000), %% Schedule out
{current_function, {erlang, hibernate, 3}} =
process_info(Pid1, current_function),
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 01dee392f5..df38aea017 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 9.3.3
+SSL_VSN = 9.3.4
diff --git a/lib/stdlib/test/lists_SUITE.erl b/lib/stdlib/test/lists_SUITE.erl
index 5dab6f6697..c3c54710eb 100644
--- a/lib/stdlib/test/lists_SUITE.erl
+++ b/lib/stdlib/test/lists_SUITE.erl
@@ -2600,6 +2600,15 @@ subtract(Config) when is_list(Config) ->
[1,2,3,4,5,6,7,8,9,9999,10000,20,21,22] =
sub(lists:seq(1, 10000)++[20,21,22], lists:seq(10, 9998)),
+ %% ERL-986; an integer overflow relating to term comparison
+ %% caused subtraction to be inconsistent.
+ Ids = [2985095936,47540628,135460048,1266126295,240535295,
+ 115724671,161800351,4187206564,4178142725,234897063,
+ 14773162,6662515191,133150693,378034895,1874402262,
+ 3507611978,22850922,415521280,253360400,71683243],
+
+ [] = id(Ids) -- id(Ids),
+
%% Floats/integers.
[42.0,42.0] = sub([42.0,42,42.0], [42,42,42]),
[1,2,3,4,43.0] = sub([1,2,3,4,5,42.0,43.0], [42.0,5]),
@@ -2627,6 +2636,8 @@ subtract(Config) when is_list(Config) ->
ok.
+id(I) -> I.
+
sub_non_matching(A, B) ->
A = sub(A, B).
diff --git a/make/otp_version_tickets b/make/otp_version_tickets
index fd9b36720a..b741526c88 100644
--- a/make/otp_version_tickets
+++ b/make/otp_version_tickets
@@ -1,6 +1,10 @@
-OTP-15805
-OTP-15819
-OTP-15867
-OTP-15879
-OTP-15887
-OTP-15888
+OTP-15900
+OTP-15908
+OTP-15909
+OTP-15910
+OTP-15913
+OTP-15922
+OTP-15930
+OTP-15938
+OTP-15939
+OTP-15941
diff --git a/otp_versions.table b/otp_versions.table
index 956a596877..43a41780b2 100644
--- a/otp_versions.table
+++ b/otp_versions.table
@@ -1,3 +1,4 @@
+OTP-22.0.5 : dialyzer-4.0.2 erts-10.4.4 inets-7.0.9 ssl-9.3.4 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 jinterface-1.10 kernel-6.4.1 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.4 : erts-10.4.3 kernel-6.4.1 ssl-9.3.3 # asn1-5.0.9 common_test-1.17.3 compiler-7.4.2 crypto-4.5.1 debugger-4.2.7 dialyzer-4.0.1 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 stdlib-3.9.2 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.3 : compiler-7.4.2 dialyzer-4.0.1 erts-10.4.2 ssl-9.3.2 stdlib-3.9.2 # asn1-5.0.9 common_test-1.17.3 crypto-4.5.1 debugger-4.2.7 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :
OTP-22.0.2 : compiler-7.4.1 crypto-4.5.1 erts-10.4.1 stdlib-3.9.1 # asn1-5.0.9 common_test-1.17.3 debugger-4.2.7 dialyzer-4.0 diameter-2.2.1 edoc-0.11 eldap-1.2.8 erl_docgen-0.9.1 erl_interface-3.12 et-1.6.4 eunit-2.3.7 ftp-1.0.2 hipe-3.19 inets-7.0.8 jinterface-1.10 kernel-6.4 megaco-3.18.5 mnesia-4.16 observer-2.9.1 odbc-2.12.4 os_mon-2.5 parsetools-2.1.8 public_key-1.6.7 reltool-0.8 runtime_tools-1.13.3 sasl-3.4 snmp-5.3 ssh-4.7.7 ssl-9.3.1 syntax_tools-2.2 tftp-1.0.1 tools-3.2 wx-1.8.8 xmerl-1.3.21 :