aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2017-11-22 23:10:50 +0100
committerLoïc Hoguin <[email protected]>2017-11-22 23:10:50 +0100
commite48f1cb34f5cc16844577ae55a9ebf0b7d08818a (patch)
treea7319d2475bb533e3565982b3cd733467f4929c4
parent757898658df849192ba4d21e11dd470daabeb44c (diff)
downloadcowlib-e48f1cb34f5cc16844577ae55a9ebf0b7d08818a.tar.gz
cowlib-e48f1cb34f5cc16844577ae55a9ebf0b7d08818a.tar.bz2
cowlib-e48f1cb34f5cc16844577ae55a9ebf0b7d08818a.zip
Crash on more error cases when parsing chnuked data
-rw-r--r--src/cow_http_te.erl22
1 files changed, 15 insertions, 7 deletions
diff --git a/src/cow_http_te.erl b/src/cow_http_te.erl
index b6290b7..9ef7a76 100644
--- a/src/cow_http_te.erl
+++ b/src/cow_http_te.erl
@@ -34,6 +34,8 @@
| {done, Data::binary(), TotalLen::non_neg_integer(), Rest::binary()}.
-export_type([decode_ret/0]).
+-include("cow_parse.hrl").
+
-ifdef(TEST).
dripfeed(<< C, Rest/bits >>, Acc, State, F) ->
case F(<< Acc/binary, C >>, State) of
@@ -164,8 +166,9 @@ stream_chunked(Data, {Rem, Streamed}, Acc) when Rem > 2 ->
stream_chunked(Rest, {0, Streamed + RemSize}, << Acc/binary, Chunk/binary >>);
<< Chunk:RemSize/binary, "\r" >> ->
{more, << Acc/binary, Chunk/binary >>, {1, Streamed + RemSize}};
- %% Everything in Data is part of the chunk.
- _ ->
+ %% Everything in Data is part of the chunk. If we have more
+ %% data than the chunk accepts, then this is an error and we crash.
+ _ when DataSize =< RemSize ->
Rem2 = Rem - DataSize,
{more, << Acc/binary, Data/binary >>, Rem2, {Rem2, Streamed + DataSize}}
end.
@@ -197,7 +200,7 @@ chunked_len(<< $f, R/bits >>, S, A, Len) -> chunked_len(R, S, A, Len * 16 + 15);
%% Note that we currently skip the first character we encounter here,
%% and not in the skip_chunk_ext function. If we latter implement
%% chunk extensions (unlikely) we will need to change this clause too.
-chunked_len(<< C, R/bits >>, S, A, Len) when C =/= $\r -> skip_chunk_ext(R, S, A, Len);
+chunked_len(<< C, R/bits >>, S, A, Len) when ?IS_WS(C); C =:= $; -> skip_chunk_ext(R, S, A, Len, 0);
%% Final chunk.
%%
%% When trailers are following we simply return them as the Rest.
@@ -216,10 +219,15 @@ chunked_len(<<"\r">>, S, A, _) -> {more, {0, S}, A};
chunked_len(<<>>, _, <<>>, _) -> more;
chunked_len(<<>>, S, A, _) -> {more, {0, S}, A}.
-%% @todo We should probably limit how much we skip.
-skip_chunk_ext(R = << "\r", _/bits >>, S, A, Len) -> chunked_len(R, S, A, Len);
-skip_chunk_ext(R = <<>>, S, A, Len) -> chunked_len(R, S, A, Len);
-skip_chunk_ext(<< _, R/bits >>, S, A, Len) -> skip_chunk_ext(R, S, A, Len).
+skip_chunk_ext(R = << "\r", _/bits >>, S, A, Len, _) -> chunked_len(R, S, A, Len);
+skip_chunk_ext(R = <<>>, S, A, Len, _) -> chunked_len(R, S, A, Len);
+%% We skip up to 128 characters of chunk extensions. The value
+%% is hardcoded: chunk extensions are very rarely seen in the
+%% wild and Cowboy doesn't do anything with them anyway.
+%%
+%% Line breaks are not allowed in the middle of chunk extensions.
+skip_chunk_ext(<< C, R/bits >>, S, A, Len, Skipped) when C =/= $\n, Skipped < 128 ->
+ skip_chunk_ext(R, S, A, Len, Skipped + 1).
%% @doc Encode a chunk.