Browse Source

Fix crash in cowboy_http2 when content-length is invalid

Loïc Hoguin 7 years ago
parent
commit
19054e40e0
2 changed files with 33 additions and 12 deletions
  1. 14 11
      src/cowboy_http2.erl
  2. 19 1
      test/rfc7540_SUITE.erl

+ 14 - 11
src/cowboy_http2.erl

@@ -927,24 +927,27 @@ headers_to_map([{Name, Value}|Tail], Acc0) ->
 	end,
 	headers_to_map(Tail, Acc).
 
-stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert},
-		StreamID, IsFin, Headers, #{method := Method, scheme := Scheme,
-			authority := Authority, path := PathWithQs}) ->
-	BodyLength = case Headers of
+stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders) ->
+	case Headers of
 		_ when IsFin =:= fin ->
-			0;
+			stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, 0);
 		#{<<"content-length">> := <<"0">>} ->
-			0;
+			stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, 0);
 		#{<<"content-length">> := BinLength} ->
 			try
-				cow_http_hd:parse_content_length(BinLength)
+				stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders,
+					cow_http_hd:parse_content_length(BinLength))
 			catch _:_ ->
-				terminate(State, {stream_error, StreamID, protocol_error,
-					'The content-length header is invalid. (RFC7230 3.3.2)'})
+				stream_malformed(State, StreamID,
+					'The content-length header is invalid. (RFC7230 3.3.2)')
 			end;
 		_ ->
-			undefined
-	end,
+			stream_req_init(State, StreamID, IsFin, Headers, PseudoHeaders, undefined)
+	end.
+
+stream_req_init(State=#state{ref=Ref, peer=Peer, sock=Sock, cert=Cert},
+		StreamID, IsFin, Headers, #{method := Method, scheme := Scheme,
+			authority := Authority, path := PathWithQs}, BodyLength) ->
 	try cow_http_hd:parse_host(Authority) of
 		{Host, Port} ->
 			try cow_http:parse_fullpath(PathWithQs) of

+ 19 - 1
test/rfc7540_SUITE.erl

@@ -3196,7 +3196,25 @@ reject_many_pseudo_header_path(Config) ->
 %   that is defined to have no payload, as described in [RFC7230],
 %   Section 3.3.2, can have a non-zero content-length header field, even
 %   though no content is included in DATA frames.
-%
+
+reject_duplicate_content_length_header(Config) ->
+	doc("A request with duplicate content-length headers must be rejected "
+		"with a PROTOCOL_ERROR stream error. (RFC7230 3.3.2, RFC7540 8.1.2.6)"),
+	{ok, Socket} = do_handshake(Config),
+	%% Send a HEADERS frame with more than one content-length header.
+	{HeadersBlock, _} = cow_hpack:encode([
+		{<<":method">>, <<"GET">>},
+		{<<":scheme">>, <<"http">>},
+		{<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+		{<<":path">>, <<>>},
+		{<<"content-length">>, <<"12">>},
+		{<<"content-length">>, <<"12">>}
+	]),
+	ok = gen_tcp:send(Socket, cow_http2:headers(1, nofin, HeadersBlock)),
+	%% Receive a PROTOCOL_ERROR stream error.
+	{ok, << _:24, 3:8, _:8, 1:32, 1:32 >>} = gen_tcp:recv(Socket, 13, 6000),
+	ok.
+
 %   Intermediaries that process HTTP requests or responses (i.e., any
 %   intermediary not acting as a tunnel) MUST NOT forward a malformed
 %   request or response.  Malformed requests or responses that are