diff options
42 files changed, 392 insertions, 167 deletions
diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index 74cc966cbe..6f715ae80d 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -49,7 +49,7 @@ do { \ #endif #ifdef DEBUG -extern Uint erts_no_schedulers; +extern Uint ERTS_WRITE_UNLIKELY(erts_no_schedulers); #endif #define ERTS_SSPA_FORCE_THR_CHECK_PROGRESS 10 diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 66d6d20c4e..730f0a0c64 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -132,14 +132,14 @@ define etp-1 if (($arg0) & 0x3) == 1 # Cons pointer if $etp_flat - printf "<etpf-cons %#x>", ($arg0) + printf "<etpf-cons %p>", (($arg0)-1) else etp-list-1 ($arg0) ($arg1) end else if (($arg0) & 0x3) == 2 if $etp_flat - printf "<etpf-boxed %#x>", ($arg0) + printf "<etpf-boxed %p>", (($arg0)-2) else etp-boxed-1 ($arg0) ($arg1) end @@ -187,7 +187,7 @@ define etp-list-1 # Reentrant # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer if $etp_chart @@ -210,7 +210,7 @@ define etp-list-printable-1 # Returns: $etp_list_printable # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Loop to check if it is a printable string set $etp_list_p = ($arg0) @@ -272,7 +272,7 @@ define etp-list-2 # Reentrant # if (($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer if ($arg1) >= $etp_max_depth @@ -307,7 +307,7 @@ define etpf-cons # Reentrant capable # if ((Eterm)($arg0) & 0x3) != 0x1 - printf "#NotCons<%#x>", ($arg0) + printf "#NotCons<%p>", ($arg0) else # Cons pointer set $etp_flat = 1 @@ -336,13 +336,13 @@ define etp-boxed-1 # Reentrant # if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) + printf "#NotBoxed<%p>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1 end - printf "#BoxedError<%#x>", ($arg0) + printf "#BoxedError<%p>", ($arg0) else if $etp_chart etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \ @@ -390,10 +390,10 @@ define etp-boxed-immediate-1 # Non-reentrant # if (($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", ($arg0) + printf "#NotBoxed<%p>", ($arg0) else if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0 - printf "#BoxedError<%#x>", ($arg0) + printf "#BoxedError<%p>", ($arg0) else set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3) set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF @@ -438,12 +438,12 @@ define etp-boxed-immediate-1 while $etp_boxed_immediate_arity > 0 set $etp_boxed_immediate_p++ if $etp_boxed_immediate_arity > 1 - printf "%#x,", *$etp_boxed_immediate_p + printf "%p,", *$etp_boxed_immediate_p else - printf "%#x", *$etp_boxed_immediate_p + printf "%p", *$etp_boxed_immediate_p if ($etp_boxed_immediate_h == 0xA) set $etp_boxed_immediate_p++ - printf ":%#x", *$etp_boxed_immediate_p + printf ":%p", *$etp_boxed_immediate_p end printf ">" end @@ -558,7 +558,7 @@ define etp-immediate-1 # Reentrant capable # if (($arg0) & 0x3) != 0x3 - printf "#NotImmediate<%#x>", ($arg0) + printf "#NotImmediate<%p>", ($arg0) else if (($arg0) & 0xF) == 0x3 etp-pid-1 ($arg0) @@ -580,7 +580,7 @@ define etp-immediate-1 if (($arg0) == $etp_nil) printf "[]" else - printf "#UnknownImmediate<%#x>", ($arg0) + printf "#UnknownImmediate<%p>", ($arg0) end end end @@ -598,7 +598,7 @@ define etp-atom-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3f) != 0xb - printf "#NotAtom<%#x>", ($arg0) + printf "#NotAtom<%p>", ($arg0) else set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF] set $etp_atom_1_i = ($etp_atom_1_ap)->len @@ -652,7 +652,7 @@ define etp-char-1 # Non-reentrant # if (($arg0) < 0) || (0377 < ($arg0)) - printf "#NotChar<%#x>", ($arg0) + printf "#NotChar<%p>", ($arg0) else if ($arg0) == ($arg1) printf "\\%c", ($arg0) @@ -787,7 +787,7 @@ define etp-pid-1 # Internal pid printf "<0.%u.%u>", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff else - printf "#NotPid<%#x>", ($arg0) + printf "#NotPid<%p>", ($arg0) end end @@ -797,11 +797,11 @@ define etp-extpid-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extpid_1_p->header & 0x3f) != 0x30 - printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header + printf "#NotExternalPid<%p>", $etp_extpid_1_p->header else ## External pid set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff @@ -812,7 +812,7 @@ define etp-extpid-1 set $etp_extpid_1_node = $etp_extpid_1_np->sysname if ($etp_extpid_1_node & 0x3f) != 0xb # Should be an atom - printf "#ExternalPidError<%#x>", ($arg0) + printf "#ExternalPidError<%p>", ($arg0) else if $etp_extpid_1_dep == erts_this_dist_entry printf "<0:" @@ -847,7 +847,7 @@ define etp-port-1 # Internal port printf "#Port<0.%u>", $etp_port_data else - printf "#NotPort<%#x>", ($arg0) + printf "#NotPort<%p>", ($arg0) end end @@ -857,11 +857,11 @@ define etp-extport-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extport_1_p->header & 0x3F) != 0x34 - printf "#NotExternalPort<%#x>", $etp_extport_1->header + printf "#NotExternalPort<%p>", $etp_extport_1->header else ## External port set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff @@ -871,7 +871,7 @@ define etp-extport-1 set $etp_extport_1_node = $etp_extport_1_np->sysname if ($etp_extport_1_node & 0x3f) != 0xb # Should be an atom - printf "#ExternalPortError<%#x>", ($arg0) + printf "#ExternalPortError<%p>", ($arg0) else if $etp_extport_1_dep == erts_this_dist_entry printf "#Port<0:" @@ -893,15 +893,15 @@ define etp-bignum-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_bignum_1_p[0] & 0x3b) != 0x08 - printf "#NotBignum<%#x>", $etp_bignum_1_p[0] + printf "#NotBignum<%p>", $etp_bignum_1_p[0] else set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6) if $etp_bignum_1_i < 1 - printf "#BignumError<%#x>", (Eterm)($arg0) + printf "#BignumError<%p>", (Eterm)($arg0) else if $etp_bignum_1_p[0] & 0x04 printf "-" @@ -932,11 +932,11 @@ define etp-float-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3) if ($etp_float_1_p[0] & 0x3f) != 0x18 - printf "#NotFloat<%#x>", $etp_float_1_p[0] + printf "#NotFloat<%p>", $etp_float_1_p[0] else printf "%f", *(double*)($etp_float_1_p+1) end @@ -951,14 +951,14 @@ define etp-ref-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_ref_1_p = (ErtsORefThing *)((Eterm)($arg0) & ~0x3) if ($etp_ref_1_p->header & 0x3b) != 0x10 - printf "#NotRef<%#x>", $etp_ref_1_p->header + printf "#NotRef<%p>", $etp_ref_1_p->header else if $etp_ref_1_p->header != etp_ref_header && $etp_ref_1_p->header != etp_magic_ref_header - printf "#InternalRefError<%#x>", ($arg0) + printf "#InternalRefError<%p>", ($arg0) else set $etp_magic_ref = 0 set $etp_ref_1_i = 3 @@ -998,11 +998,11 @@ define etp-extref-1 # Non-reentrant # if ((Eterm)($arg0) & 0x3) != 0x2 - printf "#NotBoxed<%#x>", (Eterm)($arg0) + printf "#NotBoxed<%p>", (Eterm)($arg0) else set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3) if ($etp_extref_1_p->header & 0x3F) != 0x38 - printf "#NotExternalRef<%#x>", $etp_extref_1->header + printf "#NotExternalRef<%p>", $etp_extref_1->header else ## External ref set $etp_extref_1_nump = (Uint32 *) 0 @@ -1041,7 +1041,7 @@ define etp-extref-1 end end if $etp_extref_1_error - printf "#ExternalRefError<%#x>", ($arg0) + printf "#ExternalRefError<%p>", ($arg0) else set $etp_extref_1_i-- while $etp_extref_1_i >= 0 @@ -1166,7 +1166,7 @@ define etp-cp-1 if *(Eterm*)($etp_cp) == beam_return_to_trace[0] printf "#Cp<return to trace>" else - printf "#Cp<%#x>", $etp_cp + printf "#Cp<%p>", $etp_cp end end end @@ -1743,7 +1743,7 @@ define etp-term-dump-pid # Internal pid printf "| <0.%04u.%03u> ", $etp_pid_data & 0x7fff, ($etp_pid_data >> 15) & 0x1fff else - printf "| #NotPid<%#x> ", ($arg0) + printf "| #NotPid<%p> ", ($arg0) end end @@ -3324,7 +3324,7 @@ define etp-offheapdump set $etp_offheapdump_p = $etp_offheapdump_p->next set $etp_offheapdump_i++ else - printf "#TaggedPtr<%#x>", $etp_offheapdump_p + printf "#TaggedPtr<%p>", $etp_offheapdump_p set $etp_offheapdump_p = 0 end end @@ -3363,12 +3363,12 @@ define etp-search-heaps # # Non-reentrant # - printf "%% Search all (<%u) process heaps for ", erts_max_processes + printf "%% Search all (<%u) process heaps for ", erts_proc.r.o.max set $etp_flat = 1 etp-1 ($arg0) 0 set $etp_flat = 0 printf ":...\n" - etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3)) + etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~(Eterm)3)) end define etp-search-heaps-1 @@ -3376,8 +3376,8 @@ define etp-search-heaps-1 # # Non-reentrant # - set $etp_search_heaps_q = erts_max_processes / 10 - set $etp_search_heaps_r = erts_max_processes % 10 + set $etp_search_heaps_q = erts_proc.r.o.max / 10 + set $etp_search_heaps_r = erts_proc.r.o.max % 10 set $etp_search_heaps_t = 10 set $etp_search_heaps_m = $etp_search_heaps_q if $etp_search_heaps_r > 0 @@ -3387,7 +3387,7 @@ define etp-search-heaps-1 set $etp_search_heaps_i = 0 set $etp_search_heaps_found = 0 while $etp_search_heaps_i < erts_proc.r.o.max - set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$proc_ix]) + set $proc = (Process *) *((UWord *) &erts_proc.r.o.tab[$etp_search_heaps_i]) if $proc if ($proc->heap <= ($arg0)) && \ (($arg0) < $proc->hend) @@ -3404,7 +3404,7 @@ define etp-search-heaps-1 while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth) set $etp_search_heaps_cnt++ if (&($etp_search_heaps_p->mem) <= ($arg0)) && \ - (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size) + (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->used_size) printf "process_tab[%d]->mbuf(%d)+%d\n", \ $etp_search_heaps_i, $etp_search_heaps_cnt, \ ($arg0)-&($etp_search_heaps_p->mem) @@ -3700,8 +3700,8 @@ define etp-overlapped-heaps # Non-reentrant # printf "%% Dumping heap addresses to \"etp-commands.bin\"\n" - set $etp_overlapped_heaps_q = erts_max_processes / 10 - set $etp_overlapped_heaps_r = erts_max_processes % 10 + set $etp_overlapped_heaps_q = erts_proc.r.o.max / 10 + set $etp_overlapped_heaps_r = erts_proc.r.o.max % 10 set $etp_overlapped_heaps_t = 10 set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q if $etp_overlapped_heaps_r > 0 @@ -3727,7 +3727,7 @@ define etp-overlapped-heaps append binary value etp-commands.bin 'p' append binary value etp-commands.bin 's' append binary value etp-commands.bin '\0' - while $etp_overlapped_heaps_i < erts_max_processes + while $etp_overlapped_heaps_i < erts_proc.r.o.max if process_tab[$etp_overlapped_heaps_i] append binary value etp-commands.bin \ (Eterm)$etp_overlapped_heaps_i @@ -3868,7 +3868,7 @@ define etp-chart-entry-1 append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm)) append binary value etp-commands.bin (Eterm)$etp_chart_id append binary value etp-commands.bin (Eterm)($arg1) -# printf "<dumped %#x %lu %lu %lu>", ($arg0)&~0x3, \ +# printf "<dumped %p %lu %lu %lu>", ($arg0)&~0x3, \ # (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1) end diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex ef402b5fee..c74ce3ce2d 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex 674d0d27fa..2aec433bcb 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differindex 0313988e3e..0c032e8e91 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 661bcd8413..13fdd7908b 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex ec4d6153d1..16e17b870b 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 62dc8702e7..866f9df79f 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex 669149df82..d81bc08282 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 6d3528c2dc..a8d492dfa5 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex b3af713809..dd08111aad 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex fc2bf6f6bd..0f7dd6efbe 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex 1d89174b25..942f29a11c 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/net.beam b/erts/preloaded/ebin/net.beam Binary files differindex f61b2b4a69..88d546a3af 100644 --- a/erts/preloaded/ebin/net.beam +++ b/erts/preloaded/ebin/net.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex c882e4fad4..7871b64991 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex cf671bf8f4..90d9596fe6 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 24911123f9..19935eeee3 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 0efd954e50..a2c5f2f336 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex ff9268ad38..b7c4cbe0a8 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex d319d7a343..bd51c3b271 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam Binary files differindex 558a886565..27efe10f38 100644 --- a/erts/preloaded/ebin/socket.beam +++ b/erts/preloaded/ebin/socket.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 9610c94ac2..8b7c5fe2ef 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/lib/compiler/src/beam_except.erl b/lib/compiler/src/beam_except.erl index 28c89782c9..2305502800 100644 --- a/lib/compiler/src/beam_except.erl +++ b/lib/compiler/src/beam_except.erl @@ -140,8 +140,11 @@ fix_block_1([{set,[],[],{alloc,Live,{F1,F2,Needed0,F3}}}|Is], Words) -> [{set,[],[],{alloc,Live,{F1,F2,Needed,F3}}}|Is] end; fix_block_1([I|Is], Words) -> - [I|fix_block_1(Is, Words)]. - + [I|fix_block_1(Is, Words)]; +fix_block_1([], _Words) -> + %% Rare. The heap allocation was probably done by a binary + %% construction instruction. + []. dig_out_fc(Arity, Is0) -> Regs0 = maps:from_list([{{x,X},{arg,X}} || X <- seq(0, Arity-1)]), diff --git a/lib/compiler/src/beam_ssa_codegen.erl b/lib/compiler/src/beam_ssa_codegen.erl index c2d5035b19..07f4c8b461 100644 --- a/lib/compiler/src/beam_ssa_codegen.erl +++ b/lib/compiler/src/beam_ssa_codegen.erl @@ -1016,6 +1016,14 @@ bif_fail({catch_tag,_}) -> {f,0}. next_block([]) -> none; next_block([{Next,_}|_]) -> Next. +%% Certain instructions (such as get_map_element or is_nonempty_list) +%% are only used in guards and **must** have a non-zero label; +%% otherwise, the loader will refuse to load the +%% module. ensure_label/2 replaces a zero label with the "ultimate +%% failure" label to make the module loadable. The instruction that +%% have had the zero label replaced is **not** supposed to ever fail +%% and actually jump to the label. + ensure_label(Fail0, #cg{ultimate_fail=Lbl}) -> case bif_fail(Fail0) of {f,0} -> {f,Lbl}; @@ -1160,6 +1168,11 @@ cg_block([#cg_set{op=call}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,_Fail}, St) -> %% A call in try/catch block. cg_block([I], none, St); +cg_block([#cg_set{op=get_map_element,dst=Dst0,args=Args0}, + #cg_set{op=succeeded,dst=Bool}], {Bool,Fail0}, St) -> + [Dst,Map,Key] = beam_args([Dst0|Args0], St), + Fail = ensure_label(Fail0, St), + {[{get_map_elements,Fail,Map,{list,[Key,Dst]}}],St}; cg_block([#cg_set{op=Op,dst=Dst0,args=Args0}=I, #cg_set{op=succeeded,dst=Bool}], {Bool,Fail}, St) -> [Dst|Args] = beam_args([Dst0|Args0], St), @@ -1606,8 +1619,6 @@ cg_test({float,Op0}, Fail, Args, Dst, #cg_set{anno=Anno}) -> '/' -> fdiv end, [line(Anno),{bif,Op,Fail,Args,Dst}]; -cg_test(get_map_element, Fail, [Map,Key], Dst, _I) -> - [{get_map_elements,Fail,Map,{list,[Key,Dst]}}]; cg_test(peek_message, Fail, [], Dst, _I) -> [{loop_rec,Fail,{x,0}}|copy({x,0}, Dst)]; cg_test(put_map, Fail, [{atom,exact},SrcMap|Ss], Dst, Set) -> diff --git a/lib/compiler/src/beam_ssa_dead.erl b/lib/compiler/src/beam_ssa_dead.erl index bb43a550ae..64b9b3e222 100644 --- a/lib/compiler/src/beam_ssa_dead.erl +++ b/lib/compiler/src/beam_ssa_dead.erl @@ -436,8 +436,22 @@ get_phi_arg([{Val,From}|_], From) -> Val; get_phi_arg([_|As], From) -> get_phi_arg(As, From). eval_terminator(#b_br{bool=#b_var{}=Bool}=Br, Bs, _St) -> - Val = get_value(Bool, Bs), - beam_ssa:normalize(Br#b_br{bool=Val}); + case get_value(Bool, Bs) of + #b_literal{val=Val}=Lit -> + case is_boolean(Val) of + true -> + beam_ssa:normalize(Br#b_br{bool=Lit}); + false -> + %% Non-boolean literal. This means that this `br` + %% terminator will never actually be reached with + %% these bindings. (There must be a previous two-way + %% branch that branches the other way when Bool + %% is bound to a non-boolean literal.) + none + end; + #b_var{}=Var -> + beam_ssa:normalize(Br#b_br{bool=Var}) + end; eval_terminator(#b_br{bool=#b_literal{}}=Br, _Bs, _St) -> beam_ssa:normalize(Br); eval_terminator(#b_switch{arg=Arg,fail=Fail,list=List}=Sw, Bs, St) -> @@ -680,11 +694,8 @@ will_succeed_test(is_list, is_nonempty_list) -> maybe; will_succeed_test(is_nonempty_list, is_list) -> yes; -will_succeed_test(T1, T2) -> - case is_numeric_test(T1) andalso is_numeric_test(T2) of - true -> maybe; - false -> no - end. +will_succeed_test(_T1, _T2) -> + maybe. will_succeed_1('=:=', A, '<', B) -> if @@ -769,11 +780,6 @@ will_succeed_vars('==', Val1, '/=', Val2) when Val1 == Val2 -> no; will_succeed_vars(_, _, _, _) -> maybe. -is_numeric_test(is_float) -> true; -is_numeric_test(is_integer) -> true; -is_numeric_test(is_number) -> true; -is_numeric_test(_) -> false. - eval_type_test(Test, Arg) -> case eval_type_test_1(Test, Arg) of true -> yes; diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 2ea7e89dfa..57fd7fec60 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -23,7 +23,7 @@ -include("beam_ssa_opt.hrl"). -import(lists, [all/2,any/2,droplast/1,foldl/3,last/1,member/2, - keyfind/3,partition/2,reverse/1,reverse/2, + keyfind/3,reverse/1,reverse/2, sort/1,split/2]). -define(UNICODE_INT, #t_integer{elements={0,16#10FFFF}}). @@ -896,11 +896,15 @@ type(call, [#b_remote{mod=#b_literal{val=Mod}, {_,_} -> #t_tuple{} end; - {erlang,'++',[List1,List2]} -> - case get_type(List1, Ts) =:= cons orelse - get_type(List2, Ts) =:= cons of - true -> cons; - false -> list + {erlang,'++',[LHS,RHS]} -> + LType = get_type(LHS, Ts), + RType = get_type(RHS, Ts), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list. + join(list, RType) end; {erlang,'--',[_,_]} -> list; @@ -1388,24 +1392,11 @@ get_type(#b_literal{val=Val}, _Ts) -> %% type for L. For example, if L was known to be 'list', subtracting %% 'cons' would give 'nil' as the only possible type. The result of the %% subtraction for L will be added to FailTypes. -%% -%% Here is another example, asking about the variable Bool: -%% -%% Head = bif:hd L -%% Bool = succeeded Head -%% -%% 'succeeded Head' will evaluate to 'true' if the instrution that -%% defined Head succeeded. In this case, it is the 'bif:hd L' -%% instruction, which will succeed if L is 'cons'. Thus, the meet of -%% the previous type for L and 'cons' will be added to SuccTypes. -%% -%% If 'succeeded Head' evaluates to 'false', it means that 'bif:hd L' -%% failed and that L is not 'cons'. 'cons' can be subtracted from the -%% previously known type for L and the result put in FailTypes. infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) -> #{V:=#b_set{op=Op,args=Args}} = Ds, - Types0 = infer_type(Op, Args, Ds), + PosTypes0 = infer_type(Op, Args, Ds), + NegTypes0 = infer_type_negative(Op, Args, Ds), %% We must be careful with types inferred from '=:='. %% @@ -1416,13 +1407,17 @@ infer_types_br(#b_var{}=V, Ts, #d{ds=Ds}) -> %% %% However, it is safe to subtract a type inferred from '=:=' if %% it is single-valued, e.g. if it is [] or the atom 'true'. - EqTypes0 = infer_eq_type(Op, Args, Ts, Ds), - {Types1,EqTypes} = partition(fun({_,T}) -> - is_singleton_type(T) - end, EqTypes0), - Types = Types1 ++ Types0, - {meet_types(EqTypes++Types, Ts),subtract_types(Types, Ts)}. + EqTypes = infer_eq_type(Op, Args, Ts, Ds), + NegTypes1 = [P || {_,T}=P <- EqTypes, is_singleton_type(T)], + + PosTypes = EqTypes ++ PosTypes0, + SuccTs = meet_types(PosTypes, Ts), + + NegTypes = NegTypes0 ++ NegTypes1, + FailTs = subtract_types(NegTypes, Ts), + + {SuccTs,FailTs}. infer_types_switch(V, Lit, Ts, #d{ds=Ds}) -> Types = infer_eq_type({bif,'=:='}, [V, Lit], Ts, Ds), @@ -1457,6 +1452,19 @@ infer_eq_lit(#b_set{op=get_tuple_element, [{Tuple,#t_tuple{size=Index,elements=Es}}]; infer_eq_lit(_, _) -> []. +infer_type_negative(Op, Args, Ds) -> + case is_negative_inference_safe(Op, Args) of + true -> + infer_type(Op, Args, Ds); + false -> + [] + end. + +%% Conservative list of instructions for which negative +%% inference is safe. +is_negative_inference_safe(is_nonempty_list, _Args) -> true; +is_negative_inference_safe(_, _) -> false. + infer_type({bif,element}, [#b_literal{val=Pos},#b_var{}=Tuple], _Ds) -> if is_integer(Pos), 1 =< Pos -> diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 09a5a6c104..ebe9631e09 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -2844,10 +2844,14 @@ call_return_type_1(erlang, setelement, 3, Vst) -> setelement(3, TupleType, #{}) end; call_return_type_1(erlang, '++', 2, Vst) -> - case get_term_type({x,0}, Vst) =:= cons orelse - get_term_type({x,1}, Vst) =:= cons of - true -> cons; - false -> list + LType = get_term_type({x,0}, Vst), + RType = get_term_type({x,1}, Vst), + case LType =:= cons orelse RType =:= cons of + true -> + cons; + false -> + %% `[] ++ RHS` yields RHS, even if RHS is not a list + join(list, RType) end; call_return_type_1(erlang, '--', 2, _Vst) -> list; diff --git a/lib/compiler/test/beam_except_SUITE.erl b/lib/compiler/test/beam_except_SUITE.erl index 8e3b373d29..67947dc292 100644 --- a/lib/compiler/test/beam_except_SUITE.erl +++ b/lib/compiler/test/beam_except_SUITE.erl @@ -21,7 +21,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - multiple_allocs/1,bs_get_tail/1,coverage/1]). + multiple_allocs/1,bs_get_tail/1,coverage/1, + binary_construction_allocation/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -32,7 +33,8 @@ groups() -> [{p,[parallel], [multiple_allocs, bs_get_tail, - coverage]}]. + coverage, + binary_construction_allocation]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -118,6 +120,20 @@ coverage(_) -> fake_function_clause(A) -> error(function_clause, [A,42.0]). + +binary_construction_allocation(_Config) -> + ok = do_binary_construction_allocation("PUT"), + ok. + +do_binary_construction_allocation(Req) -> + %% Allocation for building the error term was done by the + %% bs_init2 instruction. beam_except crashed because it expected + %% an explicit allocation instruction. + ok = case Req of + "POST" -> {error, <<"BAD METHOD ", Req/binary>>, Req}; + _ -> ok + end. + id(I) -> I. -file("fake.erl", 1). diff --git a/lib/compiler/test/beam_ssa_SUITE.erl b/lib/compiler/test/beam_ssa_SUITE.erl index 15cf9bcbf3..a741ebbdf9 100644 --- a/lib/compiler/test/beam_ssa_SUITE.erl +++ b/lib/compiler/test/beam_ssa_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, calls/1,tuple_matching/1,recv/1,maps/1, - cover_ssa_dead/1,combine_sw/1,share_opt/1]). + cover_ssa_dead/1,combine_sw/1,share_opt/1, + beam_ssa_dead_crash/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -37,7 +38,8 @@ groups() -> maps, cover_ssa_dead, combine_sw, - share_opt + share_opt, + beam_ssa_dead_crash ]}]. init_per_suite(Config) -> @@ -492,6 +494,60 @@ do_share_opt(A) -> end, receive after 1 -> ok end. +beam_ssa_dead_crash(_Config) -> + not_A_B = do_beam_ssa_dead_crash(id(false), id(true)), + not_A_not_B = do_beam_ssa_dead_crash(false, false), + neither = do_beam_ssa_dead_crash(true, false), + neither = do_beam_ssa_dead_crash(true, true), + ok. + +do_beam_ssa_dead_crash(A, B) -> + %% beam_ssa_dead attempts to shortcut branches that branch other + %% branches. When a two-way branch is encountered, beam_ssa_dead + %% will simulate execution along both paths, in the hope that both + %% paths happens to end up in the same place. + %% + %% During the simulated execution of this function, the boolean + %% varible for a `br` instruction would be replaced with the + %% literal atom `nil`, which is not allowed, and would crash the + %% compiler. In practice, during the actual execution, control + %% would never be transferred to that `br` instruction when the + %% variable in question had the value `nil`. + %% + %% beam_ssa_dead has been updated to immediately abort the search + %% along the current path if there is an attempt to substitute a + %% non-boolean value into a `br` instruction. + + case + case not A of + false -> + false; + true -> + B + end + of + V + when + V /= nil + andalso + V /= false -> + not_A_B; + _ -> + case + case not A of + false -> + false; + true -> + not B + end + of + true -> + not_A_not_B; + false -> + neither + end + end. + %% The identity function. id(I) -> I. diff --git a/lib/compiler/test/beam_type_SUITE.erl b/lib/compiler/test/beam_type_SUITE.erl index 0d1680fb15..2297c2e0f5 100644 --- a/lib/compiler/test/beam_type_SUITE.erl +++ b/lib/compiler/test/beam_type_SUITE.erl @@ -24,7 +24,7 @@ integers/1,numbers/1,coverage/1,booleans/1,setelement/1, cons/1,tuple/1,record_float/1,binary_float/1,float_compare/1, arity_checks/1,elixir_binaries/1,find_best/1, - test_size/1,cover_lists_functions/1]). + test_size/1,cover_lists_functions/1,list_append/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -47,7 +47,8 @@ groups() -> elixir_binaries, find_best, test_size, - cover_lists_functions + cover_lists_functions, + list_append ]}]. init_per_suite(Config) -> @@ -501,5 +502,11 @@ cover_lists_functions(Config) -> true = is_list(Zipped), ok. +list_append(_Config) -> + %% '++'/2 has a quirk where it returns the right-hand argument as-is when + %% the left-hand is []. + hello = id([]) ++ id(hello), + ok. + id(I) -> I. diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index e5611e99d1..72016c6d76 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -29,7 +29,8 @@ bs_shadowed_size_var/1, cover_v3_kernel_1/1,cover_v3_kernel_2/1,cover_v3_kernel_3/1, cover_v3_kernel_4/1,cover_v3_kernel_5/1, - non_variable_apply/1,name_capture/1,fun_letrec_effect/1]). + non_variable_apply/1,name_capture/1,fun_letrec_effect/1, + get_map_element/1]). -include_lib("common_test/include/ct.hrl"). @@ -57,7 +58,8 @@ groups() -> bs_shadowed_size_var, cover_v3_kernel_1,cover_v3_kernel_2,cover_v3_kernel_3, cover_v3_kernel_4,cover_v3_kernel_5, - non_variable_apply,name_capture,fun_letrec_effect + non_variable_apply,name_capture,fun_letrec_effect, + get_map_element ]}]. @@ -95,6 +97,7 @@ end_per_group(_GroupName, Config) -> ?comp(non_variable_apply). ?comp(name_capture). ?comp(fun_letrec_effect). +?comp(get_map_element). try_it(Mod, Conf) -> Src = filename:join(proplists:get_value(data_dir, Conf), diff --git a/lib/compiler/test/core_SUITE_data/get_map_element.core b/lib/compiler/test/core_SUITE_data/get_map_element.core new file mode 100644 index 0000000000..092b5e71eb --- /dev/null +++ b/lib/compiler/test/core_SUITE_data/get_map_element.core @@ -0,0 +1,18 @@ +module 'get_map_element' ['get_map_element'/0] +attributes [] + +'get_map_element'/0 = + fun () -> + apply 'match_map'/1(~{'foo'=>'bar'}~) + +'match_map'/1 = + fun (_0) -> + case _0 of + <~{'foo':='bar'}~> when 'true' -> + 'ok' + %% It will be undefined behaviour at runtime if no + %% clause of the case can be selected. That can't + %% happen for this module, because match_map/1 is + %% always called with a matching map argument. + end +end diff --git a/lib/compiler/test/guard_SUITE.erl b/lib/compiler/test/guard_SUITE.erl index ed0a56f064..cea7a374cd 100644 --- a/lib/compiler/test/guard_SUITE.erl +++ b/lib/compiler/test/guard_SUITE.erl @@ -35,7 +35,8 @@ basic_andalso_orelse/1,traverse_dcd/1, check_qlc_hrl/1,andalso_semi/1,t_tuple_size/1,binary_part/1, bad_constants/1,bad_guards/1, - guard_in_catch/1,beam_bool_SUITE/1]). + guard_in_catch/1,beam_bool_SUITE/1, + repeated_type_tests/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -53,7 +54,8 @@ groups() -> rel_ops,rel_op_combinations, literal_type_tests,basic_andalso_orelse,traverse_dcd, check_qlc_hrl,andalso_semi,t_tuple_size,binary_part, - bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE]}]. + bad_constants,bad_guards,guard_in_catch,beam_bool_SUITE, + repeated_type_tests]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -2261,6 +2263,25 @@ maps() -> evidence(#{0 := Charge}) when 0; #{[] => Charge} == #{[] => 42} -> ok. +repeated_type_tests(_Config) -> + binary = repeated_type_test(<<42>>), + bitstring = repeated_type_test(<<1:1>>), + other = repeated_type_test(atom), + ok. + +repeated_type_test(T) -> + %% Test for a bug in beam_ssa_dead. + if is_bitstring(T) -> + if is_binary(T) -> %This test would be optimized away. + binary; + true -> + bitstring + end; + true -> + other + end. + + %% Call this function to turn off constant propagation. id(I) -> I. diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 7faef93609..964ede9bc9 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -19,6 +19,8 @@ %% -module(code). +-include_lib("kernel/include/logger.hrl"). + %% This is the interface module to the code server. It also contains %% some implementation details. See also related modules: code_*.erl %% in this directory. @@ -707,8 +709,20 @@ do_s(Lib) -> start_get_mode() -> case init:get_argument(mode) of - {ok,[["embedded"]]} -> - embedded; + {ok, [FirstMode | Rest]} -> + case Rest of + [] -> + ok; + _ -> + ?LOG_WARNING("Multiple -mode given to erl, using the first, ~p", + [FirstMode]) + end, + case FirstMode of + ["embedded"] -> + embedded; + _ -> + interactive + end; _ -> interactive end. diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index bc4f09a023..c8c631ab23 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -141,7 +141,7 @@ init([]) -> modules => [logger_sup]}, case init:get_argument(mode) of - {ok, [["minimal"]]} -> + {ok, [["minimal"]|_]} -> {ok, {SupFlags, [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}}; _ -> diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 4f0847084f..6b133f8d6b 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -41,7 +41,7 @@ big_boot_embedded/1, module_status/1, native_early_modules/1, get_mode/1, - normalized_paths/1]). + normalized_paths/1, mult_embedded_flags/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -72,7 +72,8 @@ all() -> on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, module_status, - big_boot_embedded, native_early_modules, get_mode, normalized_paths]. + big_boot_embedded, native_early_modules, get_mode, normalized_paths, + mult_embedded_flags]. %% These need to run in order groups() -> [{sequence, [sequence], [on_load_update, @@ -354,7 +355,7 @@ load_abs(Config) when is_list(Config) -> ensure_loaded(Config) when is_list(Config) -> {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of - {ok, [["embedded"]]} -> + {ok, [["embedded"] | _]} -> {error, embedded} = code:ensure_loaded(code_b_test), {error, badarg} = code:ensure_loaded(34), ok; @@ -1836,6 +1837,28 @@ do_normalized_paths([M|Ms]) -> do_normalized_paths([]) -> ok. +%% Make sure that the extra -mode flags are ignored +mult_embedded_flags(_Config) -> + Modes = [{" -mode embedded", embedded}, + {" -mode interactive", interactive}, + {" -mode invalid", interactive}], + + [ begin + {ArgMode, ExpectedMode} = Mode, + {ok, Node} = start_node(mode_test, ArgMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || Mode <- Modes], + + [ begin + {ArgIgnoredMode, _} = IgnoredMode, + {ArgRelevantMode, ExpectedMode} = RelevantMode, + {ok, Node} = start_node(mode_test, ArgRelevantMode ++ ArgIgnoredMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || IgnoredMode <- Modes, RelevantMode <- Modes], + ok. + %% Test that module_status/1 behaves as expected module_status(_Config) -> case test_server:is_cover() of diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index e6bcedd1b1..38b5c93521 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -74,7 +74,7 @@ decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> %%-------------------------------------------------------------------- --spec pbdkdf1(string(), iodata(), integer(), atom()) -> binary(). +-spec pbdkdf1(iodata(), iodata(), integer(), atom()) -> binary(). %% %% Description: Implements password based decryption key derive function 1. %% Exported mainly for testing purposes. @@ -86,7 +86,7 @@ pbdkdf1(Password, Salt, Count, Hash) -> do_pbdkdf1(Result, Count-1, Result, Hash). %%-------------------------------------------------------------------- --spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer()) +-spec pbdkdf2(iodata(), iodata(), integer(), integer(), fun(), atom(), integer()) -> binary(). %% %% Description: Implements password based decryption key derive function 2. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6af65e09f2..f99c0e2b7a 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -125,7 +125,9 @@ protocol_extensions/0, session_id/0, error_alert/0, - srp_param_type/0]). + tls_alert/0, + srp_param_type/0, + named_curve/0]). %% ------------------------------------------------------------------------------------------------------- @@ -192,6 +194,7 @@ | rsa_pss_pss_sha512 | rsa_pkcs1_sha1 | ecdsa_sha1. + -type kex_algo() :: rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | @@ -236,7 +239,7 @@ sect163r2 | secp160k1 | secp160r1 | - secp160r2. + secp160r2. % exported -type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. @@ -279,7 +282,7 @@ bad_certificate_status_response | bad_certificate_hash_value | unknown_psk_identity | - no_application_protocol. + no_application_protocol. % exported %% ------------------------------------------------------------------------------------------------------- -type common_option() :: {protocol, protocol()} | diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index 06b1b005a5..2d57b72f7b 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,7 +32,11 @@ -include("ssl_record.hrl"). -include("ssl_internal.hrl"). --export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]). +-export([decode/1, + own_alert_txt/1, + alert_txt/1, + alert_txt/4, + reason_code/4]). %%==================================================================== %% Internal application API @@ -48,20 +52,29 @@ decode(Bin) -> decode(Bin, [], 0). %%-------------------------------------------------------------------- -%% -spec reason_code(#alert{}, client | server) -> -%% {tls_alert, unicode:chardata()} | closed. -%-spec reason_code(#alert{}, client | server) -> closed | {essl, string()}. +-spec reason_code(#alert{}, client | server, ProtocolName::string(), StateName::atom()) -> + {tls_alert, {atom(), unicode:chardata()}} | closed. %% %% Description: Returns the error reason that will be returned to the %% user. %%-------------------------------------------------------------------- -reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> +reason_code(#alert{description = ?CLOSE_NOTIFY}, _, _, _) -> closed; -reason_code(#alert{description = Description, role = Role} = Alert, Role) -> - {tls_alert, {description_atom(Description), own_alert_txt(Alert)}}; -reason_code(#alert{description = Description} = Alert, Role) -> - {tls_alert, {description_atom(Description), alert_txt(Alert#alert{role = Role})}}. +reason_code(#alert{description = Description, role = Role} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, own_alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}; +reason_code(#alert{description = Description} = Alert, Role, ProtocolName, StateName) -> + Txt = lists:flatten(alert_txt(ProtocolName, Role, StateName, alert_txt(Alert))), + {tls_alert, {description_atom(Description), Txt}}. + +%%-------------------------------------------------------------------- +-spec alert_txt(string(), server | client, StateNam::atom(), string()) -> string(). +%% +%% Description: Generates alert text for log or string part of error return. +%%-------------------------------------------------------------------- +alert_txt(ProtocolName, Role, StateName, Txt) -> + io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]). %%-------------------------------------------------------------------- -spec own_alert_txt(#alert{}) -> string(). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 345db7510f..7047c01657 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -328,34 +328,33 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> %%==================================================================== %% Alert and close handling %%==================================================================== -handle_own_alert(Alert, _, StateName, +handle_own_alert(Alert0, _, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = SslOpts} = State) -> try %% Try to tell the other side - send_alert(Alert, StateName, State) + send_alert(Alert0, StateName, State) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, try %% Try to tell the local user - log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, - Alert#alert{role = Role}), + Alert = Alert0#alert{role = Role}, + log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok end, {stop, {shutdown, own_alert}, State}. -handle_normal_shutdown(Alert, _, #state{static_env = #static_env{role = Role, - socket = Socket, - transport_cb = Transport, - protocol_cb = Connection, - tracker = Tracker}, - handshake_env = #handshake_env{renegotiation = {false, first}}, - start_or_recv_from = StartFrom} = State) -> +handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, + socket = Socket, + transport_cb = Transport, + protocol_cb = Connection, + tracker = Tracker}, + handshake_env = #handshake_env{renegotiation = {false, first}}, + start_or_recv_from = StartFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, Connection); + alert_user(Pids, Transport, Tracker,Socket, StartFrom, Alert, Role, StateName, Connection); handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = Role, socket = Socket, @@ -366,9 +365,9 @@ handle_normal_shutdown(Alert, StateName, #state{static_env = #static_env{role = socket_options = Opts, start_or_recv_from = RecvFrom} = State) -> Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, Connection). + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, RecvFrom, Alert, Role, StateName, Connection). -handle_alert(#alert{level = ?FATAL} = Alert, StateName, +handle_alert(#alert{level = ?FATAL} = Alert0, StateName, #state{static_env = #static_env{role = Role, socket = Socket, host = Host, @@ -382,11 +381,11 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName, session = Session, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), - StateName, Alert#alert{role = opposite_role(Role)}), + StateName, Alert), Pids = Connection:pids(State), - alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, - opposite_role(Role), Connection), + alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -396,13 +395,14 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, StateName, State) -> handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; -handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, +handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, internal}}, ssl_options = SslOpts} = State) -> + Alert = Alert0#alert{role = opposite_role(Role)}, log_alert(SslOpts#ssl_options.log_level, Role, - Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), + Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; @@ -1445,7 +1445,7 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName, } = State) when StateName =/= connection -> Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker,Socket, - StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), + StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, StateName, Connection), {stop, {shutdown, normal}, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_env{socket = Socket, @@ -2907,22 +2907,22 @@ send_user(Pid, Msg) -> Pid ! Msg, ok. -alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, Connection); -alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, connection, Opts, Pid, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, Opts#socket_options.active, Pid, From, Alert, Role, StateName, Connection); +alert_user(Pids, Transport, Tracker, Socket,_, _, _, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection). -alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, Connection) -> - alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, Connection). +alert_user(Pids, Transport, Tracker, Socket, From, Alert, Role, StateName, Connection) -> + alert_user(Pids, Transport, Tracker, Socket, false, no_pid, From, Alert, Role, StateName, Connection). -alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, _) when From =/= undefined -> +alert_user(_, _, _, _, false = Active, Pid, From, Alert, Role, StateName, Connection) when From =/= undefined -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. - ReasonCode = ssl_alert:reason_code(Alert, Role), + ReasonCode = ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connection) -> - case ssl_alert:reason_code(Alert, Role) of +alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, StateName, Connection) -> + case ssl_alert:reason_code(Alert, Role, Connection:protocol_name(), StateName) of closed -> send_or_reply(Active, Pid, From, {ssl_closed, Connection:socket(Pids, Transport, Socket, Tracker)}); @@ -2933,11 +2933,11 @@ alert_user(Pids, Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Con log_alert(Level, Role, ProtocolName, StateName, #alert{role = Role} = Alert) -> Txt = ssl_alert:own_alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report); log_alert(Level, Role, ProtocolName, StateName, Alert) -> Txt = ssl_alert:alert_txt(Alert), - Report = io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]), + Report = ssl_alert:alert_txt(ProtocolName, Role, StateName, Txt), ssl_logger:notice(Level, Report). invalidate_session(client, Host, Port, Session) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 3b161a0c8a..733ee993a8 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -429,14 +429,17 @@ check_result(Pid, Msg) -> end. check_server_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), ok end. check_server_alert(Server, Client, Alert) -> receive - {Server, {error, {tls_alert, {Alert, _}}}} -> + {Server, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), receive - {Client, {error, {tls_alert, {Alert, _}}}} -> + {Client, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), ok; {Client, {error, closed}} -> ok @@ -444,20 +447,35 @@ check_server_alert(Server, Client, Alert) -> end. check_client_alert(Pid, Alert) -> receive - {Pid, {error, {tls_alert, {Alert, _}}}} -> + {Pid, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), ok end. check_client_alert(Server, Client, Alert) -> receive - {Client, {error, {tls_alert, {Alert, _}}}} -> + {Client, {error, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), receive - {Server, {error, {tls_alert, {Alert, _}}}} -> + {Server, {error, {tls_alert, {Alert, STxt}}}} -> + check_server_txt(STxt), ok; {Server, {error, closed}} -> ok end end. +check_server_txt("TLS server" ++ _) -> + ok; +check_server_txt("DTLS server" ++ _) -> + ok; +check_server_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). +check_client_txt("TLS client" ++ _) -> + ok; +check_client_txt("DTLS client" ++ _) -> + ok; +check_client_txt(Txt) -> + ct:fail({expected_server, {got, Txt}}). wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive diff --git a/system/doc/system_principles/system_principles.xml b/system/doc/system_principles/system_principles.xml index 500522d778..6986d93ed9 100644 --- a/system/doc/system_principles/system_principles.xml +++ b/system/doc/system_principles/system_principles.xml @@ -156,7 +156,8 @@ init:stop()</pre> command-line flag <c>-mode</c>.</p> <pre> % <input>erl -mode embedded</input></pre> - <p>Default mode is <c>interactive</c>.</p> + <p>Default mode is <c>interactive</c> and extra <c>-mode</c> flags are + ignored.</p> <p>The mode properties are as follows:</p> <list type="bulleted"> <item>In embedded mode, all code is loaded during system startup |