diff options
Diffstat (limited to 'lib')
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,[<0.26.0>,"~s~n",[[1024]]]}, -   ...</pre> +** exception error: bad argument +     in function  io:format/3 +        called as io:format(<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,[<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(<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);  | 
