From 02dfc930b5d5aaeab0ea7bf0019d2e753daa7491 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 26 May 2015 00:10:05 +0900 Subject: [PATCH] http2: Copy data passed in Curl_http2_switched into HTTP/2 connection buffer Previously, after seeing upgrade to HTTP/2, we feed data followed by upgrade response headers directly to nghttp2_session_mem_recv() in Curl_http2_switched(). But it turns out that passed buffer, mem, is part of stream->mem, and callbacks called by nghttp2_session_mem_recv() will write stream specific data into stream->mem, overwriting input data. This will corrupt input, and most likely frame length error is detected by nghttp2 library. The fix is first copy the passed data to HTTP/2 connection buffer, httpc->inbuf, and call nghttp2_session_mem_recv(). --- lib/http2.c | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/lib/http2.c b/lib/http2.c index a56535471e..fa47d0ece1 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -1248,6 +1248,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn, CURLcode result; struct http_conn *httpc = &conn->proto.httpc; int rv; + ssize_t nproc; struct SessionHandle *data = conn->data; struct HTTP *stream = conn->data->req.protop; @@ -1290,14 +1291,43 @@ CURLcode Curl_http2_switched(struct connectdata *conn, } } - rv = (int)nghttp2_session_mem_recv(httpc->h2, (const uint8_t*)mem, nread); - - if(rv != (int)nread) { - failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", - nghttp2_strerror(rv), rv); + /* we are going to copy mem to httpc->inbuf. This is required since + mem is part of buffer pointed by stream->mem, and callbacks + called by nghttp2_session_mem_recv() will write stream specific + data into stream->mem, overwriting data already there. */ + if(H2_BUFSIZE < nread) { + failf(data, "connection buffer size is too small to store data following " + "HTTP Upgrade response header: buflen=%zu, datalen=%zu", + H2_BUFSIZE, nread); return CURLE_HTTP2; } + infof(conn->data, "Copying HTTP/2 data in stream buffer to connection buffer" + " after upgrade: len=%zu\n", + nread); + + memcpy(httpc->inbuf, mem, nread); + httpc->inbuflen = nread; + + nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf, + httpc->inbuflen); + + if(nghttp2_is_fatal((int)nproc)) { + failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", + nghttp2_strerror((int)nproc), (int)nproc); + return CURLE_HTTP2; + } + + DEBUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", nproc)); + + if((ssize_t)nread == nproc) { + httpc->inbuflen = 0; + httpc->nread_inbuf = 0; + } + else { + httpc->nread_inbuf += nproc; + } + /* Try to send some frames since we may read SETTINGS already. */ rv = nghttp2_session_send(httpc->h2);