]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http2: Copy data passed in Curl_http2_switched into HTTP/2 connection buffer
authorTatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Mon, 25 May 2015 15:10:05 +0000 (00:10 +0900)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 25 May 2015 21:07:49 +0000 (23:07 +0200)
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

index a56535471ecddb2dc4196ed2d2f163ee6f3f64da..fa47d0ece16eae3192c4eb655dc313e15f1d33f6 100644 (file)
@@ -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);
+  /* 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);
 
-  if(rv != (int)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(rv), rv);
+          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);