aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2015-07-07 13:34:57 +0200
committerAnders Svensson <[email protected]>2015-08-04 17:40:24 +0200
commit9ab5d8afedc6d2f56997276e3f016ec1c6dac6a5 (patch)
treed9a84cd1e37a257ccade39040f008737cd49ce01
parent16aaa29b7ce40596520d563b6f4a8e0aeba7b085 (diff)
downloadotp-9ab5d8afedc6d2f56997276e3f016ec1c6dac6a5.tar.gz
otp-9ab5d8afedc6d2f56997276e3f016ec1c6dac6a5.tar.bz2
otp-9ab5d8afedc6d2f56997276e3f016ec1c6dac6a5.zip
Don't compute AVP list length unnecessarily at AVP decode
This has had a hugely negative impact on performance when decoding messages containing many AVP: each decode of an AVP having variable arity computed the length of the list of previously decoded AVPs when checking that the allowed arity was not exceeded, even if the allowed arity was infinite, making for O(n^2) cost. Here are some execution times, for diameter_codec:decode/2 on a representative message with n integer AVPs in the Common application (on the host at hand): Before After ------- --------- n = 1K 5 ms 2 ms n = 10K 500 ms 25 ms n = 100K 75 sec 225 ms n = 1M 2.6 sec Note the nearly linear increase following the change. Remove the dire documentation warning for incoming_maxlen as a consequence. It can still be useful to set, but not doing so won't have the same consequences as previously.
-rw-r--r--lib/diameter/doc/src/diameter.xml8
-rw-r--r--lib/diameter/include/diameter_gen.hrl32
2 files changed, 22 insertions, 18 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml
index cb628f9529..854bc5b432 100644
--- a/lib/diameter/doc/src/diameter.xml
+++ b/lib/diameter/doc/src/diameter.xml
@@ -794,14 +794,6 @@ Messages larger than the specified number of bytes are discarded.</p>
Defaults to <c>16777215</c>, the maximum value of the 24-bit Message
Length field in a Diameter Header.</p>
-<warning>
-<p>
-This option should be set to as low a value as is sufficient for the
-Diameter applications and peers in question, since decoding incoming
-messages from a malicious peer can otherwise generate significant
-load.</p>
-</warning>
-
</item>
<tag><c>{restrict_connections, false
diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl
index 79b8e6ecde..ebe9e6363d 100644
--- a/lib/diameter/include/diameter_gen.hrl
+++ b/lib/diameter/include/diameter_gen.hrl
@@ -210,18 +210,27 @@ missing(Rec, Name, Failed) ->
sets:new(),
Failed),
[{5005, A} || F <- '#info-'(element(1, Rec), fields),
- not have_arity(avp_arity(Name, F), '#get-'(F, Rec)),
+ not has_arity(avp_arity(Name, F), '#get-'(F, Rec)),
#diameter_avp{code = C, vendor_id = V}
= A <- [empty_avp(F)],
not sets:is_element({C,V}, Avps)].
%% Maximum arities have already been checked in building the record.
-have_arity({Min, _}, L) ->
- Min =< length(L);
-have_arity(N, V) ->
+has_arity({Min, _}, L) ->
+ has_prefix(Min, L);
+has_arity(N, V) ->
N /= 1 orelse V /= undefined.
+%% Compare a non-negative integer and the length of a list without
+%% computing the length.
+has_prefix(0, _) ->
+ true;
+has_prefix(_, []) ->
+ false;
+has_prefix(N, L) ->
+ has_prefix(N-1, tl(L)).
+
%% empty_avp/1
empty_avp(Name) ->
@@ -589,14 +598,17 @@ pack(undefined, 1, FieldName, Avp, Acc) ->
%% AVP MUST be included and contain a copy of the first instance of
%% the offending AVP that exceeded the maximum number of occurrences
%%
+
pack(_, 1, _, Avp, {Rec, Failed}) ->
{Rec, [{5009, Avp} | Failed]};
-pack(L, {_, Max}, _, Avp, {Rec, Failed})
- when length(L) == Max ->
- {Rec, [{5009, Avp} | Failed]};
-
-pack(L, _, FieldName, Avp, Acc) ->
- p(FieldName, fun(V) -> [V|L] end, Avp, Acc).
+pack(L, {_, Max}, FieldName, Avp, Acc) ->
+ case '*' /= Max andalso has_prefix(Max, L) of
+ true ->
+ {Rec, Failed} = Acc,
+ {Rec, [{5009, Avp} | Failed]};
+ false ->
+ p(FieldName, fun(V) -> [V|L] end, Avp, Acc)
+ end.
%% p/4