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().
This commit is contained in:
Tatsuhiro Tsujikawa 2015-05-26 00:10:05 +09:00 committed by Daniel Stenberg
parent 96c0164b88
commit 02dfc930b5

View File

@ -1248,6 +1248,7 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
CURLcode result; CURLcode result;
struct http_conn *httpc = &conn->proto.httpc; struct http_conn *httpc = &conn->proto.httpc;
int rv; int rv;
ssize_t nproc;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
struct HTTP *stream = conn->data->req.protop; 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); /* we are going to copy mem to httpc->inbuf. This is required since
mem is part of buffer pointed by stream->mem, and callbacks
if(rv != (int)nread) { called by nghttp2_session_mem_recv() will write stream specific
failf(data, "nghttp2_session_mem_recv() failed: %s(%d)", data into stream->mem, overwriting data already there. */
nghttp2_strerror(rv), rv); 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; 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. */ /* Try to send some frames since we may read SETTINGS already. */
rv = nghttp2_session_send(httpc->h2); rv = nghttp2_session_send(httpc->h2);