From f51fa7f2a67cfdcbf98edaa79db32c54e5ee4af0 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Sun, 4 Dec 2011 12:06:30 +0100 Subject: Add range checks on dictionary integers Check that values that should be Unsigned32 actually are. --- lib/diameter/src/compiler/diameter_dict_util.erl | 87 +++++++++++++++++++----- 1 file changed, 69 insertions(+), 18 deletions(-) (limited to 'lib/diameter/src/compiler') diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl index 201b153f0f..09aab4eff4 100644 --- a/lib/diameter/src/compiler/diameter_dict_util.erl +++ b/lib/diameter/src/compiler/diameter_dict_util.erl @@ -37,6 +37,7 @@ -define(A, list_to_atom). -define(L, atom_to_list). -define(I, integer_to_list). +-define(F, io_lib:format). %% =========================================================================== %% parse/2 @@ -76,15 +77,37 @@ format_error(T) -> {Fmt, As} = fmt(T), lists:flatten(io_lib:format(Fmt, As)). -fmt({avp_code_already_defined, [Code, false, Name, Line, L]}) -> - {"AVP ~p (~s) at line ~p already defined at line ~p", - [Code, Name, Line, L]}; +fmt({avp_code_already_defined = E, [Code, false, Name, Line, L]}) -> + {fmt(E), [Code, "", Name, Line, L]}; +fmt({avp_code_already_defined = E, [Code, Vid, Name, Line, L]}) -> + {fmt(E), [Code, ?F("/~p", [Vid]), Name, Line, L]}; + +fmt({uint32_out_of_range = E, [id | T]}) -> + {fmt(E), ["@id", "application identifier" | T]}; +fmt({uint32_out_of_range = E, [K | T]}) + when K == vendor; + K == avp_vendor_id -> + {fmt(E), [?F("@~p", [K]), "vendor id" | T]}; +fmt({uint32_out_of_range = E, [K, Name | T]}) + when K == enum; + K == define -> + {fmt(E), [?F("@~p ~s", [K, Name]), "value" | T]}; +fmt({uint32_out_of_range = E, [avp_types, Name | T]}) -> + {fmt(E), ["AVP " ++ Name, "AVP code" | T]}; +fmt({uint32_out_of_range = E, [grouped, Name | T]}) -> + {fmt(E), ["Grouped AVP " ++ Name | T]}; +fmt({uint32_out_of_range = E, [messages, Name | T]}) -> + {fmt(E), ["Message " ++ Name, "command code" | T]}; fmt({Reason, As}) -> {fmt(Reason), As}; fmt(avp_code_already_defined) -> - "AVP ~p/~p (~s) at line ~p already defined at line ~p"; + "AVP ~p~s (~s) at line ~p already defined at line ~p"; + +fmt(uint32_out_of_range) -> + "~s specifies ~s ~p at line ~p that is out of range for a value of " + "Diameter type Unsigned32"; fmt(imported_avp_already_defined) -> "AVP ~s imported by @inherits ~p at line ~p defined at line ~p"; @@ -580,7 +603,9 @@ pass1(Dict) -> foldl(fun(K,D) -> foldl([fun p1/3, K], D, find(K,D)) end, Dict, - [avp_types, %% must precede inherits, grouped, enum + [id, + vendor, + avp_types, %% must precede inherits, grouped, enum avp_vendor_id, custom_types, codecs, @@ -593,6 +618,14 @@ pass1(Dict) -> %% Multiple sections are allowed as long as their bodies don't %% overlap. (Except enum/define.) +p1([_Line, N], Dict, id = K) -> + true = is_uint32(N, [K]), + Dict; + +p1([_Line, Id, _Name], Dict, vendor = K) -> + true = is_uint32(Id, [K]), + Dict; + p1([_Line, X | Body], Dict, K) when K == avp_vendor_id; K == custom_types; @@ -636,7 +669,8 @@ no_messages_without_id(Dict) -> %% {avp_vendor_id, AvpName} -> [Lineno, Id::integer()] %% {custom_types|codecs|inherits, AvpName} -> [Lineno, Mod::string()] -explode({_, Line, AvpName}, Dict, {_, _, X}, K) -> +explode({_, Line, AvpName}, Dict, {_, _, X} = T, K) -> + true = K /= avp_vendor_id orelse is_uint32(T, [K]), true = K /= inherits orelse avp_not_local(AvpName, Line, Dict), store_new({key(K), AvpName}, @@ -649,11 +683,13 @@ explode({_, Line, AvpName}, Dict, {_, _, X}, K) -> %% {define, {Name, Key}} -> [Lineno, Value::integer(), enum|define] -explode2([{_, Line, Key}, {_, _, Value}], Dict, {_, _, X}, K) -> - store_new({key(K), {X, Key}}, +explode2([{_, Line, Key}, {_, _, Value} = T], Dict, {_, _, Name}, K) -> + true = is_uint32(T, [K, Name]), + + store_new({key(K), {Name, Key}}, [Line, Value, K], Dict, - [X, Key, K, Line], + [Name, Key, K, Line], key_already_defined). %% key/1 @@ -683,17 +719,18 @@ key(K) -> explode([{_, Line, Name} | Toks], Dict0, avp_types = K) -> %% Each AVP can be defined only once. - Dict1 = store_new({K, Name}, - [Line | Toks], - Dict0, - [Name, Line], - avp_name_already_defined), + Dict = store_new({K, Name}, + [Line | Toks], + Dict0, + [Name, Line], + avp_name_already_defined), - [{number, _, _Code}, {word, _, Type}, {word, _, _Flags}] = Toks, + [{number, _, _Code} = C, {word, _, Type}, {word, _, _Flags}] = Toks, true = avp_type_known(Type, Name, Line), + true = is_uint32(C, [K, Name]), - Dict1; + Dict; %% {grouped, Name} -> [Line, HeaderTok | AvpToks] %% {grouped, {Name, AvpName}} -> [Line, Qual, Delim] @@ -711,10 +748,12 @@ explode([{_, Line, Name}, Header | Avps], Dict0, grouped = K) -> [Name, Line], group_already_defined), - [{_,_, Code}, Vid] = Header, + [{_,_, Code} = C, Vid] = Header, {DefLine, {_, _, Flags}} = grouped_flags(Name, Code, Dict0, Line), V = lists:member($V, Flags), + true = is_uint32(C, [K, Name, "AVP code"]), + true = is_uint32(Vid, [K, Name, "vendor id"]), false = vendor_id_mismatch(Vid, V, Name, Dict0, Line, DefLine), explode_avps(Avps, Dict, K, Name); @@ -756,7 +795,11 @@ explode([{_, Line, MsgName} = M, Header | Avps], [MsgName, Line], message_name_already_defined), - [{_, _, Code}, Bits, ApplId] = Header, + [{_, _, Code} = C, Bits, ApplId] = Header, + + %% Don't check any application id since it's required to be + %% the same as @id. + true = is_uint32(C, [K, MsgName]), %% An application id specified as part of the message definition %% has to agree with @id. The former is parsed just because RFC @@ -830,6 +873,14 @@ close($<) -> $>; close(${) -> $}; close($[) -> $]. +%% is_uint32/2 + +is_uint32(false, _) -> + true; +is_uint32({Line, _, N}, Args) -> + N < 1 bsl 32 orelse ?RETURN(uint32_out_of_range, Args ++ [N, Line]). +%% Can't call diameter_types here since it may not exist yet. + %% application_id_mismatch/3 application_id_mismatch({number, Line, Id}, Dict, MsgName) -> -- cgit v1.2.3