Skip to content

Commit 29c63b7

Browse files
committed
SECURITY: CVE-2016-8740
mod_http2: properly crafted, endless HTTP/2 CONTINUATION frames could be used to exhaust all server's memory. Reported by: Naveen Tiwari <naveen.tiwari@asu.edu> and CDF/SEFCOM at Arizona State University git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1772576 13f79535-47bb-0310-9956-ffa450edef68
1 parent a89bd55 commit 29c63b7

File tree

3 files changed

+46
-30
lines changed

3 files changed

+46
-30
lines changed

Diff for: CHANGES

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
-*- coding: utf-8 -*-
22
Changes with Apache 2.5.0
33

4+
*) mod_http2: CVE-2016-8740: Mitigate DoS memory exhaustion via endless
5+
CONTINUATION frames.
6+
[Naveen Tiwari <naveen.tiwari@asu.edu> and CDF/SEFCOM at Arizona State University, Stefan Eissing]
7+
48
*) mod_lua: Fix default value of LuaInherit directive. It should be
59
'parent-first' instead of 'none', as per documentation. PR 60419
610
[Christophe Jaillet]

Diff for: modules/http2/h2_session.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
394394
(void)flags;
395395
stream = get_stream(session, frame->hd.stream_id);
396396
if (!stream) {
397-
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, session->c,
397+
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
398398
APLOGNO(02920)
399399
"h2_session: stream(%ld-%d): on_header unknown stream",
400400
session->id, (int)frame->hd.stream_id);
@@ -403,7 +403,14 @@ static int on_header_cb(nghttp2_session *ngh2, const nghttp2_frame *frame,
403403

404404
status = h2_stream_add_header(stream, (const char *)name, namelen,
405405
(const char *)value, valuelen);
406-
if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) {
406+
if (status == APR_ECONNRESET) {
407+
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, session->c,
408+
"h2-stream(%ld-%d): on_header, reset stream",
409+
session->id, stream->id);
410+
nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream->id,
411+
NGHTTP2_INTERNAL_ERROR);
412+
}
413+
else if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) {
407414
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
408415
}
409416
return 0;

Diff for: modules/http2/h2_stream.c

+33-28
Original file line numberDiff line numberDiff line change
@@ -333,45 +333,50 @@ apr_status_t h2_stream_add_header(h2_stream *stream,
333333
const char *name, size_t nlen,
334334
const char *value, size_t vlen)
335335
{
336+
int error = 0;
336337
ap_assert(stream);
337338

338-
if (!stream->has_response) {
339-
if (name[0] == ':') {
340-
if ((vlen) > stream->session->s->limit_req_line) {
341-
/* pseudo header: approximation of request line size check */
342-
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
343-
"h2_stream(%ld-%d): pseudo header %s too long",
344-
stream->session->id, stream->id, name);
345-
return h2_stream_set_error(stream,
346-
HTTP_REQUEST_URI_TOO_LARGE);
347-
}
348-
}
349-
else if ((nlen + 2 + vlen) > stream->session->s->limit_req_fieldsize) {
350-
/* header too long */
339+
if (stream->has_response) {
340+
return APR_EINVAL;
341+
}
342+
++stream->request_headers_added;
343+
if (name[0] == ':') {
344+
if ((vlen) > stream->session->s->limit_req_line) {
345+
/* pseudo header: approximation of request line size check */
351346
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
352-
"h2_stream(%ld-%d): header %s too long",
347+
"h2_stream(%ld-%d): pseudo header %s too long",
353348
stream->session->id, stream->id, name);
354-
return h2_stream_set_error(stream,
355-
HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
349+
error = HTTP_REQUEST_URI_TOO_LARGE;
356350
}
357-
358-
if (name[0] != ':') {
359-
++stream->request_headers_added;
360-
if (stream->request_headers_added
361-
> stream->session->s->limit_req_fields) {
362-
/* too many header lines */
363-
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
364-
"h2_stream(%ld-%d): too many header lines",
365-
stream->session->id, stream->id);
366-
return h2_stream_set_error(stream,
367-
HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE);
368-
}
351+
}
352+
else if ((nlen + 2 + vlen) > stream->session->s->limit_req_fieldsize) {
353+
/* header too long */
354+
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
355+
"h2_stream(%ld-%d): header %s too long",
356+
stream->session->id, stream->id, name);
357+
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
358+
}
359+
360+
if (stream->request_headers_added
361+
> stream->session->s->limit_req_fields + 4) {
362+
/* too many header lines, include 4 pseudo headers */
363+
if (stream->request_headers_added
364+
> stream->session->s->limit_req_fields + 4 + 100) {
365+
/* yeah, right */
366+
return APR_ECONNRESET;
369367
}
368+
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
369+
"h2_stream(%ld-%d): too many header lines",
370+
stream->session->id, stream->id);
371+
error = HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE;
370372
}
371373

372374
if (h2_stream_is_scheduled(stream)) {
373375
return add_trailer(stream, name, nlen, value, vlen);
374376
}
377+
else if (error) {
378+
return h2_stream_set_error(stream, error);
379+
}
375380
else {
376381
if (!stream->rtmp) {
377382
stream->rtmp = h2_req_create(stream->id, stream->pool,

0 commit comments

Comments
 (0)