mirror of
https://github.com/moparisthebest/curl
synced 2024-12-24 00:58:48 -05:00
http: consolidate nghttp2_session_mem_recv() call paths
Previously there were several locations that called nghttp2_session_mem_recv and handled responses slightly differently. Those have been converted to call the existing h2_process_pending_input() function. Moved the end-of-session check to h2_process_pending_input() since the only place the end-of-session state can change is after nghttp2 processes additional input frames. This will likely fix the fuzzing error. While I don't have a root cause the out-of-bounds read seems like a use after free, so moving the nghttp2_session_check_request_allowed() call to a location with a guaranteed nghttp2 session seems reasonable. Also updated a few nghttp2 callsites to include error messages and added a few additional error checks. Closes #5648
This commit is contained in:
parent
4ba275a46a
commit
25a25f45ae
115
lib/http2.c
115
lib/http2.c
@ -1207,13 +1207,6 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
|
|||||||
}
|
}
|
||||||
http->stream_id = 0;
|
http->stream_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(0 == nghttp2_session_check_request_allowed(httpc->h2)) {
|
|
||||||
/* 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(data->conn, "http/2: No new requests allowed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1288,7 +1281,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
|
|||||||
binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
|
binlen = nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
|
||||||
httpc->local_settings,
|
httpc->local_settings,
|
||||||
httpc->local_settings_num);
|
httpc->local_settings_num);
|
||||||
if(!binlen) {
|
if(binlen <= 0) {
|
||||||
failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
|
failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
|
||||||
Curl_dyn_free(req);
|
Curl_dyn_free(req);
|
||||||
return CURLE_FAILED_INIT;
|
return CURLE_FAILED_INIT;
|
||||||
@ -1371,6 +1364,14 @@ static int h2_process_pending_input(struct connectdata *conn,
|
|||||||
return -1;
|
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)) {
|
if(should_close_session(httpc)) {
|
||||||
H2BUGF(infof(data,
|
H2BUGF(infof(data,
|
||||||
"h2_process_pending_input: nothing to do in this session\n"));
|
"h2_process_pending_input: nothing to do in this session\n"));
|
||||||
@ -1383,7 +1384,6 @@ static int h2_process_pending_input(struct connectdata *conn,
|
|||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1564,8 +1564,6 @@ static int h2_session_send(struct Curl_easy *data,
|
|||||||
static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
||||||
char *mem, size_t len, CURLcode *err)
|
char *mem, size_t len, CURLcode *err)
|
||||||
{
|
{
|
||||||
CURLcode result = CURLE_OK;
|
|
||||||
ssize_t rv;
|
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
struct http_conn *httpc = &conn->proto.httpc;
|
struct http_conn *httpc = &conn->proto.httpc;
|
||||||
struct Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
@ -1632,8 +1630,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
/* We have paused nghttp2, but we have no pause data (see
|
/* We have paused nghttp2, but we have no pause data (see
|
||||||
on_data_chunk_recv). */
|
on_data_chunk_recv). */
|
||||||
httpc->pause_stream_id = 0;
|
httpc->pause_stream_id = 0;
|
||||||
if(h2_process_pending_input(conn, httpc, &result) != 0) {
|
if(h2_process_pending_input(conn, httpc, err) != 0) {
|
||||||
*err = result;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1661,8 +1658,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
frames, then we have to call it again with 0-length data.
|
frames, then we have to call it again with 0-length data.
|
||||||
Without this, on_stream_close callback will not be called,
|
Without this, on_stream_close callback will not be called,
|
||||||
and stream could be hanged. */
|
and stream could be hanged. */
|
||||||
if(h2_process_pending_input(conn, httpc, &result) != 0) {
|
if(h2_process_pending_input(conn, httpc, err) != 0) {
|
||||||
*err = result;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1688,7 +1684,6 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
char *inbuf;
|
|
||||||
/* remember where to store incoming data for this stream and how big the
|
/* remember where to store incoming data for this stream and how big the
|
||||||
buffer is */
|
buffer is */
|
||||||
stream->mem = mem;
|
stream->mem = mem;
|
||||||
@ -1697,16 +1692,15 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
|
|
||||||
if(httpc->inbuflen == 0) {
|
if(httpc->inbuflen == 0) {
|
||||||
nread = ((Curl_recv *)httpc->recv_underlying)(
|
nread = ((Curl_recv *)httpc->recv_underlying)(
|
||||||
conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
|
conn, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, err);
|
||||||
|
|
||||||
if(nread == -1) {
|
if(nread == -1) {
|
||||||
if(result != CURLE_AGAIN)
|
if(*err != CURLE_AGAIN)
|
||||||
failf(data, "Failed receiving HTTP2 data");
|
failf(data, "Failed receiving HTTP2 data");
|
||||||
else if(stream->closed)
|
else if(stream->closed)
|
||||||
/* received when the stream was already closed! */
|
/* received when the stream was already closed! */
|
||||||
return http2_handle_stream_close(conn, data, stream, err);
|
return http2_handle_stream_close(conn, data, stream, err);
|
||||||
|
|
||||||
*err = result;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1719,48 +1713,19 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
|
|||||||
H2BUGF(infof(data, "nread=%zd\n", nread));
|
H2BUGF(infof(data, "nread=%zd\n", nread));
|
||||||
|
|
||||||
httpc->inbuflen = nread;
|
httpc->inbuflen = nread;
|
||||||
inbuf = httpc->inbuf;
|
|
||||||
|
DEBUGASSERT(httpc->nread_inbuf == 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
nread = httpc->inbuflen - httpc->nread_inbuf;
|
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",
|
H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
|
||||||
nread));
|
nread));
|
||||||
}
|
}
|
||||||
rv = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)inbuf, nread);
|
|
||||||
|
|
||||||
if(nghttp2_is_fatal((int)rv)) {
|
if(h2_process_pending_input(conn, httpc, err) != 0)
|
||||||
failf(data, "nghttp2_session_mem_recv() returned %zd:%s\n",
|
|
||||||
rv, nghttp2_strerror((int)rv));
|
|
||||||
*err = CURLE_RECV_ERROR;
|
|
||||||
return -1;
|
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;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(stream->memlen) {
|
if(stream->memlen) {
|
||||||
ssize_t retlen = stream->memlen;
|
ssize_t retlen = stream->memlen;
|
||||||
H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
|
H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
|
||||||
@ -2138,7 +2103,9 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
|
|||||||
Curl_safefree(nva);
|
Curl_safefree(nva);
|
||||||
|
|
||||||
if(stream_id < 0) {
|
if(stream_id < 0) {
|
||||||
H2BUGF(infof(conn->data, "http2_send() send error\n"));
|
H2BUGF(infof(conn->data,
|
||||||
|
"http2_send() nghttp2_submit_request error (%s)%d\n",
|
||||||
|
nghttp2_strerror(stream_id), stream_id));
|
||||||
*err = CURLE_SEND_ERROR;
|
*err = CURLE_SEND_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -2148,10 +2115,13 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
|
|||||||
stream->stream_id = stream_id;
|
stream->stream_id = stream_id;
|
||||||
|
|
||||||
/* this does not call h2_session_send() since there can not have been any
|
/* this does not call h2_session_send() since there can not have been any
|
||||||
* priority upodate since the nghttp2_submit_request() call above */
|
* priority update since the nghttp2_submit_request() call above */
|
||||||
rv = nghttp2_session_send(h2);
|
rv = nghttp2_session_send(h2);
|
||||||
|
|
||||||
if(rv != 0) {
|
if(rv != 0) {
|
||||||
|
H2BUGF(infof(conn->data,
|
||||||
|
"http2_send() nghttp2_session_send error (%s)%d\n",
|
||||||
|
nghttp2_strerror(rv), rv));
|
||||||
|
|
||||||
*err = CURLE_SEND_ERROR;
|
*err = CURLE_SEND_ERROR;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -2236,7 +2206,6 @@ 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 Curl_easy *data = conn->data;
|
struct Curl_easy *data = conn->data;
|
||||||
struct HTTP *stream = conn->data->req.protop;
|
struct HTTP *stream = conn->data->req.protop;
|
||||||
|
|
||||||
@ -2310,41 +2279,13 @@ CURLcode Curl_http2_switched(struct connectdata *conn,
|
|||||||
|
|
||||||
if(nread)
|
if(nread)
|
||||||
memcpy(httpc->inbuf, mem, nread);
|
memcpy(httpc->inbuf, mem, nread);
|
||||||
|
|
||||||
httpc->inbuflen = nread;
|
httpc->inbuflen = nread;
|
||||||
|
|
||||||
nproc = nghttp2_session_mem_recv(httpc->h2, (const uint8_t *)httpc->inbuf,
|
DEBUGASSERT(httpc->nread_inbuf == 0);
|
||||||
httpc->inbuflen);
|
|
||||||
|
|
||||||
if(nghttp2_is_fatal((int)nproc)) {
|
if(-1 == h2_process_pending_input(conn, httpc, &result))
|
||||||
failf(data, "nghttp2_session_mem_recv() failed: %s(%d)",
|
|
||||||
nghttp2_strerror((int)nproc), (int)nproc);
|
|
||||||
return CURLE_HTTP2;
|
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;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user