Fix HTTP2 connection failure with HTTP2 framing layer

Resolves: RHEL-5657
This commit is contained in:
Jacek Migacz 2023-12-05 10:55:59 +01:00
parent 0284a133ea
commit cc01c64754
3 changed files with 217 additions and 0 deletions

View File

@ -0,0 +1,193 @@
diff -up curl-7.61.1/lib/http2.c.25a25f45 curl-7.61.1/lib/http2.c
--- curl-7.61.1/lib/http2.c.25a25f45 2023-08-07 14:03:42.043463284 +0200
+++ curl-7.61.1/lib/http2.c 2023-08-07 14:10:24.769489855 +0200
@@ -1202,7 +1202,7 @@ CURLcode Curl_http2_request_upgrade(Curl
binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
httpc->local_settings,
httpc->local_settings_num);
- if(!binlen) {
+ if(binlen <= 0) {
failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
Curl_add_buffer_free(req);
return CURLE_FAILED_INIT;
@@ -1285,6 +1285,14 @@ static int h2_process_pending_input(stru
return -1;
}
+ if(nghttp2_session_check_request_allowed(httpc->h2) == 0) {
+ /* No more requests are allowed in the current session, so
+ the connection may not be reused. This is set when a
+ GOAWAY frame has been received or when the limit of stream
+ identifiers has been reached. */
+ connclose(conn, "http/2: No new requests allowed");
+ }
+
if(should_close_session(httpc)) {
H2BUGF(infof(data,
"h2_process_pending_input: nothing to do in this session\n"));
@@ -1297,7 +1305,6 @@ static int h2_process_pending_input(stru
}
return -1;
}
-
return 0;
}
@@ -1455,8 +1462,6 @@ static int h2_session_send(struct Curl_e
static ssize_t http2_recv(struct connectdata *conn, int sockindex,
char *mem, size_t len, CURLcode *err)
{
- CURLcode result = CURLE_OK;
- ssize_t rv;
ssize_t nread;
struct http_conn *httpc = &conn->proto.httpc;
struct Curl_easy *data = conn->data;
@@ -1519,8 +1524,7 @@ static ssize_t http2_recv(struct connect
/* We have paused nghttp2, but we have no pause data (see
on_data_chunk_recv). */
httpc->pause_stream_id = 0;
- if(h2_process_pending_input(conn, httpc, &result) != 0) {
- *err = result;
+ if(h2_process_pending_input(conn, httpc, err) != 0) {
return -1;
}
}
@@ -1549,8 +1553,7 @@ static ssize_t http2_recv(struct connect
frames, then we have to call it again with 0-length data.
Without this, on_stream_close callback will not be called,
and stream could be hanged. */
- if(h2_process_pending_input(conn, httpc, &result) != 0) {
- *err = result;
+ if(h2_process_pending_input(conn, httpc, err) != 0) {
return -1;
}
}
@@ -1573,7 +1576,6 @@ static ssize_t http2_recv(struct connect
return -1;
}
else {
- char *inbuf;
/* remember where to store incoming data for this stream and how big the
buffer is */
stream->mem = mem;
@@ -1582,16 +1584,15 @@ static ssize_t http2_recv(struct connect
if(httpc->inbuflen == 0) {
nread = ((Curl_recv *)httpc->recv_underlying)(
- conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
+ conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
if(nread == -1) {
- if(result != CURLE_AGAIN)
+ if(*err != CURLE_AGAIN)
failf(data, "Failed receiving HTTP2 data");
else if(stream->closed)
/* received when the stream was already closed! */
return http2_handle_stream_close(conn, data, stream, err);
- *err = result;
return -1;
}
@@ -1604,47 +1605,17 @@ static ssize_t http2_recv(struct connect
H2BUGF(infof(data, "nread=%zd\n", nread));
httpc->inbuflen = nread;
- inbuf = httpc->inbuf;
+
+ DEBUGASSERT(httpc->nread_inbuf == 0);
}
else {
nread = httpc->inbuflen - httpc->nread_inbuf;
- inbuf = httpc->inbuf + httpc->nread_inbuf;
-
+ (void)nread; /* silence warning, used in debug */
H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
nread));
}
- rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
-
- if(nghttp2_is_fatal((int)rv)) {
- failf(data, "nghttp2_session_mem_recv() returned %zd:%s\n",
- rv, nghttp2_strerror((int)rv));
- *err = CURLE_RECV_ERROR;
- return -1;
- }
- H2BUGF(infof(data, "nghttp2_session_mem_recv() returns %zd\n", rv));
- if(nread == rv) {
- H2BUGF(infof(data, "All data in connection buffer processed\n"));
- httpc->inbuflen = 0;
- httpc->nread_inbuf = 0;
- }
- else {
- httpc->nread_inbuf += rv;
- H2BUGF(infof(data, "%zu bytes left in connection buffer\n",
- httpc->inbuflen - httpc->nread_inbuf));
- }
- /* Always send pending frames in nghttp2 session, because
- nghttp2_session_mem_recv() may queue new frame */
- rv = h2_session_send(data, httpc->h2);
- if(rv != 0) {
- *err = CURLE_SEND_ERROR;
- return -1;
- }
-
- if(should_close_session(httpc)) {
- H2BUGF(infof(data, "http2_recv: nothing to do in this session\n"));
- *err = CURLE_HTTP2;
+ if(h2_process_pending_input(conn, httpc, err) != 0)
return -1;
- }
}
if(stream->memlen) {
ssize_t retlen = stream->memlen;
@@ -2108,7 +2079,6 @@ CURLcode Curl_http2_switched(struct conn
CURLcode result;
struct http_conn *httpc = &conn->proto.httpc;
int rv;
- ssize_t nproc;
struct Curl_easy *data = conn->data;
struct HTTP *stream = conn->data->req.protop;
@@ -2186,39 +2156,10 @@ CURLcode Curl_http2_switched(struct conn
memcpy(httpc->inbuf, mem, nread);
httpc->inbuflen = nread;
- nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
- httpc->inbuflen);
+ DEBUGASSERT(httpc->nread_inbuf == 0);
- if(nghttp2_is_fatal((int)nproc)) {
- failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
- nghttp2_strerror((int)nproc), (int)nproc);
+ if(-1 == h2_process_pending_input(conn, httpc, &result))
return CURLE_HTTP2;
- }
-
- H2BUGF(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 = h2_session_send(data, httpc->h2);
-
- if(rv != 0) {
- failf(data, "nghttp2_session_send() failed: %s(%d)",
- nghttp2_strerror(rv), rv);
- return CURLE_HTTP2;
- }
-
- if(should_close_session(httpc)) {
- H2BUGF(infof(data,
- "nghttp2_session_send(): nothing to do in this session\n"));
- return CURLE_HTTP2;
- }
return CURLE_OK;
}

View File

@ -0,0 +1,15 @@
diff -up curl-7.61.1/lib/http2.c.c1b6a384 curl-7.61.1/lib/http2.c
--- curl-7.61.1/lib/http2.c.c1b6a384 2023-08-07 13:59:18.482137005 +0200
+++ curl-7.61.1/lib/http2.c 2023-08-07 14:03:42.043463284 +0200
@@ -1467,6 +1467,11 @@ static ssize_t http2_recv(struct connect
if(should_close_session(httpc)) {
H2BUGF(infof(data,
"http2_recv: nothing to do in this session\n"));
+ if(conn->bits.close) {
+ /* already marked for closure, return OK and we're done */
+ *err = CURLE_OK;
+ return 0;
+ }
*err = CURLE_HTTP2;
return -1;
}

View File

@ -163,6 +163,12 @@ Patch55: 0055-curl-7.61.1-CVE-2023-28322.patch
# fix cookie injection with none file (CVE-2023-38546)
Patch56: 0056-curl-7.61.1-CVE-2023-38546.patch
# consolidate nghttp2_session_mem_recv() call paths
Patch57: 0057-curl-7.61.1-consolidate-nghttp2-session-mem-recv.patch
# when marked for closure and wanted to close == OK
Patch58: 0058-curl-7.61.1-error-in-the-HTTP2-framing-layer.patch
# patch making libcurl multilib ready
Patch101: 0101-curl-7.32.0-multilib.patch
@ -392,6 +398,8 @@ git apply %{PATCH52}
%patch54 -p1
%patch55 -p1
%patch56 -p1
%patch57 -p1
%patch58 -p1
# make tests/*.py use Python 3
sed -e '1 s|^#!/.*python|#!%{__python3}|' -i tests/*.py
@ -559,6 +567,7 @@ rm -f ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la
- cap SFTP packet size sent (RHEL-5311)
- unify the upload/method handling (CVE-2023-28322)
- fix cookie injection with none file (CVE-2023-38546)
- fix HTTP2 connection failure with HTTP2 framing layer (RHEL-5657)
* Tue Jun 27 2023 Jacek Migacz <jmigacz@redhat.com> - 7.61.1-33
- fix host name wildcard checking (CVE-2023-28321)