aboutsummaryrefslogblamecommitdiffstats
path: root/lib/dialyzer/test/small_SUITE_data/src/tuple_set_crash.erl
blob: 5503f39412947ce9d63ab9687eb772e3ce8cecef (plain) (tree)














































































































































































































                                                                                         
%% ====================================================================
%% Program which resulted in an erl_types crash due to incomplete
%% handling of tuple_sets in function inf_tuples_in_sets/4.
%% Reported by Alexey Romanov on 10/10/2010 and fixed 16/10/2010.
%% Stavros Aronis provided a better fix of the issue on 8/11/2010.
%% ====================================================================

-module(tuple_set_crash).
-export([test/5]).

%% ====================================================================

-define(PREPEND_IF_BIT_SET(BitMap, Bit,
			   PatternInBinary, PatternInList,
			   OldRestVar, NewRestVar,
			   OldAccVar, NewAccVar),
	case byteset:contains(Bit, BitMap) of
	    true ->
		<<PatternInBinary, NewRestVar/binary>> = OldRestVar,
		NewAccVar = [PatternInList | OldAccVar];
	    false ->
		NewRestVar = OldRestVar,
		NewAccVar = OldAccVar
	end).

%% ====================================================================

%% Types used in parsing binaries
-define(BITMAP1, 8/integer-big-unsigned).
-define(BYTE, 8/integer-little-unsigned).
-define(WORD, 16/integer-little-unsigned).
-define(DWORD, 32/integer-little-unsigned).
-define(DATE, 16/integer-little-signed).
-define(TIME, 32/float-little-unsigned).
-define(TINY_STRING_M(Var, Size), Size:?BYTE, Var:Size/binary).
-define(SMALL_STRING_M(Var, Size), Size:?WORD, Var:Size/binary).

-type config_change() ::
	{device_properties |
	 video_target |
	 audio_target |
	 video_device |
	 audio_device |
	 video_output |
	 audio_output, [{atom(), any()}]}.

-type message_from_server() ::
	 ok |
	 {error, atom()} |
	 config_change() |
	 {media_item_url_reply, integer(), binary()}.

%% ====================================================================

-spec test(integer(), [integer()], binary(), binary(), binary()) -> {binary(), binary()}.
test(_TargetId, [], _Key, IVT, IVF) ->
    {IVT, IVF};
test(TargetId, [Date | DateTail], Key, IVT, IVF) ->
    PlayListRequest = play_list_request(TargetId, Date),
    {ok, Reply, IVT1, IVF1} = culprit(PlayListRequest, Key, IVT, IVF),
    case Reply of
	{play_list, _Playlist} ->
	    test(TargetId, DateTail, Key, IVT1, IVF1);
	{error, 16#11} ->
	    {IVT1, IVF1} %% we can finish early
    end.

-spec culprit(binary(), binary(), binary(), binary()) ->
	{ok, message_from_server(), binary(), binary()}.
culprit(Message, Key, IVecToServer, IVecFromServer) ->
    {Packet, NewIVecToServer} = message_to_packet(Message, Key, IVecToServer),
    Message = crypto:aes_cbc_128_decrypt(Key, IVecFromServer, Packet),
    NewIVecFromServer = crypto:aes_cbc_ivec(Packet),
    ParsedMessage = parse_message(Message),
    {ok, ParsedMessage, NewIVecToServer, NewIVecFromServer}.

%% ====================================================================

-spec play_list_request(integer(), integer()) -> binary().
play_list_request(TargetId, Date) ->
    <<16#06:?WORD, TargetId:?DWORD, Date:?DATE>>.

-spec parse_message(binary()) -> message_from_server().
parse_message(<<MessageID:?WORD, Rest/binary>>) ->
    case MessageID of
	16#00 -> parse_error_code(Rest);
	16#22 -> {device_properties, parse_device_properties(Rest)};
	16#24 -> {video_target_info, parse_video_target_info(Rest)};
	16#25 -> {audio_target_info, parse_audio_target_info(Rest)};
	16#26 -> {video_device_info, parse_av_device_info(Rest)};
	16#27 -> {audio_device_info, parse_av_device_info(Rest)};
	16#28 -> {video_output_info, parse_video_output_info(Rest)};
	16#29 -> {audio_output_info, parse_audio_output_info(Rest)}
    end.

-spec parse_error_code(binary()) -> ok | {error, integer()}.
parse_error_code(<<ErrorCode:?BYTE, _Padding/binary>>) ->
    case ErrorCode of
	0 -> ok;
	_ -> {error, ErrorCode}
    end.

-spec parse_device_properties(binary()) -> config_change().
parse_device_properties(<<BitMap:?BITMAP1, Rest/binary>>) ->
    Acc0 = [],
    ?PREPEND_IF_BIT_SET(BitMap, 0,
			FwVersion:3/binary, {fw_version, FwVersion},
			Rest, Rest1, Acc0, Acc1),
    ?PREPEND_IF_BIT_SET(BitMap, 1,
			?TINY_STRING_M(ControllerName, _S1),
			{controller_name, ControllerName},
			Rest1, Rest2, Acc1, Acc2),
    ?PREPEND_IF_BIT_SET(BitMap, 2,
			?SMALL_STRING_M(ControllerDescription, _S2),
			{controller_description, ControllerDescription},
			Rest2, Rest3, Acc2, Acc3),
    ?PREPEND_IF_BIT_SET(BitMap, 3,
			ControllerStatus:?BYTE,
			{controller_status, ControllerStatus},
			Rest3, _Padding, Acc3, Acc4),
    Acc4.

-spec parse_video_target_info(binary()) -> config_change().
parse_video_target_info(<<TargetId:?DWORD, Status:?BYTE, _Padding/binary>>) ->
    [{target_id, TargetId}, {status, Status}].

-spec parse_audio_target_info(binary()) -> [config_change()].
parse_audio_target_info(<<TargetId:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
    Acc0 = [{target_id, TargetId}],
    ?PREPEND_IF_BIT_SET(BitMap, 0,
			Status:?BYTE, {status, Status},
			Rest, Rest1, Acc0, Acc1),
    ?PREPEND_IF_BIT_SET(BitMap, 1,
			MasterVolume:?WORD, {master_volume, MasterVolume},
			Rest1, _Padding, Acc1, Acc2),
    Acc2.

-spec parse_av_device_info(binary()) -> [config_change()].
parse_av_device_info(<<DeviceId:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
    Acc0 = [{device_id, DeviceId}],
    ?PREPEND_IF_BIT_SET(BitMap, 0,
			TargetId:?DWORD, {target_id, TargetId},
			Rest, Rest1, Acc0, Acc1),
    ?PREPEND_IF_BIT_SET(BitMap, 1,
			?TINY_STRING_M(Model, _S1), {model, Model},
			Rest1, Rest2, Acc1, Acc2),
    ?PREPEND_IF_BIT_SET(BitMap, 2,
			Address:?BYTE, {address, Address},
			Rest2, Rest3, Acc2, Acc3),
    ?PREPEND_IF_BIT_SET(BitMap, 3,
			Status:?BYTE, {status, Status},
			Rest3, _Padding, Acc3, Acc4),
    Acc4.

-spec parse_video_output_info(binary()) -> [config_change()].
parse_video_output_info(<<Output:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
    Acc0 = [{output_id, Output}],
    ?PREPEND_IF_BIT_SET(BitMap, 0,
			DeviceId:?DWORD, {device_id, DeviceId},
			Rest, Rest1, Acc0, Acc1),
    ?PREPEND_IF_BIT_SET(BitMap, 1,
			?TINY_STRING_M(DisplayType, _S1),
			{display_type, DisplayType},
			Rest1, Rest2, Acc1, Acc2),
    ?PREPEND_IF_BIT_SET(BitMap, 2,
			AudioVolume:?WORD,
			{audio_volume, AudioVolume},
			Rest2, _Padding, Acc2, Acc3),
    Acc3.

-spec parse_audio_output_info(binary()) -> [config_change()].
parse_audio_output_info(<<Output:?DWORD, BitMap:?BITMAP1, Rest/binary>>) ->
    Acc0 = [{output_id, Output}],
    ?PREPEND_IF_BIT_SET(BitMap, 0,
			DeviceId:?DWORD, {device_id, DeviceId},
			Rest, Rest1, Acc0, Acc1),
    ?PREPEND_IF_BIT_SET(BitMap, 1,
			AudioVolume:?WORD, {audio_volume, AudioVolume},
			Rest1, Rest2, Acc1, Acc2),
    ?PREPEND_IF_BIT_SET(BitMap, 2,
			Delay:?WORD, {delay, Delay},
			Rest2, _Padding, Acc2, Acc3),
    Acc3.

-spec message_to_packet(binary(), binary(), binary()) -> {binary(), binary()}.
message_to_packet(Message, Key, IVec) ->
    PaddedMessage = pad_pkcs5(Message),
    Packet = crypto:aes_cbc_128_encrypt(Key, IVec, PaddedMessage),
    TotalSize = byte_size(Packet),
    NewIVec = crypto:aes_cbc_ivec(Packet),
    {<<TotalSize:?WORD, Packet/binary>>, NewIVec}.

-spec pad_pkcs5(binary()) -> binary().
pad_pkcs5(Message) ->
    Size = byte_size(Message),
    PaddingSize = case Size rem 16 of
		      0 -> 0;
		      Rem -> 16 - Rem
		  end,
    pad_pkcs5(Message, PaddingSize, PaddingSize).

-spec pad_pkcs5(binary(), integer(), integer()) -> binary().
pad_pkcs5(Message, _PaddingSize, 0) ->
    Message;
pad_pkcs5(Message, PaddingSize, PaddingSizeRemaining) ->
    pad_pkcs5(<<Message/binary, PaddingSize:?BYTE>>,
	      PaddingSize, PaddingSizeRemaining - 1).