aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/beam_block.erl69
-rw-r--r--lib/compiler/src/beam_type.erl28
-rw-r--r--lib/compiler/src/beam_utils.erl45
-rw-r--r--lib/compiler/src/beam_validator.erl7
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/abs.erl9
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl14
-rw-r--r--lib/hipe/cerl/erl_bif_types.erl6
-rw-r--r--lib/observer/include/etop.hrl2
-rw-r--r--lib/observer/src/crashdump_viewer.erl6
-rw-r--r--lib/observer/src/observer_pro_wx.erl2
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl46
-rw-r--r--lib/ssh/src/ssh_transport.erl2
-rw-r--r--lib/ssh/test/Makefile1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE.erl814
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image38
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image71
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image61
-rwxr-xr-xlib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all87
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa12
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa27
-rw-r--r--lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub1
-rw-r--r--lib/ssl/src/ssl_cipher.erl2
-rw-r--r--lib/ssl/test/ssl_test_lib.erl124
-rw-r--r--lib/stdlib/doc/src/io.xml14
-rw-r--r--lib/stdlib/src/erl_eval.erl128
-rw-r--r--lib/stdlib/src/erl_tar.erl2
-rw-r--r--lib/stdlib/src/eval_bits.erl21
-rw-r--r--lib/stdlib/test/erl_eval_SUITE.erl214
-rw-r--r--lib/syntax_tools/src/erl_prettypr.erl3
-rw-r--r--lib/syntax_tools/src/erl_syntax.erl10
48 files changed, 1700 insertions, 255 deletions
diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl
index fe1ce6f60b..570fc05ae5 100644
--- a/lib/compiler/src/beam_block.erl
+++ b/lib/compiler/src/beam_block.erl
@@ -23,7 +23,7 @@
-module(beam_block).
-export([module/2]).
--import(lists, [reverse/1,reverse/2,foldl/3,member/2]).
+-import(lists, [reverse/1,reverse/2,member/2]).
-spec module(beam_utils:module_code(), [compile:option()]) ->
{'ok',beam_utils:module_code()}.
@@ -41,7 +41,8 @@ function({function,Name,Arity,CLabel,Is0}) ->
Is4 = move_allocates(Is3),
Is5 = beam_utils:live_opt(Is4),
Is6 = opt_blocks(Is5),
- Is = beam_utils:delete_live_annos(Is6),
+ Is7 = beam_utils:delete_live_annos(Is6),
+ Is = opt_allocs(Is7),
%% Done.
{function,Name,Arity,CLabel,Is}
@@ -143,10 +144,9 @@ opt_blocks([I|Is]) ->
opt_blocks([]) -> [].
opt_block(Is0) ->
- Is = find_fixpoint(fun(Is) ->
- opt_tuple_element(opt(Is))
- end, Is0),
- opt_alloc(Is).
+ find_fixpoint(fun(Is) ->
+ opt_tuple_element(opt(Is))
+ end, Is0).
find_fixpoint(OptFun, Is0) ->
case OptFun(Is0) of
@@ -411,31 +411,47 @@ eliminate_use_of_from_reg([I]=Is, From, _To, Acc) ->
no
end.
+%% opt_allocs(Instructions) -> Instructions. Optimize allocate
+%% instructions inside blocks. If safe, replace an allocate_zero
+%% instruction with the slightly cheaper allocate instruction.
+
+opt_allocs(Is) ->
+ D = beam_utils:index_labels(Is),
+ opt_allocs_1(Is, D).
+
+opt_allocs_1([{block,Bl0}|Is], D) ->
+ Bl = opt_alloc(Bl0, {D,Is}),
+ [{block,Bl}|opt_allocs_1(Is, D)];
+opt_allocs_1([I|Is], D) ->
+ [I|opt_allocs_1(Is, D)];
+opt_allocs_1([], _) -> [].
+
%% opt_alloc(Instructions) -> Instructions'
%% Optimises all allocate instructions.
opt_alloc([{set,[],[],{alloc,Live0,Info0}},
- {set,[],[],{alloc,Live,Info}}|Is]) ->
+ {set,[],[],{alloc,Live,Info}}|Is], D) ->
Live = Live0, %Assertion.
Alloc = combine_alloc(Info0, Info),
I = {set,[],[],{alloc,Live,Alloc}},
- opt_alloc([I|Is]);
-opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is]) ->
- [{set,[],[],opt_alloc(Is, Ns, Nh, R)}|Is];
-opt_alloc([I|Is]) -> [I|opt_alloc(Is)];
-opt_alloc([]) -> [].
+ opt_alloc([I|Is], D);
+opt_alloc([{set,[],[],{alloc,R,{_,Ns,Nh,[]}}}|Is], D) ->
+ [{set,[],[],opt_alloc(Is, D, Ns, Nh, R)}|Is];
+opt_alloc([I|Is], D) -> [I|opt_alloc(Is, D)];
+opt_alloc([], _) -> [].
combine_alloc({_,Ns,Nh1,Init}, {_,nostack,Nh2,[]}) ->
{zero,Ns,beam_utils:combine_heap_needs(Nh1, Nh2),Init}.
-
+
%% opt_alloc(Instructions, FrameSize, HeapNeed, LivingRegs) -> [Instr]
%% Generates the optimal sequence of instructions for
%% allocating and initalizing the stack frame and needed heap.
-opt_alloc(_Is, nostack, Nh, LivingRegs) ->
+opt_alloc(_Is, _D, nostack, Nh, LivingRegs) ->
{alloc,LivingRegs,{nozero,nostack,Nh,[]}};
-opt_alloc(Is, Ns, Nh, LivingRegs) ->
- InitRegs = init_yreg(Is, 0),
+opt_alloc(Bl, {D,OuterIs}, Ns, Nh, LivingRegs) ->
+ Is = [{block,Bl}|OuterIs],
+ InitRegs = init_yregs(Ns, Is, D),
case count_ones(InitRegs) of
N when N*2 > Ns ->
{alloc,LivingRegs,{nozero,Ns,Nh,gen_init(Ns, InitRegs)}};
@@ -451,19 +467,14 @@ gen_init(Fs, Regs, Y, Acc) when Regs band 1 =:= 0 ->
gen_init(Fs, Regs, Y, Acc) ->
gen_init(Fs, Regs bsr 1, Y+1, Acc).
-%% init_yreg(Instructions, RegSet) -> RegSetInitialized
-%% Calculate the set of initialized y registers.
-
-init_yreg([{set,_,_,{bif,_,_}}|_], Reg) -> Reg;
-init_yreg([{set,_,_,{alloc,_,{gc_bif,_,_}}}|_], Reg) -> Reg;
-init_yreg([{set,_,_,{alloc,_,{put_map,_,_}}}|_], Reg) -> Reg;
-init_yreg([{set,Ds,_,_}|Is], Reg) -> init_yreg(Is, add_yregs(Ds, Reg));
-init_yreg(_Is, Reg) -> Reg.
-
-add_yregs(Ys, Reg) -> foldl(fun(Y, R0) -> add_yreg(Y, R0) end, Reg, Ys).
-
-add_yreg({y,Y}, Reg) -> Reg bor (1 bsl Y);
-add_yreg(_, Reg) -> Reg.
+init_yregs(Y, Is, D) when Y >= 0 ->
+ case beam_utils:is_killed({y,Y}, Is, D) of
+ true ->
+ (1 bsl Y) bor init_yregs(Y-1, Is, D);
+ false ->
+ init_yregs(Y-1, Is, D)
+ end;
+init_yregs(_, _, _) -> 0.
count_ones(Bits) -> count_ones(Bits, 0).
count_ones(0, Acc) -> Acc;
diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl
index 082cd41cfe..14fe22fd27 100644
--- a/lib/compiler/src/beam_type.erl
+++ b/lib/compiler/src/beam_type.erl
@@ -349,27 +349,17 @@ flt_need_heap_2({set,_,_,{put_tuple,_}}, H, Fl) ->
{[],H+1,Fl};
flt_need_heap_2({set,_,_,put}, H, Fl) ->
{[],H+1,Fl};
-%% Then the "neutral" instructions. We just pass them.
-flt_need_heap_2({set,[{fr,_}],_,_}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,[],[],fclearerror}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,[],[],fcheckerror}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,_,_,{bif,_,_}}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,_,_,move}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,_,_,{get_tuple_element,_}}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,_,_,get_list}, H, Fl) ->
- {[],H,Fl};
-flt_need_heap_2({set,_,_,{try_catch,_,_}}, H, Fl) ->
- {[],H,Fl};
-%% All other instructions should cause the insertion of an allocation
+%% The following instructions cause the insertion of an allocation
%% instruction if needed.
+flt_need_heap_2({set,_,_,{alloc,_,_}}, H, Fl) ->
+ {flt_alloc(H, Fl),0,0};
+flt_need_heap_2({set,_,_,{set_tuple_element,_}}, H, Fl) ->
+ {flt_alloc(H, Fl),0,0};
+flt_need_heap_2({'%live',_,_}, H, Fl) ->
+ {flt_alloc(H, Fl),0,0};
+%% All other instructions are "neutral". We just pass them.
flt_need_heap_2(_, H, Fl) ->
- {flt_alloc(H, Fl),0,0}.
+ {[],H,Fl}.
flt_alloc(0, 0) ->
[];
diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl
index 60221578bd..a2795e625f 100644
--- a/lib/compiler/src/beam_utils.erl
+++ b/lib/compiler/src/beam_utils.erl
@@ -118,6 +118,7 @@ is_killed(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{killed,_} -> true;
+ {exit_not_used,_} -> true;
{_,_} -> false
end.
@@ -130,6 +131,7 @@ is_killed_at(R, Lbl, D) when is_integer(Lbl) ->
St0 = #live{lbl=D,res=gb_trees:empty()},
case check_liveness_at(R, Lbl, St0) of
{killed,_} -> true;
+ {exit_not_used,_} -> true;
{_,_} -> false
end.
@@ -146,6 +148,7 @@ is_not_used(R, Is, D) ->
St = #live{lbl=D,res=gb_trees:empty()},
case check_liveness(R, Is, St) of
{used,_} -> false;
+ {exit_not_used,_} -> false;
{_,_} -> true
end.
@@ -309,7 +312,7 @@ delete_live_annos([]) -> [].
combine_heap_needs(H1, H2) when is_integer(H1), is_integer(H2) ->
H1 + H2;
combine_heap_needs(H1, H2) ->
- combine_alloc_lists([H1,H2]).
+ {alloc,combine_alloc_lists([H1,H2])}.
%% anno_defs(Instructions) -> Instructions'
@@ -347,6 +350,9 @@ split_even(Rs) -> split_even(Rs, [], []).
%%
%% killed - Reg is assigned or killed by an allocation instruction.
%% not_used - the value of Reg is not used, but Reg must not be garbage
+%% exit_not_used - the value of Reg is not used, but must not be garbage
+%% because the stack will be scanned because an
+%% exit BIF will raise an exception
%% used - Reg is used
check_liveness(R, [{block,Blk}|Is], St0) ->
@@ -370,6 +376,8 @@ check_liveness(R, [{test,_,{f,Fail},As}|Is], St0) ->
case check_liveness_at(R, Fail, St0) of
{killed,St1} ->
check_liveness(R, Is, St1);
+ {exit_not_used,St1} ->
+ check_liveness(R, Is, St1);
{not_used,St1} ->
not_used(check_liveness(R, Is, St1));
{used,_}=Used ->
@@ -432,7 +440,7 @@ check_liveness(R, [{bs_init,_,_,Live,Ss,Dst}|Is], St) ->
false ->
if
R =:= Dst -> {killed,St};
- true -> check_liveness(R, Is, St)
+ true -> not_used(check_liveness(R, Is, St))
end
end
end;
@@ -466,7 +474,7 @@ check_liveness(R, [{call_ext,Live,_}=I|Is], St) ->
%% We must make sure we don't check beyond this
%% instruction or we will fall through into random
%% unrelated code and get stuck in a loop.
- {killed,St}
+ {exit_not_used,St}
end
end;
check_liveness(R, [{call_fun,Live}|Is], St) ->
@@ -631,6 +639,7 @@ check_liveness_at(R, Lbl, #live{lbl=Ll,res=ResMemorized}=St0) ->
{Res,St#live{res=gb_trees:insert(Lbl, Res, St#live.res)}}
end.
+not_used({exit_not_used,St}) -> {not_used,St};
not_used({killed,St}) -> {not_used,St};
not_used({_,_}=Res) -> Res.
@@ -659,13 +668,30 @@ check_liveness_block({x,X}=R, [{set,Ds,Ss,{alloc,Live,Op}}|Is], St0) ->
{killed,St0};
true ->
case check_liveness_block_1(R, Ss, Ds, Op, Is, St0) of
- {killed,St} -> {not_used,St};
{transparent,St} -> {alloc_used,St};
- {_,_}=Res -> Res
+ {_,_}=Res -> not_used(Res)
end
end;
-check_liveness_block({y,_}=R, [{set,Ds,Ss,{alloc,_Live,Op}}|Is], St) ->
- check_liveness_block_1(R, Ss, Ds, Op, Is, St);
+check_liveness_block({y,_}=R, [{set,Ds,Ss,{alloc,_Live,Op}}|Is], St0) ->
+ case check_liveness_block_1(R, Ss, Ds, Op, Is, St0) of
+ {transparent,St} -> {alloc_used,St};
+ {_,_}=Res -> not_used(Res)
+ end;
+check_liveness_block({y,_}=R, [{set,Ds,Ss,{try_catch,_,Op}}|Is], St0) ->
+ case Ds of
+ [R] ->
+ {killed,St0};
+ _ ->
+ case check_liveness_block_1(R, Ss, Ds, Op, Is, St0) of
+ {exit_not_used,St} ->
+ {used,St};
+ {transparent,St} ->
+ %% Conservatively assumed that it is used.
+ {used,St};
+ {_,_}=Res ->
+ Res
+ end
+ end;
check_liveness_block(R, [{set,Ds,Ss,Op}|Is], St) ->
check_liveness_block_1(R, Ss, Ds, Op, Is, St);
check_liveness_block(_, [], St) -> {transparent,St}.
@@ -681,6 +707,11 @@ check_liveness_block_1(R, Ss, Ds, Op, Is, St0) ->
true -> {killed,St};
false -> check_liveness_block(R, Is, St)
end;
+ {exit_not_used,St} ->
+ case member(R, Ds) of
+ true -> {exit_not_used,St};
+ false -> check_liveness_block(R, Is, St)
+ end;
{not_used,St} ->
not_used(case member(R, Ds) of
true -> {killed,St};
diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl
index 2ad9747940..4feb26c513 100644
--- a/lib/compiler/src/beam_validator.erl
+++ b/lib/compiler/src/beam_validator.erl
@@ -530,9 +530,10 @@ valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) ->
Type = bif_type(Op, Src, Vst),
set_type_reg(Type, Dst, Vst);
valfun_4({gc_bif,Op,{f,Fail},Live,Src,Dst}, #vst{current=St0}=Vst0) ->
+ verify_live(Live, Vst0),
+ verify_y_init(Vst0),
St = kill_heap_allocation(St0),
Vst1 = Vst0#vst{current=St},
- verify_live(Live, Vst1),
Vst2 = branch_state(Fail, Vst1),
Vst = prune_x_regs(Live, Vst2),
validate_src(Src, Vst),
@@ -686,6 +687,7 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) ->
set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst));
valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -698,6 +700,7 @@ valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
if
is_integer(Sz) ->
ok;
@@ -710,6 +713,7 @@ valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) ->
set_type_reg(binary, Dst, Vst);
valfun_4({bs_append,{f,Fail},Bits,Heap,Live,_Unit,Bin,_Flags,Dst}, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
assert_term(Bits, Vst0),
assert_term(Bin, Vst0),
Vst1 = heap_alloc(Heap, Vst0),
@@ -945,6 +949,7 @@ deallocate(#vst{current=St}=Vst) ->
test_heap(Heap, Live, Vst0) ->
verify_live(Live, Vst0),
+ verify_y_init(Vst0),
Vst = prune_x_regs(Live, Vst0),
heap_alloc(Heap, Vst).
diff --git a/lib/dialyzer/test/small_SUITE_data/src/abs.erl b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
index 251e24cdfc..0e38c3dbb7 100644
--- a/lib/dialyzer/test/small_SUITE_data/src/abs.erl
+++ b/lib/dialyzer/test/small_SUITE_data/src/abs.erl
@@ -5,7 +5,7 @@
-export([t/0]).
t() ->
- Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0],
+ Fs = [fun i1/0, fun i2/0, fun i3/0, fun i4/0, fun f1/0, fun erl_551/0],
_ = [catch F() || F <- Fs],
ok.
@@ -60,6 +60,13 @@ f1() ->
f1(A) ->
abs(A).
+erl_551() ->
+ accept(9),
+ accept(-3).
+
+accept(Number) when abs(Number) >= 8 -> first;
+accept(_Number) -> second.
+
-spec int() -> integer().
int() ->
diff --git a/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
new file mode 100644
index 0000000000..2dc00d272a
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/erl_tar_table.erl
@@ -0,0 +1,14 @@
+-module(erl_tar_table).
+
+%% OTP-14860, PR 1670.
+
+-export([t/0, v/0, x/0]).
+
+t() ->
+ {ok, ["file"]} = erl_tar:table("table.tar").
+
+v() ->
+ {ok, [{_,_,_,_,_,_,_}]} = erl_tar:table("table.tar", [verbose]).
+
+x() ->
+ {ok, ["file"]} = erl_tar:table("table.tar", []).
diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl
index 462a1f9dcd..fc6a844e22 100644
--- a/lib/hipe/cerl/erl_bif_types.erl
+++ b/lib/hipe/cerl/erl_bif_types.erl
@@ -1974,9 +1974,11 @@ arith_abs(X1, Opaques) ->
case infinity_geq(Min1, 0) of
true -> {Min1, Max1};
false ->
+ NegMin1 = infinity_inv(Min1),
+ NegMax1 = infinity_inv(Max1),
case infinity_geq(Max1, 0) of
- true -> {0, infinity_inv(Min1)};
- false -> {infinity_inv(Max1), infinity_inv(Min1)}
+ true -> {0, max(NegMin1, Max1)};
+ false -> {NegMax1, NegMin1}
end
end,
t_from_range(NewMin, NewMax)
diff --git a/lib/observer/include/etop.hrl b/lib/observer/include/etop.hrl
index 002937e522..f8d370450b 100644
--- a/lib/observer/include/etop.hrl
+++ b/lib/observer/include/etop.hrl
@@ -18,4 +18,4 @@
%% %CopyrightEnd%
%%
--include("../../runtime_tools/include/observer_backend.hrl").
+-include_lib("runtime_tools/include/observer_backend.hrl").
diff --git a/lib/observer/src/crashdump_viewer.erl b/lib/observer/src/crashdump_viewer.erl
index f197e72b90..d578639f1a 100644
--- a/lib/observer/src/crashdump_viewer.erl
+++ b/lib/observer/src/crashdump_viewer.erl
@@ -2029,12 +2029,16 @@ all_modinfo(Fd,LM,LineHead,DecodeOpts) ->
end.
get_attribute(Fd, DecodeOpts) ->
+ Term = do_get_attribute(Fd, DecodeOpts),
+ io_lib:format("~tp~n",[Term]).
+
+do_get_attribute(Fd, DecodeOpts) ->
Bytes = bytes(Fd, ""),
try get_binary(Bytes, DecodeOpts) of
{Bin,_} ->
try binary_to_term(Bin) of
Term ->
- io_lib:format("~tp~n",[Term])
+ Term
catch
_:_ ->
{"WARNING: The term is probably truncated!",
diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl
index 2e5fe0bc1a..d612e0a1c5 100644
--- a/lib/observer/src/observer_pro_wx.erl
+++ b/lib/observer/src/observer_pro_wx.erl
@@ -27,7 +27,7 @@
handle_event/2, handle_cast/2]).
-include_lib("wx/include/wx.hrl").
--include("../include/etop.hrl").
+-include("etop.hrl").
-include("observer_defs.hrl").
-include("etop_defs.hrl").
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 9fbd1a62a4..32773a779e 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -459,6 +459,27 @@ special(File,Procs) ->
old_attrib=undefined,
old_comp_info=undefined}=Mod2,
ok;
+ ".trunc_mod" ->
+ ModName = atom_to_list(?helper_mod),
+ {ok,Mod=#loaded_mod{},[TW]} =
+ crashdump_viewer:loaded_mod_details(ModName),
+ "WARNING: The crash dump is truncated here."++_ = TW,
+ #loaded_mod{current_attrib=CA,current_comp_info=CCI,
+ old_attrib=OA,old_comp_info=OCI} = Mod,
+ case lists:all(fun(undefined) ->
+ true;
+ (S) when is_list(S) ->
+ io_lib:printable_unicode_list(lists:flatten(S));
+ (_) -> false
+ end,
+ [CA,CCI,OA,OCI]) of
+ true ->
+ ok;
+ false ->
+ ct:fail({should_be_printable_strings_or_undefined,
+ {CA,CCI,OA,OCI}})
+ end,
+ ok;
".trunc_bin1" ->
%% This is 'full_dist' truncated after the first
%% "=binary:"
@@ -658,13 +679,32 @@ do_create_dumps(DataDir,Rel) ->
CD5 = dump_with_size_limit_reached(DataDir,Rel,"trunc_bytes"),
CD6 = dump_with_unicode_atoms(DataDir,Rel,"unicode"),
CD7 = dump_with_maps(DataDir,Rel,"maps"),
- TruncatedDumps = truncate_dump(CD1),
- {[CD1,CD2,CD3,CD4,CD5,CD6,CD7|TruncatedDumps], DosDump};
+ TruncDumpMod = truncate_dump_mod(CD1),
+ TruncatedDumpsBinary = truncate_dump_binary(CD1),
+ {[CD1,CD2,CD3,CD4,CD5,CD6,CD7,TruncDumpMod|TruncatedDumpsBinary],
+ DosDump};
_ ->
{[CD1,CD2], DosDump}
end.
-truncate_dump(File) ->
+truncate_dump_mod(File) ->
+ {ok,Bin} = file:read_file(File),
+ ModNameBin = atom_to_binary(?helper_mod,latin1),
+ NewLine = case os:type() of
+ {win32,_} -> <<"\r\n">>;
+ _ -> <<"\n">>
+ end,
+ RE = <<NewLine/binary,"=mod:",ModNameBin/binary,
+ NewLine/binary,"Current size: [0-9]*",
+ NewLine/binary,"Current attributes: ...">>,
+ {match,[{Pos,Len}]} = re:run(Bin,RE),
+ Size = Pos + Len,
+ <<Truncated:Size/binary,_/binary>> = Bin,
+ DumpName = filename:rootname(File) ++ ".trunc_mod",
+ file:write_file(DumpName,Truncated),
+ DumpName.
+
+truncate_dump_binary(File) ->
{ok,Bin} = file:read_file(File),
BinTag = <<"\n=binary:">>,
Colon = <<":">>,
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 154894cda8..ad9efc4755 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -54,7 +54,7 @@
sha/1, sign/3, verify/5]).
%%% For test suites
--export([pack/3]).
+-export([pack/3, adjust_algs_for_peer_version/2]).
-export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove
-define(Estring(X), ?STRING((if is_binary(X) -> X;
diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile
index a18383d148..21359a0386 100644
--- a/lib/ssh/test/Makefile
+++ b/lib/ssh/test/Makefile
@@ -37,6 +37,7 @@ MODULES= \
ssh_renegotiate_SUITE \
ssh_basic_SUITE \
ssh_bench_SUITE \
+ ssh_compat_SUITE \
ssh_connection_SUITE \
ssh_engine_SUITE \
ssh_protocol_SUITE \
diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl
new file mode 100644
index 0000000000..74ab5aca3a
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE.erl
@@ -0,0 +1,814 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssh_compat_SUITE).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("ssh/src/ssh_transport.hrl"). % #ssh_msg_kexinit{}
+-include_lib("kernel/include/inet.hrl"). % #hostent{}
+-include_lib("kernel/include/file.hrl"). % #file_info{}
+-include("ssh_test_lib.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-define(USER,"sshtester").
+-define(PWD, "foobar").
+-define(DOCKER_PFX, "ssh_compat_suite-ssh").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() ->
+ [%%{ct_hooks,[ts_install_cth]},
+ {timetrap,{seconds,40}}].
+
+all() ->
+ [{group,G} || G <- vers()].
+
+groups() ->
+ [{G, [], tests()} || G <- vers()].
+
+tests() ->
+ [login_with_password_otp_is_client,
+ login_with_password_otp_is_server,
+ login_with_keyboard_interactive_otp_is_client,
+ login_with_keyboard_interactive_otp_is_server,
+ login_with_all_public_keys_otp_is_client,
+ login_with_all_public_keys_otp_is_server,
+ all_algorithms_otp_is_client,
+ all_algorithms_otp_is_server
+ ].
+
+
+
+vers() ->
+ try
+ %% Find all useful containers in such a way that undefined command, too low
+ %% priviliges, no containers and containers found give meaningful result:
+ L0 = ["REPOSITORY"++_|_] = string:tokens(os:cmd("docker images"), "\r\n"),
+ [["REPOSITORY","TAG"|_]|L1] = [string:tokens(E, " ") || E<-L0],
+ [list_to_atom(V) || [?DOCKER_PFX,V|_] <- L1]
+ of
+ Vs ->
+ lists:sort(Vs)
+ catch
+ error:{badmatch,_} ->
+ []
+ end.
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ ?CHECK_CRYPTO(
+ case os:find_executable("docker") of
+ false ->
+ {skip, "No docker"};
+ _ ->
+ ssh:start(),
+ ct:log("Crypto info: ~p",[crypto:info_lib()]),
+ Config
+ end).
+
+end_per_suite(Config) ->
+ %% Remove all containers that are not running:
+%%% os:cmd("docker rm $(docker ps -aq -f status=exited)"),
+ %% Remove dangling images:
+%%% os:cmd("docker rmi $(docker images -f dangling=true -q)"),
+ Config.
+
+
+
+init_per_group(G, Config) ->
+ case lists:member(G, vers()) of
+ true ->
+ try start_docker(G) of
+ {ok,ID} ->
+ ct:log("==> ~p",[G]),
+ [Vssh|VsslRest] = string:tokens(atom_to_list(G), "-"),
+ Vssl = lists:flatten(lists:join($-,VsslRest)),
+ ct:comment("+++ ~s + ~s +++",[Vssh,Vssl]),
+ %% Find the algorithms that both client and server supports:
+ {IP,Port} = ip_port([{id,ID}]),
+ try common_algs([{id,ID}|Config], IP, Port) of
+ {ok, RemoteServerCommon, RemoteClientCommon} ->
+ [{ssh_version,Vssh},{ssl_version,Vssl},
+ {id,ID},
+ {common_server_algs,RemoteServerCommon},
+ {common_client_algs,RemoteClientCommon}
+ |Config];
+ Other ->
+ ct:log("Error in init_per_group: ~p",[Other]),
+ stop_docker(ID),
+ {fail, "Can't contact docker sshd"}
+ catch
+ Class:Exc ->
+ ST = erlang:get_stacktrace(),
+ ct:log("common_algs: ~p:~p~n~p",[Class,Exc,ST]),
+ stop_docker(ID),
+ {fail, "Failed during setup"}
+ end
+ catch
+ cant_start_docker ->
+ {skip, "Can't start docker"};
+
+ C:E ->
+ ST = erlang:get_stacktrace(),
+ ct:log("No ~p~n~p:~p~n~p",[G,C,E,ST]),
+ {skip, "Can't start docker"}
+ end;
+
+ false ->
+ Config
+ end.
+
+end_per_group(_, Config) ->
+ catch stop_docker(proplists:get_value(id,Config)),
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+login_with_password_otp_is_client(Config) ->
+ {IP,Port} = ip_port(Config),
+ {ok,C} = ssh:connect(IP, Port, [{auth_methods,"password"},
+ {user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ ssh:close(C).
+
+%%--------------------------------------------------------------------
+login_with_password_otp_is_server(Config) ->
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods,"password"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, new_dir(Config)},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+2]).\r\n'",
+ [<<"Answer=3">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+login_with_keyboard_interactive_otp_is_client(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{auth_methods,"keyboard-interactive"},
+ {user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ ssh:close(C).
+
+%%--------------------------------------------------------------------
+login_with_keyboard_interactive_otp_is_server(Config) ->
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods,"keyboard-interactive"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, new_dir(Config)},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+3]).\r\n'",
+ [<<"Answer=4">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+login_with_all_public_keys_otp_is_client(Config) ->
+ CommonAlgs = [{public_key_from_host,A}
+ || {public_key,A} <- proplists:get_value(common_server_algs, Config)],
+ {DockerIP,DockerPort} = ip_port(Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(_Tag,Alg) ->
+ ssh:connect(DockerIP, DockerPort,
+ [{auth_methods, "publickey"},
+ {user, ?USER},
+ {user_dir, setup_remote_auth_keys_and_local_priv(Alg, Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+login_with_all_public_keys_otp_is_server(Config) ->
+ CommonAlgs = [{public_key_to_host,A}
+ || {public_key,A} <- proplists:get_value(common_client_algs, Config)],
+ UserDir = new_dir(Config),
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{auth_methods, "publickey"},
+ {system_dir, setup_local_hostdir('ssh-rsa',Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+
+ R = chk_all_algos(CommonAlgs, Config,
+ fun(_Tag,Alg) ->
+ setup_remote_priv_and_local_auth_keys(Alg, clear_dir(UserDir), Config),
+ exec_from_docker(Config, Host, HostPort,
+ "'lists:concat([\"Answer=\",1+4]).\r\n'",
+ [<<"Answer=5">>],
+ "")
+ end),
+ ssh:stop_daemon(Server),
+ R.
+
+%%--------------------------------------------------------------------
+all_algorithms_otp_is_client(Config) ->
+ CommonAlgs = proplists:get_value(common_server_algs, Config),
+ {IP,Port} = ip_port(Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(Tag, Alg) ->
+ ssh:connect(IP, Port, [{user,?USER},
+ {password,?PWD},
+ {auth_methods, "password"},
+ {user_dir, new_dir(Config)},
+ {preferred_algorithms, [{Tag,[Alg]}]},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ])
+ end).
+
+%%--------------------------------------------------------------------
+all_algorithms_otp_is_server(Config) ->
+ CommonAlgs = proplists:get_value(common_client_algs, Config),
+ UserDir = setup_remote_priv_and_local_auth_keys('ssh-rsa', Config),
+ chk_all_algos(CommonAlgs, Config,
+ fun(Tag,Alg) ->
+ HostKeyAlg = case Tag of
+ public_key -> Alg;
+ _ -> 'ssh-rsa'
+ end,
+ {Server, Host, HostPort} =
+ ssh_test_lib:daemon(0,
+ [{preferred_algorithms, [{Tag,[Alg]}]},
+ {system_dir, setup_local_hostdir(HostKeyAlg, Config)},
+ {user_dir, UserDir},
+ {user_passwords, [{?USER,?PWD}]},
+ {failfun, fun ssh_test_lib:failfun/2}
+ ]),
+ R = exec_from_docker(Config, Host, HostPort,
+ "hi_there.\r\n",
+ [<<"hi_there">>],
+ ""),
+ ssh:stop_daemon(Server),
+ R
+ end).
+
+%%--------------------------------------------------------------------
+%% Utilities ---------------------------------------------------------
+%%--------------------------------------------------------------------
+exec_from_docker(WhatEver, {0,0,0,0}, HostPort, Command, Expects, ExtraSshArg) ->
+ exec_from_docker(WhatEver, host_ip(), HostPort, Command, Expects, ExtraSshArg);
+
+exec_from_docker(Config, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)),
+ is_list(Config) ->
+ {DockerIP,DockerPort} = ip_port(Config),
+ {ok,C} = ssh:connect(DockerIP, DockerPort,
+ [{user,?USER},
+ {password,?PWD},
+ {user_dir, new_dir(Config)},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ R = exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg),
+ ssh:close(C),
+ R;
+
+exec_from_docker(C, HostIP, HostPort, Command, Expects, ExtraSshArg) when is_binary(hd(Expects)) ->
+ SSH_from_docker =
+ lists:concat(["sshpass -p ",?PWD," ",
+ "/buildroot/ssh/bin/ssh -p ",HostPort," -o 'CheckHostIP=no' -o 'StrictHostKeyChecking=no' ",
+ ExtraSshArg," ",
+ inet_parse:ntoa(HostIP)," "
+ ]),
+ ExecCommand = SSH_from_docker ++ Command,
+ R = exec(C, ExecCommand),
+ case R of
+ {ok,{ExitStatus,Result}} when ExitStatus == 0 ->
+ case binary:match(Result, Expects) of
+ nomatch ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Bad answer"};
+ _ ->
+ ok
+ end;
+ {ok,_} ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Exit status =/= 0"};
+ _ ->
+ ct:log("Result of~n ~s~nis~n ~p",[ExecCommand,R]),
+ {fail, "Couldn't login to host"}
+ end.
+
+
+
+
+exec(C, Cmd) ->
+ ct:log("~s",[Cmd]),
+ {ok,Ch} = ssh_connection:session_channel(C, 10000),
+ success = ssh_connection:exec(C, Ch, Cmd, 10000),
+ exec_result(C, Ch).
+
+
+exec_result(C, Ch) ->
+ exec_result(C, Ch, undefined, <<>>).
+
+exec_result(C, Ch, ExitStatus, Acc) ->
+ receive
+ {ssh_cm,C,{closed,Ch}} ->
+ %%ct:log("CHAN ~p got *closed*",[Ch]),
+ {ok, {ExitStatus, Acc}};
+
+ {ssh_cm,C,{exit_status,Ch,ExStat}} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got *exit status ~p*",[Ch,ExStat]),
+ exec_result(C, Ch, ExStat, Acc);
+
+ {ssh_cm,C,{data,Ch,_,Data}=_X} when ExitStatus == undefined ->
+ %%ct:log("CHAN ~p got ~p",[Ch,_X]),
+ exec_result(C, Ch, ExitStatus, <<Acc/binary, Data/binary>>);
+
+ _Other ->
+ %%ct:log("OTHER: ~p",[_Other]),
+ exec_result(C, Ch, ExitStatus, Acc)
+
+ after 5000 ->
+ %%ct:log("NO MORE, received so far:~n~s",[Acc]),
+ {error, timeout}
+ end.
+
+
+chk_all_algos(CommonAlgs, Config, DoTestFun) when is_function(DoTestFun,2) ->
+ ct:comment("~p algorithms",[length(CommonAlgs)]),
+ %% Check each algorithm
+ Failed =
+ lists:foldl(
+ fun({Tag,Alg}, FailedAlgos) ->
+ ct:log("Try ~p",[Alg]),
+ case DoTestFun(Tag,Alg) of
+ {ok,C} ->
+ ssh:close(C),
+ FailedAlgos;
+ ok ->
+ FailedAlgos;
+ Other ->
+ ct:log("FAILED! ~p ~p: ~p",[Tag,Alg,Other]),
+ [Alg|FailedAlgos]
+ end
+ end, [], CommonAlgs),
+ ct:pal("~s", [format_result_table_use_all_algos(Config, CommonAlgs, Failed)]),
+ case Failed of
+ [] ->
+ ok;
+ _ ->
+ {fail, Failed}
+ end.
+
+setup_local_hostdir(KeyAlg, Config) ->
+ setup_local_hostdir(KeyAlg, new_dir(Config), Config).
+setup_local_hostdir(KeyAlg, HostDir, Config) ->
+ {ok, {Priv,Publ}} = host_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public key
+ DstFile = filename:join(HostDir, dst_filename(host,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ HostDir.
+
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, Config) ->
+ setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_auth_keys_and_local_priv(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local private and public keys
+ DstFile = filename:join(UserDir, dst_filename(user,KeyAlg)),
+ ok = file:write_file(DstFile, Priv),
+ ok = file:write_file(DstFile++".pub", Publ),
+ %% Remote auth_methods with public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ ok = ssh_sftp:write_file(Ch, ".ssh/authorized_keys", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh/authorized_keys", #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, UserDir, Config) ->
+ {IP,Port} = ip_port(Config),
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, Config) ->
+ setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, new_dir(Config), Config).
+
+setup_remote_priv_and_local_auth_keys(KeyAlg, IP, Port, UserDir, Config) ->
+ {ok, {Priv,Publ}} = user_priv_pub_keys(Config, KeyAlg),
+ %% Local auth_methods with public key
+ AuthKeyFile = filename:join(UserDir, "authorized_keys"),
+ ok = file:write_file(AuthKeyFile, Publ),
+ %% Remote private and public key
+ {ok,Ch,Cc} = ssh_sftp:start_channel(IP, Port, [{user, ?USER },
+ {password, ?PWD },
+ {auth_methods, "password"},
+ {silently_accept_hosts,true},
+ {user_interaction,false}
+ ]),
+ _ = ssh_sftp:make_dir(Ch, ".ssh"),
+ DstFile = filename:join(".ssh", dst_filename(user,KeyAlg)),
+ ok = ssh_sftp:write_file(Ch, DstFile, Priv),
+ ok = ssh_sftp:write_file_info(Ch, DstFile, #file_info{mode=8#700}),
+ ok = ssh_sftp:write_file(Ch, DstFile++".pub", Publ),
+ ok = ssh_sftp:write_file_info(Ch, ".ssh", #file_info{mode=8#700}),
+ ok = ssh_sftp:stop_channel(Ch),
+ ok = ssh:close(Cc),
+ UserDir.
+
+user_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("users_keys", user, Config, KeyAlg).
+host_priv_pub_keys(Config, KeyAlg) -> priv_pub_keys("host_keys", host, Config, KeyAlg).
+
+priv_pub_keys(KeySubDir, Type, Config, KeyAlg) ->
+ KeyDir = filename:join(proplists:get_value(data_dir,Config), KeySubDir),
+ {ok,Priv} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg))),
+ {ok,Publ} = file:read_file(filename:join(KeyDir,src_filename(Type,KeyAlg)++".pub")),
+ {ok, {Priv,Publ}}.
+
+
+src_filename(user, 'ssh-rsa' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+src_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+src_filename(user, 'ssh-dss' ) -> "id_dsa";
+src_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa256";
+src_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa384";
+src_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa521";
+src_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+src_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+src_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+src_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key256";
+src_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key384";
+src_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key521".
+
+dst_filename(user, 'ssh-rsa' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-256' ) -> "id_rsa";
+dst_filename(user, 'rsa-sha2-512' ) -> "id_rsa";
+dst_filename(user, 'ssh-dss' ) -> "id_dsa";
+dst_filename(user, 'ecdsa-sha2-nistp256') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp384') -> "id_ecdsa";
+dst_filename(user, 'ecdsa-sha2-nistp521') -> "id_ecdsa";
+dst_filename(host, 'ssh-rsa' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-256' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'rsa-sha2-512' ) -> "ssh_host_rsa_key";
+dst_filename(host, 'ssh-dss' ) -> "ssh_host_dsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+dst_filename(host, 'ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key".
+
+
+format_result_table_use_all_algos(Config, CommonAlgs, Failed) ->
+ %% Write a nice table with the result
+ AlgHead = 'Algorithm',
+ AlgWidth = lists:max([length(atom_to_list(A)) || {_,A} <- CommonAlgs]),
+ {ResultTable,_} =
+ lists:mapfoldl(
+ fun({T,A}, Tprev) ->
+ Tag = case T of
+ Tprev -> "";
+ _ -> io_lib:format('~s~n',[T])
+ end,
+ {io_lib:format('~s ~*s ~s~n',
+ [Tag, -AlgWidth, A,
+ case lists:member(A,Failed) of
+ true -> "<<<< FAIL <<<<";
+ false-> "(ok)"
+ end]),
+ T}
+ end, undefined, CommonAlgs),
+
+ Vssh = proplists:get_value(ssh_version,Config,""),
+ Vssl = proplists:get_value(ssl_version,Config,""),
+ io_lib:format("~nResults, Peer versions: ~s and ~s~n"
+ "Tag ~*s Result~n"
+ "=====~*..=s=======~n~s"
+ ,[Vssh,Vssl,
+ -AlgWidth,AlgHead,
+ AlgWidth, "", ResultTable]).
+
+
+start_docker(Ver) ->
+ Cmnd = lists:concat(["docker run -itd --rm -p 1234 ",?DOCKER_PFX,":",Ver]),
+ Id0 = os:cmd(Cmnd),
+ ct:log("Ver = ~p, Cmnd ~p~n-> ~p",[Ver,Cmnd,Id0]),
+ case is_docker_sha(Id0) of
+ true ->
+ Id = hd(string:tokens(Id0, "\n")),
+ IP = ip(Id),
+ Port = 1234,
+ {ok, {Ver,{IP,Port},Id}};
+ false ->
+ throw(cant_start_docker)
+ end.
+
+
+stop_docker({_Ver,_,Id}) ->
+ Cmnd = lists:concat(["docker kill ",Id]),
+ os:cmd(Cmnd).
+
+is_docker_sha(L) ->
+ lists:all(fun(C) when $a =< C,C =< $z -> true;
+ (C) when $0 =< C,C =< $9 -> true;
+ ($\n) -> true;
+ (_) -> false
+ end, L).
+
+ip_port(Config) ->
+ {_Ver,{IP,Port},_} = proplists:get_value(id,Config),
+ {IP,Port}.
+
+port_mapped_to(Id) ->
+ Cmnd = lists:concat(["docker ps --format \"{{.Ports}}\" --filter id=",Id]),
+ [_, PortStr | _] = string:tokens(os:cmd(Cmnd), ":->/"),
+ list_to_integer(PortStr).
+
+ip(Id) ->
+ Cmnd = lists:concat(["docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ",
+ Id]),
+ IPstr0 = os:cmd(Cmnd),
+ ct:log("Cmnd ~p~n-> ~p",[Cmnd,IPstr0]),
+ IPstr = hd(string:tokens(IPstr0, "\n")),
+ {ok,IP} = inet:parse_address(IPstr),
+ IP.
+
+new_dir(Config) ->
+ PrivDir = proplists:get_value(priv_dir, Config),
+ SubDirName = integer_to_list(erlang:system_time()),
+ Dir = filename:join(PrivDir, SubDirName),
+ case file:read_file_info(Dir) of
+ {error,enoent} ->
+ ok = file:make_dir(Dir),
+ Dir;
+ _ ->
+ timer:sleep(25),
+ new_dir(Config)
+ end.
+
+clear_dir(Dir) ->
+ delete_all_contents(Dir),
+ {ok,[]} = file:list_dir(Dir),
+ Dir.
+
+delete_all_contents(Dir) ->
+ {ok,Fs} = file:list_dir(Dir),
+ lists:map(fun(F0) ->
+ F = filename:join(Dir, F0),
+ case filelib:is_file(F) of
+ true ->
+ file:delete(F);
+ false ->
+ case filelib:is_dir(F) of
+ true ->
+ delete_all_contents(F),
+ file:del_dir(F);
+ false ->
+ ct:log("Neither file nor dir: ~p",[F])
+ end
+ end
+ end, Fs).
+
+common_algs(Config, IP, Port) ->
+ case remote_server_algs(IP, Port) of
+ {ok, {RemoteHelloBin, RemoteServerKexInit}} ->
+ case remote_client_algs(Config) of
+ {ok,{_Hello,RemoteClientKexInit}} ->
+ RemoteServerAlgs = kexint_msg2default_algorithms(RemoteServerKexInit),
+ Server = find_common_algs(RemoteServerAlgs,
+ use_algorithms(RemoteHelloBin)),
+ RemoteClientAlgs = kexint_msg2default_algorithms(RemoteClientKexInit),
+ Client = find_common_algs(RemoteClientAlgs,
+ use_algorithms(RemoteHelloBin)),
+ ct:log("Docker server algorithms:~n ~p~n~nDocker client algorithms:~n ~p",
+ [RemoteServerAlgs,RemoteClientAlgs]),
+ {ok, Server, Client};
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end.
+
+
+find_common_algs(Remote, Local) ->
+ [{T,V} || {T,Vs} <- ssh_test_lib:extract_algos(
+ ssh_test_lib:intersection(Remote,
+ Local)),
+ V <- Vs].
+
+
+use_algorithms(RemoteHelloBin) ->
+ MyAlgos = ssh:chk_algos_opts(
+ [{modify_algorithms,
+ [{append,
+ [{kex,['diffie-hellman-group1-sha1']}
+ ]}
+ ]}
+ ]),
+ ssh_transport:adjust_algs_for_peer_version(binary_to_list(RemoteHelloBin)++"\r\n",
+ MyAlgos).
+
+kexint_msg2default_algorithms(#ssh_msg_kexinit{kex_algorithms = Kex,
+ server_host_key_algorithms = PubKey,
+ encryption_algorithms_client_to_server = CipherC2S,
+ encryption_algorithms_server_to_client = CipherS2C,
+ mac_algorithms_client_to_server = MacC2S,
+ mac_algorithms_server_to_client = MacS2C,
+ compression_algorithms_client_to_server = CompC2S,
+ compression_algorithms_server_to_client = CompS2C
+ }) ->
+ [{kex, ssh_test_lib:to_atoms(Kex)},
+ {public_key, ssh_test_lib:to_atoms(PubKey)},
+ {cipher, [{client2server,ssh_test_lib:to_atoms(CipherC2S)},
+ {server2client,ssh_test_lib:to_atoms(CipherS2C)}]},
+ {mac, [{client2server,ssh_test_lib:to_atoms(MacC2S)},
+ {server2client,ssh_test_lib:to_atoms(MacS2C)}]},
+ {compression, [{client2server,ssh_test_lib:to_atoms(CompC2S)},
+ {server2client,ssh_test_lib:to_atoms(CompS2C)}]}].
+
+
+
+remote_server_algs(IP, Port) ->
+ case try_gen_tcp_connect(IP, Port, 5) of
+ {ok,S} ->
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ receive_hello(S, <<>>);
+ {error,Error} ->
+ {error,Error}
+ end.
+
+try_gen_tcp_connect(IP, Port, N) when N>0 ->
+ case gen_tcp:connect(IP, Port, [binary]) of
+ {ok,S} ->
+ {ok,S};
+ {error,_Error} when N>1 ->
+ receive after 1000 -> ok end,
+ try_gen_tcp_connect(IP, Port, N-1);
+ {error,Error} ->
+ {error,Error}
+ end;
+try_gen_tcp_connect(_, _, _) ->
+ {error, "No contact"}.
+
+
+remote_client_algs(Config) ->
+ Parent = self(),
+ Ref = make_ref(),
+ spawn(
+ fun() ->
+ {ok,Sl} = gen_tcp:listen(0, [binary]),
+ {ok,{IP,Port}} = inet:sockname(Sl),
+ Parent ! {addr,Ref,IP,Port},
+ {ok,S} = gen_tcp:accept(Sl),
+ ok = gen_tcp:send(S, "SSH-2.0-CheckAlgs\r\n"),
+ Parent ! {Ref,receive_hello(S, <<>>)}
+ end),
+ receive
+ {addr,Ref,IP,Port} ->
+ spawn(fun() ->
+ exec_from_docker(Config, IP, Port,
+ "howdy.\r\n",
+ [<<"howdy">>],
+ "")
+ end),
+ receive
+ {Ref, Result} ->
+ Result
+ after 15000 ->
+ {error, timeout2}
+ end
+ after 15000 ->
+ {error, timeout1}
+ end.
+
+
+
+receive_hello(S, Ack) ->
+ %% The Ack is to collect bytes until the full message is received
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ case binary:split(<<Ack/binary, Bin0/binary>>, [<<"\r\n">>,<<"\r">>,<<"\n">>]) of
+ [Hello = <<"SSH-2.0-",_/binary>>, NextPacket] ->
+ ct:log("Got 2.0 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Hello = <<"SSH-1.99-",_/binary>>, NextPacket] ->
+ ct:comment("Old SSH ~s",["1.99"]),
+ ct:log("Got 1.99 hello (~p), ~p bytes to next msg",[Hello,size(NextPacket)]),
+ {ok, {Hello, receive_kexinit(S, NextPacket)}};
+
+ [Bin] when size(Bin) < 256 ->
+ ct:log("Got part of hello (~p chars):~n~s~n~s",[size(Bin),Bin,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ receive_hello(S, Bin0);
+
+ _ ->
+ ct:log("Bad hello string (line ~p, ~p chars):~n~s~n~s",[?LINE,size(Bin0),Bin0,
+ [io_lib:format('~2.16.0b ',[C])
+ || C <- binary_to_list(Bin0)
+ ]
+ ]),
+ ct:fail("Bad hello string received")
+ end;
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for hello!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+receive_kexinit(_S, <<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>)
+ when PacketLen < 5000, % heuristic max len to stop huge attempts if packet decodeing get out of sync
+ size(PayloadAndPadding) >= (PacketLen-1) % Need more bytes?
+ ->
+ ct:log("Has all ~p packet bytes",[PacketLen]),
+ PayloadLen = PacketLen - PaddingLen - 1,
+ <<Payload:PayloadLen/binary, _Padding:PaddingLen/binary>> = PayloadAndPadding,
+ ssh_message:decode(Payload);
+
+receive_kexinit(S, Ack) ->
+ ct:log("Has ~p bytes, need more",[size(Ack)]),
+ receive
+ {tcp, S, Bin0} when is_binary(Bin0) ->
+ receive_kexinit(S, <<Ack/binary, Bin0/binary>>);
+ Other ->
+ ct:log("Bad hello string (line ~p):~n~p",[?LINE,Other]),
+ ct:fail("Bad hello string received")
+
+ after 10000 ->
+ ct:log("Timeout waiting for kexinit!~n~s",[Ack]),
+ throw(timeout)
+ end.
+
+
+
+host_ip() ->
+ {ok,Name} = inet:gethostname(),
+ {ok,#hostent{h_addr_list = [IP|_]}} = inet_res:gethostbyname(Name),
+ IP.
+
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
new file mode 100755
index 0000000000..1cb7bf33e1
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-base-image
@@ -0,0 +1,38 @@
+#!/bin/sh
+
+UBUNTU_VER=${1:-16.04}
+
+USER=sshtester
+PWD=foobar
+
+docker build \
+ -t ubuntubuildbase \
+ --build-arg https_proxy=$HTTPS_PROXY \
+ --build-arg http_proxy=$HTTP_PROXY \
+ - <<EOF
+
+ FROM ubuntu:$UBUNTU_VER
+ WORKDIR /buildroot
+
+ # Prepare for installing OpenSSH
+ RUN apt-get update
+ RUN apt-get upgrade -y
+ RUN apt-get -y install apt-utils
+ RUN apt-get -y install build-essential zlib1g-dev
+ RUN apt-get -y install sudo iputils-ping tcptraceroute net-tools
+ RUN apt-get -y install sshpass expect
+ RUN apt-get -y install libpam0g-dev
+
+ # A user for the tests
+ RUN (echo $PWD; echo $PWD; echo; echo; echo; echo; echo; echo ) | adduser $USER
+ RUN adduser $USER sudo
+
+ # Prepare the privsep preauth environment for openssh
+ RUN mkdir -p /var/empty
+ RUN chown root:sys /var/empty
+ RUN chmod 755 /var/empty
+ RUN groupadd -f sshd
+ RUN ls /bin/false
+ RUN id -u sshd 2> /dev/null || useradd -g sshd -c 'sshd privsep' -d /var/empty -s /bin/false sshd
+
+EOF
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
new file mode 100755
index 0000000000..983c57b18b
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssh-image
@@ -0,0 +1,71 @@
+#!/bin/sh
+
+# ./create-image openssh 7.3p1 openssl 1.0.2m
+
+set -x
+
+case $1 in
+ openssh)
+ FAMssh=openssh
+ VERssh=$2
+ PFX=https://ftp.eu.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo "Unsupported: $1"
+ exit
+esac
+
+FAMssl=$3
+VERssl=$4
+
+VER=${FAMssh}${VERssh}-${FAMssl}${VERssl}
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ssh_compat_suite-${FAMssl}:${VERssl}
+
+ LABEL openssh-version=${VER}
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ # Build and install
+
+ WORKDIR ${FAMssh}-${VERssh}
+
+ # Probably VERY OpenSSH dependent...:
+ RUN ./configure --without-pie \
+ --prefix=/buildroot/ssh \
+ --with-ssl-dir=/buildroot/ssl \
+ --with-pam
+ RUN make
+ RUN make install
+ RUN echo UsePAM yes >> /buildroot/ssh/etc/sshd_config
+
+ RUN echo Built ${VER}
+
+ # Start the daemon, but keep it in foreground to avoid killing the container
+ CMD /buildroot/ssh/sbin/sshd -D -p 1234
+
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VERssh$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-ssh:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
+
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
new file mode 100755
index 0000000000..66f8358b8a
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create-ssl-image
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+# ./create-image openssl 1.0.2m
+
+case "$1" in
+ "openssl")
+ FAM=openssl
+ VER=$2
+ PFX=https://www.openssl.org/source/openssl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ "libressl")
+ FAM=libressl
+ VER=$2
+ PFX=https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-
+ SFX=.tar.gz
+ TMP=tmp.tar.gz
+ ;;
+ *)
+ echo No lib type
+ exit
+ ;;
+esac
+
+# This way of fetching the tar-file separate from the docker commands makes
+# http-proxy handling way easier. The wget command handles the $https_proxy
+# variable while the docker command must have /etc/docker/something changed
+# and the docker server restarted. That is not possible without root access.
+
+# Make a Dockerfile. This method simplifies env variable handling considerably:
+cat - > TempDockerFile <<EOF
+
+ FROM ubuntubuildbase
+
+ LABEL version=$FAM-$VER
+
+ WORKDIR /buildroot
+
+ COPY ${TMP} .
+ RUN tar xf ${TMP}
+
+ WORKDIR ${FAM}-${VER}
+
+ RUN ./config --prefix=/buildroot/ssl
+
+ RUN make
+ RUN make install
+
+ RUN echo Built ${FAM}-${VER}
+EOF
+
+# Fetch the tar file. This could be done in an "ADD ..." in the Dockerfile,
+# but then we hit the proxy problem...
+wget -O $TMP $PFX$VER$SFX
+
+# Build the image:
+docker build -t ssh_compat_suite-$FAM:$VER -f ./TempDockerFile .
+
+# Cleaning
+rm -fr ./TempDockerFile $TMP
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
new file mode 100755
index 0000000000..16b9c21d9f
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/build_scripts/create_all
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+UBUNTU_VERSION=16.04
+
+SSH_SSL_VERSIONS=(\
+ openssh 4.4p1 openssl 0.9.8zh \
+ openssh 4.5p1 openssl 0.9.8zh \
+ openssh 5.0p1 openssl 0.9.8zh \
+ openssh 6.2p2 openssl 0.9.8zh \
+ openssh 6.3p1 openssl 0.9.8zh \
+ \
+ openssh 7.1p1 openssl 1.0.0t \
+ \
+ openssh 7.1p1 openssl 1.0.1p \
+ \
+ openssh 6.6p1 openssl 1.0.2n \
+ openssh 7.1p1 openssl 1.0.2n \
+ openssh 7.6p1 openssl 1.0.2n \
+ )
+
+if [ "x$1" == "x-b" ]
+then
+ shift
+ SKIP_CREATE_BASE=true
+fi
+
+WHAT_TO_DO=$1
+
+function create_one_image ()
+{
+ SSH_FAM=$1
+ SSH_VER=$2
+ SSL_FAM=$3
+ SSL_VER=$4
+
+ [ "x$SKIP_CREATE_BASE" == "xtrue" ] || ./create-base-image || (echo "Create base failed." >&2; exit 1)
+ ./create-ssl-image $SSL_FAM $SSL_VER \
+ || (echo "Create $SSL_FAM $SSL_VER failed." >&2; exit 2)
+
+ ./create-ssh-image $SSH_FAM $SSH_VER $SSL_FAM $SSL_VER \
+ || (echo "Create $SSH_FAM $SSH_VER on $SSL_FAM $SSL_VER failed." >&2; exit 3)
+}
+
+
+case ${WHAT_TO_DO} in
+ list)
+ ;;
+ listatoms)
+ PRE="["
+ POST="]"
+ C=\'
+ COMMA=,
+ ;;
+ build_one)
+ if [ $# != 5 ]
+ then
+ echo "$0 build_one openssh SSH_ver openssl SSL_ver " && exit
+ else
+ create_one_image $2 $3 $4 $5
+ exit
+ fi
+ ;;
+ build_all)
+ ;;
+ *)
+ echo "$0 [-b] list | listatoms | build_one openssh SSH_ver openssl SSL_ver | build_all" && exit
+ ;;
+esac
+
+
+echo -n $PRE
+i=0
+while [ "x${SSH_SSL_VERSIONS[i]}" != "x" ]
+do
+ case ${WHAT_TO_DO} in
+ list*)
+ [ $i -eq 0 ] || echo $COMMA
+ echo -n $C${SSH_SSL_VERSIONS[$i]}${SSH_SSL_VERSIONS[$(( $i + 1 ))]}-${SSH_SSL_VERSIONS[$(( $i + 2 ))]}${SSH_SSL_VERSIONS[$(( $i + 3 ))]}$C
+ ;;
+ build_all)
+ create_one_image ${SSH_SSL_VERSIONS[$i]} ${SSH_SSL_VERSIONS[$(( $i + 1 ))]} ${SSH_SSL_VERSIONS[$(( $i + 2 ))]} ${SSH_SSL_VERSIONS[$(( $i + 3 ))]}
+ ;;
+ esac
+
+ i=$(( $i + 4 ))
+done
+echo $POST
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
new file mode 100644
index 0000000000..8b2354a7ea
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBugIBAAKBgQDlXDEddxFbTtPsu2bRTbSONFVKMxe430iqBoXoKK2Gyhlqn7J8
+SRGlmvTN7T06+9iFqgJi+x+dlSJGlNEY/v67Z8C7rWfJynYuRier4TujLwP452RT
+YrsnCq47pGJXHb9xAWr7UGMv85uDrECUiIdK4xIrwpW/gMb5zPSThDGNiwIVANts
+B9nBX0NH/B0lXthVCg2jRSkpAoGAIS3vG8VmjQNYrGfdcdvQtGubFXs4jZJO6iDe
+9u9/O95dcnH4ZIL4y3ZPHbw73dCKXFe5NlqI/POmn3MyFdpyqH5FTHWB/aAFrma6
+qo00F1mv83DkQCEfg6fwE/SaaBjDecr5I14hWOtocpYqlY1/x1aspahwK6NLPp/D
+A4aAt78CgYAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7Xmyq
+blfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZ
+iEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpgIURgW8SZGj
+X0mMkMJv/Ltdt0gYx60=
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
new file mode 100644
index 0000000000..9116493472
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_dsa_key.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAOVcMR13EVtO0+y7ZtFNtI40VUozF7jfSKoGhegorYbKGWqfsnxJEaWa9M3tPTr72IWqAmL7H52VIkaU0Rj+/rtnwLutZ8nKdi5GJ6vhO6MvA/jnZFNiuycKrjukYlcdv3EBavtQYy/zm4OsQJSIh0rjEivClb+AxvnM9JOEMY2LAAAAFQDbbAfZwV9DR/wdJV7YVQoNo0UpKQAAAIAhLe8bxWaNA1isZ91x29C0a5sVeziNkk7qIN7273873l1ycfhkgvjLdk8dvDvd0IpcV7k2Woj886afczIV2nKofkVMdYH9oAWuZrqqjTQXWa/zcORAIR+Dp/AT9JpoGMN5yvkjXiFY62hyliqVjX/HVqylqHAro0s+n8MDhoC3vwAAAIAmNgr3dnHgMXrEsAeHswioAad3YLtnPvdFdHqd5j4oSbgKwFd7XmyqblfeQ6rRo8dmUF0rkUU8cn71IqbhpsCJQEZPt9WBlhHiY95B1ELKYHoHCbZA8qrZiEIcfwch85Da0/uzv4VE0UHTC0P3WRD3sZDfXd9dZLdc80n6ImYRpg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..5ed2b361cc
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEILwQIf+Jul+oygeJn7cBSvn2LGqnW1ZfiHDQMDXZ96mooAoGCCqGSM49
+AwEHoUQDQgAEJUo0gCIhXEPJYvxec23IAjq7BjV1xw8deI8JV9vL5BMCZNhyj5Vt
+NbFPbKPuL/Sikn8p4YP/5y336ug7szvYrg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..240387d901
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBCVKNIAiIVxDyWL8XnNtyAI6uwY1dccPHXiPCVfby+QTAmTYco+VbTWxT2yj7i/0opJ/KeGD/+ct9+roO7M72K4= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..9d31d75cd5
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBw+P1sic2i41wTGQgjyUlBtxQfnY77L8TFcDngoRiVrbCugnDrioNo
+JogqymWhSC+gBwYFK4EEACKhZANiAATwaqEp3vyLzfb08kqgIZLv/mAYJyGD+JMt
+f11OswGs3uFkbHZOErFCgeLuBvarSTAFkOlMR9GZGaDEfcrPBTtvKj+jEaAvh6yr
+JxS97rtwk2uadDMem2x4w9Ga4jw4S8E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..cca85bda72
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBPBqoSne/IvN9vTySqAhku/+YBgnIYP4ky1/XU6zAaze4WRsdk4SsUKB4u4G9qtJMAWQ6UxH0ZkZoMR9ys8FO28qP6MRoC+HrKsnFL3uu3CTa5p0Mx6bbHjD0ZriPDhLwQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..b698be1ec9
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIBtGVvyn7kGX7BfWAYHK2ZXmhWscTOV0J0mAfab0u0ZMw0id2a3O9s
+sBjJoCqoAXTJ7d/OUw85qqQNDE5GDQpDFq6gBwYFK4EEACOhgYkDgYYABAHPWfUD
+tQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJ
+yO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuh
+RPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..d181d30d69
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHPWfUDtQ/JmfwmmSdWWjGm94hFqwaivI4H43acDdd71+vods4rN2Yh3X7fSUvJkeOhXFOJyO9F+61ssKgS0a0nxQEvdXks3QyfKTPjYQuBUvY+AV/A4AskPBz731xCDmbYuWuhRPekZ7d5bF0U0pGlExbX+naQJMSbJSdZrPM9993EmA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
new file mode 100644
index 0000000000..84096298ca
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAuC6uxC0P8voYQCrwJzczo9iSiwsovPv4etd2BLnu8cKWdnjR
+34tWvtguw2kO+iDyt4hFGGfDBQf2SXl+ZEsE2N1RlSp5A73me2byw/L4MreX2rbU
+TwyNXF3TBvKb3Gbpx7PoiB9frcb9RCMxtypBvGQD6bx6h5UWKuSkYzARaRLv3kbB
+swcqfrA3PfWybkIoaa2RO1Ca86u6K0v+a4r0OfRxTnghuakZkH6CD7+uU3irliPI
+UFt2wTI/qWmnDrMFh4RffToHK0QZHXdkq4ama5kRZdZ0svSorxqkl8EWGPhReoUj
+Yrz0bCNevSlDxHCxLi8epRxuv+AhZHW0YdMCCwIDAQABAoIBAHUyj1aZbfqolWHP
+cL0jbSKnHqiHU0bd9sED9T8QqTEBJwj/3Fwop+wMV8VURol3CbsrZPwgmoHLDTa3
+rmtXKSBtxAns2tA8uDpxyaxSIQj0thYgHHyoehL6SNu06OSYP84pdp+XhyRm6KXA
+11O7+dRMuAi1PCql/VMR5mCPJ6T5qWAVYHFyEBvMm4q5yYSRSPaAaZHC6WbEsxHN
+jGzcyl3tvmOyN0+M7v0U86lQ+H2tSXH+nQg/Ig6hWgFGg8AYoos/9yUGOY+e9bUE
+serYdsuiyxBfo4CgoSeDsjwNp1lAZ5UOrIDdRqK9C8jGVkHDzwfmmtczWXkVVzGZ
+Bd05izECgYEA31yHzSA/umamyZAQbi/5psk1Fc5m6MzsgmJmB6jm7hUZ0EbpSV4C
+6b1nOrk/IAtA12rvDHgWy0zpkJbC5b03C77RnBgTRgLQyolrcpLDJ47+Kxf/AHGk
+m63KaCpwZEQ4f9ARBXySD/jNoW9gz5S6Xa3RnHOC70DsIIk5VOCjWk0CgYEA0xiM
+Ay27PJcbAG/4tnjH8DZfHb8SULfnfUj8kMe3V2SDPDWbhY8zheo45wTBDRflFU5I
+XyGmfuZ7PTTnFVrJz8ua3mAMOzkFn4MmdaRCX9XtuE4YWq3lFvxlrJvfXSjEL0km
+8UwlhJMixaEPqFQjsKc9BHwWKRiKcF4zFQ1DybcCgYB46yfdhYLaj23lmqc6b6Bw
+iWbCql2N1DqJj2l65hY2d5fk6C6s+EcNcOrsoJKq70yoEgzdrDlyz+11yBg0tU2S
+fzgMkAAHG8kajHBts0QRK1kvzSrQe7VITjpQUAFOVpxbnTFJzhloqiHwLlKzremC
+g3IBh4svqO7r4j32VDI61QKBgQCQL4gS872cWSncVp7vI/iNHtZBHy2HbNX1QVEi
+Iwgb7U+mZIdh5roukhlj0l96bgPPVbUhJX7v1sX+vI/KikSmZk/V7IzuNrich5xR
+ZmzfwOOqq8z+wyBjXuqjx6P9oca+9Zxf3L8Tmtx5WNW1CCOImfKXiZopX9XPgsgp
+bPIMaQKBgQCql4uTSacSQ5s6rEEdvR+y6nTohF3zxhOQ+6xivm3Hf1mgTk40lQ+t
+sr6HsSTv8j/ZbhhtaUUb2efro3pDztjlxXFvITar9ZDB2B4QMlpSsDR9UNk8xKGY
+J9aYLr4fJC6J6VA7Wf0yq6LpjSXRH/2GeNtmMl5rFRsHt+VU7GZK9g==
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
new file mode 100644
index 0000000000..4ac6e7b124
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/host_keys/ssh_host_rsa_key.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4Lq7ELQ/y+hhAKvAnNzOj2JKLCyi8+/h613YEue7xwpZ2eNHfi1a+2C7DaQ76IPK3iEUYZ8MFB/ZJeX5kSwTY3VGVKnkDveZ7ZvLD8vgyt5fattRPDI1cXdMG8pvcZunHs+iIH1+txv1EIzG3KkG8ZAPpvHqHlRYq5KRjMBFpEu/eRsGzByp+sDc99bJuQihprZE7UJrzq7orS/5rivQ59HFOeCG5qRmQfoIPv65TeKuWI8hQW3bBMj+paacOswWHhF99OgcrRBkdd2SrhqZrmRFl1nSy9KivGqSXwRYY+FF6hSNivPRsI169KUPEcLEuLx6lHG6/4CFkdbRh0wIL uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
new file mode 100644
index 0000000000..01a88acea2
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa
@@ -0,0 +1,12 @@
+-----BEGIN DSA PRIVATE KEY-----
+MIIBvAIBAAKBgQC97XncQDaa9PQYEWK7llBxZQ2suVYTz1eadw2HtY+Y8ZKdUBLd
+9LUQ2lymUC9yq66rb5pBBR13k/9Zcbu8I0nafrZT4wJ4H0YGD6Ob5O4HR4EHjO5q
+hgnMJ17e1XnzI31MW5xAuAHTLLClNvnG05T1jaU+tRAsVSCHin3+sOenowIVAMSe
+ANBvw7fm5+Lw+ziOAHPjeYzRAoGBALkWCGpKmlJ65F3Y/RcownHQvsrDAllzKF/a
+cSfriCVVP5qVZ3Ach28ZZ9BFEnRE2SKqVsyBAiceb/+ISlu8CqKEvvoNIMJAu5rU
+MwZh+PeHN4ES6tWTwBGAwu84ke6N4BgV+6Q4qkcyywHsT5oU0EdVbn2zzAZw8c7v
+BpbsJ1KsAoGABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CI
+TjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJ
+MIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+WsCFQCRJayH
+P4vM1XUOVEeX7u04K1EAFg==
+-----END DSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
new file mode 100644
index 0000000000..30661d5adf
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_dsa.pub
@@ -0,0 +1 @@
+ssh-dss AAAAB3NzaC1kc3MAAACBAL3tedxANpr09BgRYruWUHFlDay5VhPPV5p3DYe1j5jxkp1QEt30tRDaXKZQL3KrrqtvmkEFHXeT/1lxu7wjSdp+tlPjAngfRgYPo5vk7gdHgQeM7mqGCcwnXt7VefMjfUxbnEC4AdMssKU2+cbTlPWNpT61ECxVIIeKff6w56ejAAAAFQDEngDQb8O35ufi8Ps4jgBz43mM0QAAAIEAuRYIakqaUnrkXdj9FyjCcdC+ysMCWXMoX9pxJ+uIJVU/mpVncByHbxln0EUSdETZIqpWzIECJx5v/4hKW7wKooS++g0gwkC7mtQzBmH494c3gRLq1ZPAEYDC7ziR7o3gGBX7pDiqRzLLAexPmhTQR1VufbPMBnDxzu8GluwnUqwAAACABraHWqSFhaX4+GHmtKwXZFVRKh/4R6GR2LpkFzGm3Ixv+eo9K5CITjiBYiVMrWH23G1LiDuJyMGqHEnIef+sorNfNzdnwq+8qRCTS6mbpRXkUt9p1arJMIKmosS+GFhTN6Z85gCwC51S2EDC4GW7J4ViHKacr1FwJSw9RC9F+Ws= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
new file mode 100644
index 0000000000..60e8f6eb6e
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIC557KPgmq+pWOAh1L8DV8GWW0u7W5vz6mim3FFB1l8koAoGCCqGSM49
+AwEHoUQDQgAEC3J5fQ8+8xQso0lhBdoLdvD14oSsQiMuweXq+Dy2+4Mjdw2/bbw0
+CvbE2+KWNcgwxRLycNGcMCBdf/cOgNyGkA==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
new file mode 100644
index 0000000000..b349d26da3
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAtyeX0PPvMULKNJYQXaC3bw9eKErEIjLsHl6vg8tvuDI3cNv228NAr2xNviljXIMMUS8nDRnDAgXX/3DoDchpA= sshtester@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
new file mode 100644
index 0000000000..ece6c8f284
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDBdgJs/xThHiy/aY1ymtQ4B0URNnRCm8l2WZMFjua57+nvq4Duf+igN
+pN/5p/+azLKgBwYFK4EEACKhZANiAATUw6pT/UW2HvTW6lL2BGY7NfUGEX285XVi
+9AcTXH1K+TOekbGmcpSirlGzSb15Wycajpmaae5vAzH1nnvcVd3FYODVdDXTHgV/
+FeXQ+vaw7CZnEAKZsr8mjXRX3fEyO1E=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
new file mode 100644
index 0000000000..fd81e220f7
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBNTDqlP9RbYe9NbqUvYEZjs19QYRfbzldWL0BxNcfUr5M56RsaZylKKuUbNJvXlbJxqOmZpp7m8DMfWee9xV3cVg4NV0NdMeBX8V5dD69rDsJmcQApmyvyaNdFfd8TI7UQ== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521 b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
new file mode 100644
index 0000000000..21c000ea03
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEEhm0w3xcGILU8eP61mThVBwCJfyzrFktGf7cCa1ciL4YLsukd20Q3Z
+yp0YcEDLcEm36CZGabgkEvblJ1Rx2lPTu6AHBgUrgQQAI6GBiQOBhgAEAYep8cX2
+7wUPw5pNYwFkWQXrJ2GSkmO8iHwkWJ6srRay/sF3WoPF/dyDVymFgirtsSTJ+D0u
+ex4qphOOJxkd1Yf+ANHvDFN9LoBvbgtNLTRJlpuNLCdWQlt+mEnPMDGMV/HWHHiz
+7/mWE+XUVIcQjhm5uv0ObI/wroZEurXMGEhTis3L
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
new file mode 100644
index 0000000000..d9830da5de
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAGHqfHF9u8FD8OaTWMBZFkF6ydhkpJjvIh8JFierK0Wsv7Bd1qDxf3cg1cphYIq7bEkyfg9LnseKqYTjicZHdWH/gDR7wxTfS6Ab24LTS00SZabjSwnVkJbfphJzzAxjFfx1hx4s+/5lhPl1FSHEI4Zubr9DmyP8K6GRLq1zBhIU4rNyw== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
new file mode 100644
index 0000000000..2e50ac2304
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA7+C3gLoflKybq4I+clbg2SWf6cXyHpnLNDnZeMvIbOz2X/Ce
+XUm17DFeexTaVBs9Zq9WwDFOFkLQhbuXgpvB0shSY0nr+Em7InRM8AiRLxPe0txM
+mFFhL+v083dYwgaJOo1PthNM/tGRZJu+0sQDqrmN7CusFHdZg2NTzTzbwWqPiuP/
+mf3o7W4CWqDTBzbYTgpWlH7vRZf9FYwT4on5YWzLA8TvO2TwBGTfTMK5nswH++iO
+v4jKecoEwyBFMUSKqZ9UYHGw/kshHbltM65Ye/xjXEX0GxDdxu8ZyVKXd4acNbJJ
+P0tcxN4GzKJiR6zNYwCzDhjqDEbM5qCGhShhgQIDAQABAoIBAQCucdGBP9mvmUcs
+Fu+q3xttTztYGqfVMSrhtCA/BJOhA0K4ypegZ/Zw6gY3pBaSi6y/fEuuQSz0a2qR
+lra8OOFflGa15hBA4/2/NKyu8swCXITy+1qIesYev43HcMePcolhl1qcorSfq2/8
+pnbDd+Diy0Y2thvSVmk2b4mF+/gkUx3CHLhgRMcxCHLG1VeqIfLf+pa0jIw94tZ5
+CoIoI096pDTsneO9xhh1QxWQRRFVqdf3Q9zyiBgJCggpX+1fVsbQejuEK4hKRBKx
+SRPX/pX5aU+7+KSZ/DbtXGg1sCw9NUDFTIEV3UPmko4oWawNGv/CQAK80g3go28v
+UnVf11BBAoGBAP2amIFp+Ps33A5eesT7g/NNkGqBEi5W37K8qzYJxqXJvH0xmpFo
+8a3Je3PQRrzbTUJyISA6/XNnA62+bEvWiEXPiK3stQzNHoVz7ftCb19zgW4sLKRW
+Qhjq7QsGeRrdksJnZ7ekfzOv658vHJPElS1MdPu2WWhiNvAjtmdyFQulAoGBAPIk
+6831QAnCdp/ffH/K+cqV9vQYOFig8n4mQNNC+sLghrtZh9kbmTuuNKAhF56vdCCn
+ABD/+RiLXKVsF0PvQ5g9wRLKaiJubXI7XEBemCCLhjtESxGpWEV8GalslUgE1cKs
+d1pwSVjd0sYt0gOAf2VRhlbpSWhXA2xVll34xgetAoGAHaI089pYN7K9SgiMO/xP
+3NxRZcCTSUrpdM9LClN2HOVH2zEyqI8kvnPuswfBXEwb6QnBCS0bdKKy8Vhw+yOk
+ZNPtWrVwKoDFcj6rrlKDBRpQI3mR9doGezboYANvn04I2iKPIgxcuMNzuvQcWL/9
+1n86pDcYl3Pyi3kA1XGlN+kCgYEAz1boBxpqdDDsjGa8X1y5WUviAw8+KD3ghj5R
+IdTnjbjeBUxbc38bTawUac0MQZexE0iMWQImFGs4sHkGzufwdErkqSdjjAoMc1T6
+4C9fifaOwO7wbLYZ3J2wB4/vn5RsSV6OcIVXeN2wXnvbqZ38+A+/vWnSrqJbTwdW
+Uy7yup0CgYEA8M9vjpAoCr3XzNDwJyWRQcT7e+nRYUNDlXBl3jpQhHuJtnSnkoUv
+HXYXEwvp8peycNzeVz5OwFVMzCH8OG4WiGN4Pmo0rDWHED/W7eIRHIitHGTzZ+Qw
+gRxscoewblSLSkYMXidBLmQjr4U5bDBesRuGhm5NuLyMTa1f3Pc/90k=
+-----END RSA PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
new file mode 100644
index 0000000000..26e560d4f8
--- /dev/null
+++ b/lib/ssh/test/ssh_compat_SUITE_data/users_keys/id_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDv4LeAuh+UrJurgj5yVuDZJZ/pxfIemcs0Odl4y8hs7PZf8J5dSbXsMV57FNpUGz1mr1bAMU4WQtCFu5eCm8HSyFJjSev4SbsidEzwCJEvE97S3EyYUWEv6/Tzd1jCBok6jU+2E0z+0ZFkm77SxAOquY3sK6wUd1mDY1PNPNvBao+K4/+Z/ejtbgJaoNMHNthOClaUfu9Fl/0VjBPiiflhbMsDxO87ZPAEZN9MwrmezAf76I6/iMp5ygTDIEUxRIqpn1RgcbD+SyEduW0zrlh7/GNcRfQbEN3G7xnJUpd3hpw1skk/S1zE3gbMomJHrM1jALMOGOoMRszmoIaFKGGB uabhnil@elxadlj3q32
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index b0e38fb9ad..6022e69907 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -2301,7 +2301,7 @@ calc_mac_hash(Type, Version,
MacSecret, SeqNo, Type,
Length, PlainFragment).
-is_stream_ciphersuite({_, rc4_128, _, _}) ->
+is_stream_ciphersuite(#{cipher := rc4_128}) ->
true;
is_stream_ciphersuite(_) ->
false.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 03c3ed9be3..e74529b455 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -1024,15 +1024,26 @@ string_regex_filter(Str, Search) when is_list(Str) ->
string_regex_filter(_Str, _Search) ->
false.
-anonymous_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))].
-
-psk_suites(Version) ->
- [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))].
-
-psk_anon_suites(Version) ->
- [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)].
-
+anonymous_suites({3,_ } = Version) ->
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version))];
+anonymous_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:anonymous_suites(Version)),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
+
+psk_suites({3,_ } = Version) ->
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version))];
+psk_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:filter_suites(ssl_cipher:psk_suites(Version)),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(ssl_cipher:erl_suite_definition(S)))].
+
+psk_anon_suites({3,_ } = Version) ->
+ [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite)];
+psk_anon_suites(DTLSVersion) ->
+ Version = dtls_v1:corresponding_tls_version(DTLSVersion),
+ [Suite || Suite <- psk_suites(Version), is_psk_anon_suite(Suite),
+ not ssl_cipher:is_stream_ciphersuite(tuple_to_map(Suite))].
srp_suites() ->
[ssl_cipher:erl_suite_definition(Suite) ||
Suite <-
@@ -1335,8 +1346,9 @@ enough_openssl_crl_support(_) -> true.
wait_for_openssl_server(Port, tls) ->
do_wait_for_openssl_tls_server(Port, 10);
-wait_for_openssl_server(Port, dtls) ->
- do_wait_for_openssl_dtls_server(Port, 10).
+wait_for_openssl_server(_Port, dtls) ->
+ ok. %% No need to wait for DTLS over UDP server
+ %% client will retransmitt until it is up.
do_wait_for_openssl_tls_server(_, 0) ->
exit(failed_to_connect_to_openssl);
@@ -1349,21 +1361,6 @@ do_wait_for_openssl_tls_server(Port, N) ->
do_wait_for_openssl_tls_server(Port, N-1)
end.
-do_wait_for_openssl_dtls_server(_, 0) ->
- %%exit(failed_to_connect_to_openssl);
- ok;
-do_wait_for_openssl_dtls_server(Port, N) ->
- %% case gen_udp:open(0) of
- %% {ok, S} ->
- %% gen_udp:connect(S, "localhost", Port),
- %% gen_udp:close(S);
- %% _ ->
- %% ct:sleep(?SLEEP),
- %% do_wait_for_openssl_dtls_server(Port, N-1)
- %% end.
- ct:sleep(500),
- do_wait_for_openssl_dtls_server(Port, N-1).
-
version_flag(tlsv1) ->
"-tls1";
version_flag('tlsv1.1') ->
@@ -1664,78 +1661,3 @@ hardcode_dsa_key(3) ->
y = 48598545580251057979126570873881530215432219542526130654707948736559463436274835406081281466091739849794036308281564299754438126857606949027748889019480936572605967021944405048011118039171039273602705998112739400664375208228641666852589396502386172780433510070337359132965412405544709871654840859752776060358,
x = 1457508827177594730669011716588605181448418352823}.
-dtls_hello() ->
- [1,
- <<0,1,4>>,
- <<0,0>>,
- <<0,0,0>>,
- <<0,1,4>>,
- <<254,253,88,
- 156,129,61,
- 131,216,15,
- 131,194,242,
- 46,154,190,
- 20,228,234,
- 234,150,44,
- 62,96,96,103,
- 127,95,103,
- 23,24,42,138,
- 13,142,32,57,
- 230,177,32,
- 210,154,152,
- 188,121,134,
- 136,53,105,
- 118,96,106,
- 103,231,223,
- 133,10,165,
- 50,32,211,
- 227,193,14,
- 181,143,48,
- 66,0,0,100,0,
- 255,192,44,
- 192,48,192,
- 36,192,40,
- 192,46,192,
- 50,192,38,
- 192,42,0,159,
- 0,163,0,107,
- 0,106,0,157,
- 0,61,192,43,
- 192,47,192,
- 35,192,39,
- 192,45,192,
- 49,192,37,
- 192,41,0,158,
- 0,162,0,103,
- 0,64,0,156,0,
- 60,192,10,
- 192,20,0,57,
- 0,56,192,5,
- 192,15,0,53,
- 192,8,192,18,
- 0,22,0,19,
- 192,3,192,13,
- 0,10,192,9,
- 192,19,0,51,
- 0,50,192,4,
- 192,14,0,47,
- 1,0,0,86,0,0,
- 0,14,0,12,0,
- 0,9,108,111,
- 99,97,108,
- 104,111,115,
- 116,0,10,0,
- 58,0,56,0,14,
- 0,13,0,25,0,
- 28,0,11,0,12,
- 0,27,0,24,0,
- 9,0,10,0,26,
- 0,22,0,23,0,
- 8,0,6,0,7,0,
- 20,0,21,0,4,
- 0,5,0,18,0,
- 19,0,1,0,2,0,
- 3,0,15,0,16,
- 0,17,0,11,0,
- 2,1,0>>].
-
diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml
index 64fcf4379f..72c774e6ef 100644
--- a/lib/stdlib/doc/src/io.xml
+++ b/lib/stdlib/doc/src/io.xml
@@ -257,8 +257,9 @@ ok</pre>
\x{400}
ok
5> <input>io:fwrite("~s~n",[[1024]]).</input>
-** exception exit: {badarg,[{io,format,[&lt;0.26.0&gt;,"~s~n",[[1024]]]},
- ...</pre>
+** exception error: bad argument
+ in function io:format/3
+ called as io:format(&lt;0.53.0>,"~s~n",[[1024]])</pre>
</item>
<tag><c>w</c></tag>
<item>
@@ -454,12 +455,9 @@ ok</pre>
abc def 'abc def' {foo,1} A
ok
2> <input>io:fwrite("~s", [65]).</input>
-** exception exit: {badarg,[{io,format,[&lt;0.22.0>,"~s","A"]},
- {erl_eval,do_apply,5},
- {shell,exprs,6},
- {shell,eval_exprs,6},
- {shell,eval_loop,3}]}
- in function io:o_request/2</pre>
+** exception error: bad argument
+ in function io:format/3
+ called as io:format(&lt;0.53.0>,"~s","A")</pre>
<p>In this example, an attempt was made to output the single
character 65 with the aid of the string formatting directive
<c>"~s"</c>.</p>
diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl
index eafee346eb..4ee11383da 100644
--- a/lib/stdlib/src/erl_eval.erl
+++ b/lib/stdlib/src/erl_eval.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -69,6 +69,9 @@
-type(non_local_function_handler() :: {value, nlfun_handler()}
| none).
+-define(STACKTRACE,
+ element(2, erlang:process_info(self(), current_stacktrace))).
+
%% exprs(ExpressionSeq, Bindings)
%% exprs(ExpressionSeq, Bindings, LocalFuncHandler)
%% exprs(ExpressionSeq, Bindings, LocalFuncHandler, ExternalFuncHandler)
@@ -90,7 +93,7 @@ exprs(Exprs, Bs) ->
ok ->
exprs(Exprs, Bs, none, none, none);
{error,{_Line,_Mod,Error}} ->
- erlang:raise(error, Error, [{?MODULE,exprs,2}])
+ erlang:raise(error, Error, ?STACKTRACE)
end.
-spec(exprs(Expressions, Bindings, LocalFunctionHandler) ->
@@ -141,7 +144,7 @@ expr(E, Bs) ->
ok ->
expr(E, Bs, none, none, none);
{error,{_Line,_Mod,Error}} ->
- erlang:raise(error, Error, [{?MODULE,expr,2}])
+ erlang:raise(error, Error, ?STACKTRACE)
end.
-spec(expr(Expression, Bindings, LocalFunctionHandler) ->
@@ -182,7 +185,7 @@ check_command(Es, Bs) ->
fun_data(F) when is_function(F) ->
case erlang:fun_info(F, module) of
- {module,erl_eval} ->
+ {module,?MODULE} ->
case erlang:fun_info(F, env) of
{env,[{FBs,_FLf,_FEf,FCs}]} ->
{fun_data,FBs,FCs};
@@ -209,8 +212,8 @@ expr({var,_,V}, Bs, _Lf, _Ef, RBs) ->
case binding(V, Bs) of
{value,Val} ->
ret_expr(Val, Bs, RBs);
- unbound -> % Should not happen.
- erlang:raise(error, {unbound,V}, stacktrace())
+ unbound -> % Cannot not happen if checked by erl_lint
+ erlang:raise(error, {unbound,V}, ?STACKTRACE)
end;
expr({char,_,C}, Bs, _Lf, _Ef, RBs) ->
ret_expr(C, Bs, RBs);
@@ -236,13 +239,13 @@ expr({tuple,_,Es}, Bs0, Lf, Ef, RBs) ->
{Vs,Bs} = expr_list(Es, Bs0, Lf, Ef),
ret_expr(list_to_tuple(Vs), Bs, RBs);
expr({record_field,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, {undef_record,Name}, stacktrace());
+ erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
expr({record_index,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, {undef_record,Name}, stacktrace());
+ erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
expr({record,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, {undef_record,Name}, stacktrace());
+ erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
expr({record,_,_,Name,_}, _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, {undef_record,Name}, stacktrace());
+ erlang:raise(error, {undef_record,Name}, ?STACKTRACE);
%% map
expr({map,_,Binding,Es}, Bs0, Lf, Ef, RBs) ->
@@ -281,7 +284,7 @@ expr({'fun',_Line,{function,Mod0,Name0,Arity0}}, Bs0, Lf, Ef, RBs) ->
ret_expr(F, Bs, RBs);
expr({'fun',_Line,{function,Name,Arity}}, _Bs0, _Lf, _Ef, _RBs) -> % R8
%% Don't know what to do...
- erlang:raise(error, undef, [{erl_eval,Name,Arity}|stacktrace()]);
+ erlang:raise(error, undef, [{?MODULE,Name,Arity}|?STACKTRACE]);
expr({'fun',Line,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs) ->
%% Save only used variables in the function environment.
%% {value,L,V} are hidden while lint finds used variables.
@@ -326,7 +329,7 @@ expr({'fun',Line,{clauses,Cs}} = Ex, Bs, Lf, Ef, RBs) ->
eval_fun([A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T], Info) end;
_Other ->
erlang:raise(error, {'argument_limit',{'fun',Line,Cs}},
- stacktrace())
+ ?STACKTRACE)
end,
ret_expr(F, Bs, RBs);
expr({named_fun,Line,Name,Cs} = Ex, Bs, Lf, Ef, RBs) ->
@@ -378,7 +381,7 @@ expr({named_fun,Line,Name,Cs} = Ex, Bs, Lf, Ef, RBs) ->
RF, Info) end;
_Other ->
erlang:raise(error, {'argument_limit',{named_fun,Line,Name,Cs}},
- stacktrace())
+ ?STACKTRACE)
end,
ret_expr(F, Bs, RBs);
expr({call,_,{remote,_,{atom,_,qlc},{atom,_,q}},[{lc,_,_E,_Qs}=LC | As0]},
@@ -422,25 +425,28 @@ expr({call,_,Func0,As0}, Bs0, Lf, Ef, RBs) -> % function or {Mod,Fun}
{As,Bs2} = expr_list(As0, Bs1, Lf, Ef),
case Func of
{M,F} when is_atom(M), is_atom(F) ->
- erlang:raise(error, {badfun,Func}, stacktrace());
+ erlang:raise(error, {badfun,Func}, ?STACKTRACE);
_ ->
do_apply(Func, As, Bs2, Ef, RBs)
end;
expr({'catch',_,Expr}, Bs0, Lf, Ef, RBs) ->
- Ref = make_ref(),
- case catch {Ref,expr(Expr, Bs0, Lf, Ef, none)} of
- {Ref,{value,V,Bs}} -> % Nothing was thrown (guaranteed).
- ret_expr(V, Bs, RBs);
- Other ->
- ret_expr(Other, Bs0, RBs)
+ try expr(Expr, Bs0, Lf, Ef, none) of
+ {value,V,Bs} ->
+ ret_expr(V, Bs, RBs)
+ catch
+ throw:Term ->
+ ret_expr(Term, Bs0, RBs);
+ exit:Reason ->
+ ret_expr({'EXIT',Reason}, Bs0, RBs);
+ error:Reason:Stacktrace ->
+ ret_expr({'EXIT',{Reason,Stacktrace}}, Bs0, RBs)
end;
expr({match,_,Lhs,Rhs0}, Bs0, Lf, Ef, RBs) ->
{value,Rhs,Bs1} = expr(Rhs0, Bs0, Lf, Ef, none),
case match(Lhs, Rhs, Bs1) of
{match,Bs} ->
ret_expr(Rhs, Bs, RBs);
- nomatch ->
- erlang:raise(error, {badmatch,Rhs}, stacktrace())
+ nomatch -> erlang:raise(error, {badmatch,Rhs}, ?STACKTRACE)
end;
expr({op,_,Op,A0}, Bs0, Lf, Ef, RBs) ->
{value,A,Bs} = expr(A0, Bs0, Lf, Ef, none),
@@ -452,7 +458,7 @@ expr({op,_,'andalso',L0,R0}, Bs0, Lf, Ef, RBs) ->
{value,R,_} = expr(R0, Bs1, Lf, Ef, none),
R;
false -> false;
- _ -> erlang:raise(error, {badarg,L}, stacktrace())
+ _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE)
end,
ret_expr(V, Bs1, RBs);
expr({op,_,'orelse',L0,R0}, Bs0, Lf, Ef, RBs) ->
@@ -462,7 +468,7 @@ expr({op,_,'orelse',L0,R0}, Bs0, Lf, Ef, RBs) ->
false ->
{value,R,_} = expr(R0, Bs1, Lf, Ef, none),
R;
- _ -> erlang:raise(error, {badarg,L}, stacktrace())
+ _ -> erlang:raise(error, {badarg,L}, ?STACKTRACE)
end,
ret_expr(V, Bs1, RBs);
expr({op,_,Op,L0,R0}, Bs0, Lf, Ef, RBs) ->
@@ -474,7 +480,7 @@ expr({bin,_,Fs}, Bs0, Lf, Ef, RBs) ->
{value,V,Bs} = eval_bits:expr_grp(Fs, Bs0, EvalFun),
ret_expr(V, Bs, RBs);
expr({remote,_,_,_}, _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, {badexpr,':'}, stacktrace());
+ erlang:raise(error, {badexpr,':'}, ?STACKTRACE);
expr({value,_,Val}, Bs, _Lf, _Ef, RBs) -> % Special case straight values.
ret_expr(Val, Bs, RBs).
@@ -570,7 +576,7 @@ local_func(Func, As, _Bs, {M,F,Eas}, _Ef, RBs) ->
local_func2(apply(M, F, [Func,As|Eas]), RBs);
%% Default unknown function handler to undefined function.
local_func(Func, As0, _Bs0, none, _Ef, _RBs) ->
- erlang:raise(error, undef, [{erl_eval,Func,length(As0)}|stacktrace()]).
+ erlang:raise(error, undef, [{?MODULE,Func,length(As0)}|?STACKTRACE]).
local_func2({value,V,Bs}, RBs) ->
ret_expr(V, Bs, RBs);
@@ -637,7 +643,7 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
{{arity, Arity}, Arity} ->
eval_fun(FCs, As, FBs, FLf, FEf, NRBs);
_ ->
- erlang:raise(error, {badarity,{Func,As}},stacktrace())
+ erlang:raise(error, {badarity,{Func,As}},?STACKTRACE)
end;
{{env,[{FBs,FLf,FEf,FCs,FName}]},_} ->
NRBs = if
@@ -648,7 +654,7 @@ do_apply(Func, As, Bs0, Ef, RBs) ->
{{arity, Arity}, Arity} ->
eval_named_fun(FCs, As, FBs, FLf, FEf, FName, Func, NRBs);
_ ->
- erlang:raise(error, {badarity,{Func,As}},stacktrace())
+ erlang:raise(error, {badarity,{Func,As}},?STACKTRACE)
end;
{no_env,none} when RBs =:= value ->
%% Make tail recursive calls when possible.
@@ -730,7 +736,7 @@ eval_generate([V|Rest], P, Bs0, Lf, Ef, CompFun, Acc) ->
eval_generate([], _P, _Bs0, _Lf, _Ef, _CompFun, Acc) ->
Acc;
eval_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) ->
- erlang:raise(error, {bad_generator,Term}, stacktrace()).
+ erlang:raise(error, {bad_generator,Term}, ?STACKTRACE).
eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) ->
Mfun = match_fun(Bs0),
@@ -746,7 +752,7 @@ eval_b_generate(<<_/bitstring>>=Bin, P, Bs0, Lf, Ef, CompFun, Acc) ->
Acc
end;
eval_b_generate(Term, _P, _Bs0, _Lf, _Ef, _CompFun, _Acc) ->
- erlang:raise(error, {bad_generator,Term}, stacktrace()).
+ erlang:raise(error, {bad_generator,Term}, ?STACKTRACE).
eval_filter(F, Bs0, Lf, Ef, CompFun, Acc) ->
case erl_lint:is_guard_test(F) of
@@ -760,7 +766,7 @@ eval_filter(F, Bs0, Lf, Ef, CompFun, Acc) ->
{value,true,Bs1} -> CompFun(Bs1);
{value,false,_} -> Acc;
{value,V,_} ->
- erlang:raise(error, {bad_filter,V}, stacktrace())
+ erlang:raise(error, {bad_filter,V}, ?STACKTRACE)
end
end.
@@ -816,7 +822,7 @@ eval_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, RBs) ->
end;
eval_fun([], As, _Bs, _Lf, _Ef, _RBs) ->
erlang:raise(error, function_clause,
- [{?MODULE,'-inside-an-interpreted-fun-',As}|stacktrace()]).
+ [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]).
eval_named_fun(As, Fun, {Bs0,Lf,Ef,Cs,Name}) ->
@@ -836,7 +842,7 @@ eval_named_fun([{clause,_,H,G,B}|Cs], As, Bs0, Lf, Ef, Name, Fun, RBs) ->
end;
eval_named_fun([], As, _Bs, _Lf, _Ef, _Name, _Fun, _RBs) ->
erlang:raise(error, function_clause,
- [{?MODULE,'-inside-an-interpreted-fun-',As}|stacktrace()]).
+ [{?MODULE,'-inside-an-interpreted-fun-',As}|?STACKTRACE]).
%% expr_list(ExpressionList, Bindings)
@@ -894,13 +900,13 @@ if_clauses([{clause,_,[],G,B}|Cs], Bs, Lf, Ef, RBs) ->
false -> if_clauses(Cs, Bs, Lf, Ef, RBs)
end;
if_clauses([], _Bs, _Lf, _Ef, _RBs) ->
- erlang:raise(error, if_clause, stacktrace()).
+ erlang:raise(error, if_clause, ?STACKTRACE).
%% try_clauses(Body, CaseClauses, CatchClauses, AfterBody, Bindings,
%% LocalFuncHandler, ExtFuncHandler, RBs)
-%% When/if variable bindings between the different parts of a
-%% try-catch expression are introduced this will have to be rewritten.
+
try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs) ->
+ check_stacktrace_vars(Catches, Bs),
try exprs(B, Bs, Lf, Ef, none) of
{value,V,Bs1} when Cases =:= [] ->
ret_expr(V, Bs1, RBs);
@@ -909,23 +915,18 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs) ->
{B2,Bs2} ->
exprs(B2, Bs2, Lf, Ef, RBs);
nomatch ->
- erlang:raise(error, {try_clause,V}, stacktrace())
+ erlang:raise(error, {try_clause,V}, ?STACKTRACE)
end
catch
- Class:Reason when Catches =:= [] ->
- %% Rethrow
- erlang:raise(Class, Reason, stacktrace());
- Class:Reason ->
-%%% %% Set stacktrace
-%%% try erlang:raise(Class, Reason, stacktrace())
-%%% catch _:_ -> ok
-%%% end,
- V = {Class,Reason,erlang:get_stacktrace()},
- case match_clause(Catches, [V],Bs, Lf, Ef) of
+ Class:Reason:Stacktrace when Catches =:= [] ->
+ erlang:raise(Class, Reason, Stacktrace);
+ Class:Reason:Stacktrace ->
+ V = {Class,Reason,Stacktrace},
+ case match_clause(Catches, [V], Bs, Lf, Ef) of
{B2,Bs2} ->
exprs(B2, Bs2, Lf, Ef, RBs);
nomatch ->
- erlang:raise(Class, Reason, stacktrace())
+ erlang:raise(Class, Reason, Stacktrace)
end
after
if AB =:= [] ->
@@ -935,6 +936,23 @@ try_clauses(B, Cases, Catches, AB, Bs, Lf, Ef, RBs) ->
end
end.
+check_stacktrace_vars([{clause,_,[{tuple,_,[_,_,STV]}],_,_}|Cs], Bs) ->
+ case STV of
+ {var,_,V} ->
+ case binding(V, Bs) of
+ {value, _} ->
+ erlang:raise(error, stacktrace_bound, ?STACKTRACE);
+ unbound ->
+ check_stacktrace_vars(Cs, Bs)
+ end;
+ _ ->
+ erlang:raise(error,
+ {illegal_stacktrace_variable,STV},
+ ?STACKTRACE)
+ end;
+check_stacktrace_vars([], _Bs) ->
+ ok.
+
%% case_clauses(Value, Clauses, Bindings, LocalFuncHandler, ExtFuncHandler,
%% RBs)
@@ -943,7 +961,7 @@ case_clauses(Val, Cs, Bs, Lf, Ef, RBs) ->
{B, Bs1} ->
exprs(B, Bs1, Lf, Ef, RBs);
nomatch ->
- erlang:raise(error, {case_clause,Val}, stacktrace())
+ erlang:raise(error, {case_clause,Val}, ?STACKTRACE)
end.
%%
@@ -1018,7 +1036,7 @@ guard0([G|Gs], Bs0, Lf, Ef) ->
{value,false,_} -> false
end;
false ->
- erlang:raise(error, guard_expr, stacktrace())
+ erlang:raise(error, guard_expr, ?STACKTRACE)
end;
guard0([], _Bs, _Lf, _Ef) -> true.
@@ -1073,7 +1091,7 @@ match(Pat, Term, Bs) ->
match(Pat, Term, Bs, BBs) ->
case catch match1(Pat, Term, Bs, BBs) of
invalid ->
- erlang:raise(error, {illegal_pattern,Pat}, stacktrace());
+ erlang:raise(error, {illegal_pattern,Pat}, ?STACKTRACE);
Other ->
Other
end.
@@ -1254,7 +1272,7 @@ merge_bindings(Bs1, Bs2) ->
case orddict:find(Name, Bs) of
{ok,Val} -> Bs; %Already with SAME value
{ok,V1} ->
- erlang:raise(error, {badmatch,V1}, stacktrace());
+ erlang:raise(error, {badmatch,V1}, ?STACKTRACE);
error -> orddict:store(Name, Val, Bs)
end end,
Bs2, orddict:to_list(Bs1)).
@@ -1264,7 +1282,7 @@ merge_bindings(Bs1, Bs2) ->
%% fun (Name, Val, Bs) ->
%% case orddict:find(Name, Bs) of
%% {ok,Val} -> orddict:erase(Name, Bs);
-%% {ok,V1} -> erlang:raise(error,{badmatch,V1},stacktrace());
+%% {ok,V1} -> erlang:raise(error,{badmatch,V1},?STACKTRACE);
%% error -> Bs
%% end
%% end, Bs2, Bs1).
@@ -1326,7 +1344,3 @@ ret_expr(_Old, New) ->
New.
line(Expr) -> element(2, Expr).
-
-%% {?MODULE,expr,3} is still the stacktrace, despite the
-%% fact that expr() now takes two, three or four arguments...
-stacktrace() -> [{?MODULE,expr,3}].
diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl
index 76f0b38108..5ee584d612 100644
--- a/lib/stdlib/src/erl_tar.erl
+++ b/lib/stdlib/src/erl_tar.erl
@@ -189,7 +189,7 @@ table(Name) ->
%% Returns a list of names of the files in the tar file Name.
%% Options accepted: compressed, verbose, cooked.
-spec table(open_handle(), [compressed | verbose | cooked]) ->
- {ok, [tar_entry()]} | {error, term()}.
+ {ok, [string() | tar_entry()]} | {error, term()}.
table(Name, Opts) when is_list(Opts) ->
foldl_read(Name, fun table1/4, [], table_opts(Opts)).
diff --git a/lib/stdlib/src/eval_bits.erl b/lib/stdlib/src/eval_bits.erl
index 631faa3be5..bb86a65c72 100644
--- a/lib/stdlib/src/eval_bits.erl
+++ b/lib/stdlib/src/eval_bits.erl
@@ -2,7 +2,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -25,6 +25,9 @@
-export([expr_grp/3,expr_grp/5,match_bits/6,
match_bits/7,bin_gen/6]).
+-define(STACKTRACE,
+ element(2, erlang:process_info(self(), current_stacktrace))).
+
%% Types used in this module:
%% @type bindings(). An abstract structure for bindings between
%% variables and values (the environment)
@@ -93,9 +96,9 @@ eval_exp_field1(V, Size, Unit, Type, Endian, Sign) ->
eval_exp_field(V, Size, Unit, Type, Endian, Sign)
catch
error:system_limit ->
- error(system_limit);
+ erlang:raise(error, system_limit, ?STACKTRACE);
error:_ ->
- error(badarg)
+ erlang:raise(error, badarg, ?STACKTRACE)
end.
eval_exp_field(Val, Size, Unit, integer, little, signed) ->
@@ -131,7 +134,7 @@ eval_exp_field(Val, all, Unit, binary, _, _) ->
Size when Size rem Unit =:= 0 ->
<<Val:Size/binary-unit:1>>;
_ ->
- error(badarg)
+ erlang:raise(error, badarg, ?STACKTRACE)
end;
eval_exp_field(Val, Size, Unit, binary, _, _) ->
<<Val:(Size*Unit)/binary-unit:1>>.
@@ -377,12 +380,12 @@ make_bit_type(Line, default, Type0) ->
{ok,all,Bt} -> {{atom,Line,all},erl_bits:as_list(Bt)};
{ok,undefined,Bt} -> {{atom,Line,undefined},erl_bits:as_list(Bt)};
{ok,Size,Bt} -> {{integer,Line,Size},erl_bits:as_list(Bt)};
- {error,Reason} -> error(Reason)
+ {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
end;
make_bit_type(_Line, Size, Type0) -> %Size evaluates to an integer or 'all'
case erl_bits:set_bit_type(Size, Type0) of
{ok,Size,Bt} -> {Size,erl_bits:as_list(Bt)};
- {error,Reason} -> error(Reason)
+ {error,Reason} -> erlang:raise(error, Reason, ?STACKTRACE)
end.
match_check_size(Mfun, Size, Bs) ->
@@ -405,9 +408,3 @@ match_check_size(_, {value,_,_}, _Bs, _AllowAll) ->
ok; %From the debugger.
match_check_size(_, _, _Bs, _AllowAll) ->
throw(invalid).
-
-%% error(Reason) -> exception thrown
-%% Throw a nice-looking exception, similar to exceptions from erl_eval.
-error(Reason) ->
- erlang:raise(error, Reason, [{erl_eval,expr,3}]).
-
diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl
index c3ef4eb051..8eb85cab8e 100644
--- a/lib/stdlib/test/erl_eval_SUITE.erl
+++ b/lib/stdlib/test/erl_eval_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
+%% Copyright Ericsson AB 1998-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
otp_8133/1,
otp_10622/1,
otp_13228/1,
+ otp_14826/1,
funs/1,
try_catch/1,
eval_expr_5/1,
@@ -57,6 +58,7 @@
-export([count_down/2, count_down_fun/0, do_apply/2,
local_func/3, local_func_value/2]).
+-export([simple/0]).
-ifdef(STANDALONE).
-define(config(A,B),config(A,B)).
@@ -83,7 +85,7 @@ all() ->
pattern_expr, match_bin, guard_3, guard_4, guard_5, lc,
simple_cases, unary_plus, apply_atom, otp_5269,
otp_6539, otp_6543, otp_6787, otp_6977, otp_7550,
- otp_8133, otp_10622, otp_13228,
+ otp_8133, otp_10622, otp_13228, otp_14826,
funs, try_catch, eval_expr_5, zero_width,
eep37, eep43].
@@ -988,6 +990,173 @@ otp_13228(_Config) ->
EFH = {value, fun({io, fwrite}, [atom]) -> io_fwrite end},
{value, worked, []} = parse_and_run("foo(io:fwrite(atom)).", LFH, EFH).
+%% OTP-14826: more accurate stacktrace.
+otp_14826(_Config) ->
+ backtrace_check("fun(P) when is_pid(P) -> true end(a).",
+ function_clause,
+ [{erl_eval,'-inside-an-interpreted-fun-',[a],[]},
+ {erl_eval,eval_fun,6},
+ ?MODULE]),
+ backtrace_check("B.",
+ {unbound_var, 'B'},
+ [{erl_eval,expr,2}, ?MODULE]),
+ backtrace_check("B.",
+ {unbound, 'B'},
+ [{erl_eval,expr,5}, ?MODULE],
+ none, none),
+ backtrace_check("1/0.",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ backtrace_catch("catch 1/0.",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ check(fun() -> catch exit(foo) end,
+ "catch exit(foo).",
+ {'EXIT', foo}),
+ check(fun() -> catch throw(foo) end,
+ "catch throw(foo).",
+ foo),
+ backtrace_check("try 1/0 after foo end.",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ backtrace_catch("catch (try 1/0 after foo end).",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ backtrace_catch("try catch 1/0 after foo end.",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ backtrace_check("try a of b -> bar after foo end.",
+ {try_clause,a},
+ [{erl_eval,try_clauses,8}]),
+ check(fun() -> X = try foo:bar() catch A:B:C -> {A,B} end, X end,
+ "try foo:bar() catch A:B:C -> {A,B} end.",
+ {error, undef}),
+ backtrace_check("C = 4, try foo:bar() catch A:B:C -> {A,B,C} end.",
+ stacktrace_bound,
+ [{erl_eval,check_stacktrace_vars,2},
+ {erl_eval,try_clauses,8}],
+ none, none),
+ backtrace_catch("catch (try a of b -> bar after foo end).",
+ {try_clause,a},
+ [{erl_eval,try_clauses,8}]),
+ backtrace_check("try 1/0 catch exit:a -> foo end.",
+ badarith,
+ [{erlang,'/',[1,0],[]},
+ {erl_eval,do_apply,6}]),
+ Es = [{'try',1,[{call,1,{remote,1,{atom,1,foo},{atom,1,bar}},[]}],
+ [],
+ [{clause,1,[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}],
+ [],[{tuple,1,[{var,1,'A'},{var,1,'B'},{atom,1,'C'}]}]}],[]}],
+ try
+ erl_eval:exprs(Es, [], none, none),
+ ct:fail(stacktrace_variable)
+ catch
+ error:{illegal_stacktrace_variable,{atom,1,'C'}}:S ->
+ [{erl_eval,check_stacktrace_vars,2,_},
+ {erl_eval,try_clauses,8,_}|_] = S
+ end,
+ backtrace_check("{1,1} = {A = 1, A = 2}.",
+ {badmatch, 1},
+ [erl_eval, {lists,foldl,3}]),
+ backtrace_check("case a of a when foo:bar() -> x end.",
+ guard_expr,
+ [{erl_eval,guard0,4}], none, none),
+ backtrace_check("case a of foo() -> ok end.",
+ {illegal_pattern,{call,1,{atom,1,foo},[]}},
+ [{erl_eval,match,4}], none, none),
+ backtrace_check("case a of b -> ok end.",
+ {case_clause,a},
+ [{erl_eval,case_clauses,6}, ?MODULE]),
+ backtrace_check("if a =:= b -> ok end.",
+ if_clause,
+ [{erl_eval,if_clauses,5}, ?MODULE]),
+ backtrace_check("fun A(b) -> ok end(a).",
+ function_clause,
+ [{erl_eval,'-inside-an-interpreted-fun-',[a],[]},
+ {erl_eval,eval_named_fun,8},
+ ?MODULE]),
+ backtrace_check("[A || A <- a].",
+ {bad_generator, a},
+ [{erl_eval,eval_generate,7}, {erl_eval, eval_lc, 6}]),
+ backtrace_check("<< <<A>> || <<A>> <= a>>.",
+ {bad_generator, a},
+ [{erl_eval,eval_b_generate,7}, {erl_eval, eval_bc, 6}]),
+ backtrace_check("[A || A <- [1], begin a end].",
+ {bad_filter, a},
+ [{erl_eval,eval_filter,6}, {erl_eval, eval_generate, 7}]),
+ fun() ->
+ {'EXIT', {{badarity, {_Fun, []}}, BT}} =
+ (catch parse_and_run("fun(A) -> A end().")),
+ check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT)
+ end(),
+ fun() ->
+ {'EXIT', {{badarity, {_Fun, []}}, BT}} =
+ (catch parse_and_run("fun F(A) -> A end().")),
+ check_backtrace([{erl_eval,do_apply,5}, ?MODULE], BT)
+ end(),
+ backtrace_check("foo().",
+ undef,
+ [{erl_eval,foo,0},{erl_eval,local_func,6}],
+ none, none),
+ backtrace_check("a orelse false.",
+ {badarg, a},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("a andalso false.",
+ {badarg, a},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("t = u.",
+ {badmatch, u},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("{math,sqrt}(2).",
+ {badfun, {math,sqrt}},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("erl_eval_SUITE:simple().",
+ simple,
+ [{?MODULE,simple1,0},{?MODULE,simple,0},erl_eval]),
+ Args = [{integer,1,I} || I <- lists:seq(1, 30)],
+ backtrace_check("fun(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,"
+ "19,20,21,22,23,24,25,26,27,28,29,30) -> a end.",
+ {argument_limit,
+ {'fun',1,[{clause,1,Args,[],[{atom,1,a}]}]}},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("fun F(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,"
+ "19,20,21,22,23,24,25,26,27,28,29,30) -> a end.",
+ {argument_limit,
+ {named_fun,1,'F',[{clause,1,Args,[],[{atom,1,a}]}]}},
+ [{erl_eval,expr,5}, ?MODULE]),
+ backtrace_check("#r{}.",
+ {undef_record,r},
+ [{erl_eval,expr,5}, ?MODULE],
+ none, none),
+ %% eval_bits
+ backtrace_check("<<100:8/bitstring>>.",
+ badarg,
+ [{eval_bits,eval_exp_field1,6},
+ eval_bits,eval_bits,erl_eval]),
+ backtrace_check("<<100:8/foo>>.",
+ {undefined_bittype,foo},
+ [{eval_bits,make_bit_type,3},eval_bits,
+ eval_bits,erl_eval],
+ none, none),
+ backtrace_check("B = <<\"foo\">>, <<B/binary-unit:7>>.",
+ badarg,
+ [{eval_bits,eval_exp_field1,6},
+ eval_bits,eval_bits,erl_eval],
+ none, none),
+ ok.
+
+simple() ->
+ A = simple1(),
+ {A}.
+
+simple1() ->
+ erlang:error(simple).
+
%% Simple cases, just to cover some code.
funs(Config) when is_list(Config) ->
do_funs(none, none),
@@ -1488,6 +1657,43 @@ error_check(String, Result, LFH, EFH) ->
ct:fail({eval, Other, Result})
end.
+backtrace_check(String, Result, Backtrace) ->
+ case catch parse_and_run(String) of
+ {'EXIT', {Result, BT}} ->
+ check_backtrace(Backtrace, BT);
+ Other ->
+ ct:fail({eval, Other, Result})
+ end.
+
+backtrace_check(String, Result, Backtrace, LFH, EFH) ->
+ case catch parse_and_run(String, LFH, EFH) of
+ {'EXIT', {Result, BT}} ->
+ check_backtrace(Backtrace, BT);
+ Other ->
+ ct:fail({eval, Other, Result})
+ end.
+
+backtrace_catch(String, Result, Backtrace) ->
+ case parse_and_run(String) of
+ {value, {'EXIT', {Result, BT}}, _Bindings} ->
+ check_backtrace(Backtrace, BT);
+ Other ->
+ ct:fail({eval, Other, Result})
+ end.
+
+check_backtrace([B1|Backtrace], [B2|BT]) ->
+ case {B1, B2} of
+ {M, {M,_,_,_}} ->
+ ok;
+ {{M,F,A}, {M,F,A,_}} ->
+ ok;
+ {B, B} ->
+ ok
+ end,
+ check_backtrace(Backtrace, BT);
+check_backtrace([], _) ->
+ ok.
+
eval_string(String) ->
{value, Result, _} = parse_and_run(String),
Result.
@@ -1499,8 +1705,8 @@ parse_and_run(String) ->
parse_and_run(String, LFH, EFH) ->
{ok,Tokens,_} = erl_scan:string(String),
- {ok, [Expr]} = erl_parse:parse_exprs(Tokens),
- erl_eval:expr(Expr, [], LFH, EFH).
+ {ok, Exprs} = erl_parse:parse_exprs(Tokens),
+ erl_eval:exprs(Exprs, [], LFH, EFH).
no_final_dot(S) ->
case lists:reverse(S) of
diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl
index f03f326278..60a15c8e3f 100644
--- a/lib/syntax_tools/src/erl_prettypr.erl
+++ b/lib/syntax_tools/src/erl_prettypr.erl
@@ -780,8 +780,7 @@ lay_2(Node, Ctxt) ->
'_' ->
beside(D1, beside(text(":"), D2));
_ ->
- D3 = lay(erl_syntax:class_qualifier_stacktrace(Node),
- Ctxt1),
+ D3 = lay(Stacktrace, Ctxt1),
beside(D1, beside(beside(text(":"), D2),
beside(text(":"), D3)))
end;
diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl
index ed552b73df..b816c0699c 100644
--- a/lib/syntax_tools/src/erl_syntax.erl
+++ b/lib/syntax_tools/src/erl_syntax.erl
@@ -3895,7 +3895,9 @@ fold_try_clause({clause, Pos, [P], Guard, Body}) ->
unfold_try_clauses(Cs) ->
[unfold_try_clause(C) || C <- Cs].
-unfold_try_clause({clause, Pos, [{tuple, _, [{atom, _, throw}, V, _]}],
+unfold_try_clause({clause, Pos, [{tuple, _, [{atom, _, throw},
+ V,
+ [{var, _, '_'}]]}],
Guard, Body}) ->
{clause, Pos, [V], Guard, Body};
unfold_try_clause({clause, Pos, [{tuple, _, [C, V, Stacktrace]}],
@@ -7761,8 +7763,9 @@ subtrees(T) ->
catch_expr ->
[[catch_expr_body(T)]];
class_qualifier ->
- [[class_qualifier_argument(T)],
- [class_qualifier_body(T)]];
+ [[class_qualifier_argument(T)],
+ [class_qualifier_body(T)],
+ [class_qualifier_stacktrace(T)]];
clause ->
case clause_guard(T) of
none ->
@@ -7983,6 +7986,7 @@ make_tree(block_expr, [B]) -> block_expr(B);
make_tree(case_expr, [[A], C]) -> case_expr(A, C);
make_tree(catch_expr, [[B]]) -> catch_expr(B);
make_tree(class_qualifier, [[A], [B]]) -> class_qualifier(A, B);
+make_tree(class_qualifier, [[A], [B], [C]]) -> class_qualifier(A, B, C);
make_tree(clause, [P, B]) -> clause(P, none, B);
make_tree(clause, [P, [G], B]) -> clause(P, G, B);
make_tree(cond_expr, [C]) -> cond_expr(C);