MEDIUM: h2: prevent the various mux encoders from modifying the buffer

Functions h2s_frt_make_resp_headers() and h2s_frt_make_resp_data() used
to modify the buffer's output data count. This is problematic for the
buffer's rework as we don't want to rely on this anymore. This commit
modifies these functions to take an offset (relative to the buffer's
head) and a maximum byte count. Thus h2_snd_buf() now calls them with
buf->o and takes care of removing deleted data itself. The send functions
now almost support being passed const buffers (except for the data part
which is still embedded).
This commit is contained in:
Willy Tarreau 2018-06-14 13:33:30 +02:00
parent 1dc41e75d8
commit 5dd17353d5

View File

@ -2938,12 +2938,12 @@ static int h2_rcv_buf(struct conn_stream *cs, struct buffer *buf, int count)
return ret; return ret;
} }
/* Try to send a HEADERS frame matching HTTP/1 response present in buffer <buf> /* Try to send a HEADERS frame matching HTTP/1 response present at offset <ofs>
* for the H2 stream <h2s>. Returns the number of bytes sent. The caller must * and for <max> bytes in buffer <buf> for the H2 stream <h2s>. Returns the
* check the stream's status to detect any error which might have happened * number of bytes sent. The caller must check the stream's status to detect
* subsequently to a successful send. * any error which might have happened subsequently to a successful send.
*/ */
static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf) static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf, size_t ofs, size_t max)
{ {
struct http_hdr list[MAX_HTTP_HDR]; struct http_hdr list[MAX_HTTP_HDR];
struct h2c *h2c = h2s->h2c; struct h2c *h2c = h2s->h2c;
@ -2969,7 +2969,7 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf)
* block does not wrap and we can safely read it this way without * block does not wrap and we can safely read it this way without
* having to realign the buffer. * having to realign the buffer.
*/ */
ret = h1_headers_to_hdr_list(b_head(buf), b_head(buf) + buf->o, ret = h1_headers_to_hdr_list(b_peek(buf, ofs), b_peek(buf, ofs) + max,
list, sizeof(list)/sizeof(list[0]), h1m); list, sizeof(list)/sizeof(list[0]), h1m);
if (ret <= 0) { if (ret <= 0) {
/* incomplete or invalid response, this is abnormal coming from /* incomplete or invalid response, this is abnormal coming from
@ -3075,7 +3075,7 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf)
outbuf.str[4] |= H2_F_HEADERS_END_STREAM; outbuf.str[4] |= H2_F_HEADERS_END_STREAM;
/* consume incoming H1 response */ /* consume incoming H1 response */
b_del(buf, ret); max -= ret;
/* commit the H2 response */ /* commit the H2 response */
h2c->mbuf->o += outbuf.len; h2c->mbuf->o += outbuf.len;
@ -3087,7 +3087,7 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf)
*/ */
if (es_now) { if (es_now) {
// trim any possibly pending data (eg: inconsistent content-length) // trim any possibly pending data (eg: inconsistent content-length)
b_del(buf, buf->o); ret += max;
h1m->state = HTTP_MSG_DONE; h1m->state = HTTP_MSG_DONE;
h2s->flags |= H2_SF_ES_SENT; h2s->flags |= H2_SF_ES_SENT;
@ -3111,12 +3111,12 @@ static size_t h2s_frt_make_resp_headers(struct h2s *h2s, struct buffer *buf)
return ret; return ret;
} }
/* Try to send a DATA frame matching HTTP/1 response present in the response /* Try to send a DATA frame matching HTTP/1 response present at offset <ofs>
* buffer <buf>, for stream <h2s>. Returns the number of bytes sent. The caller * for up to <max> bytes in response buffer <buf>, for stream <h2s>. Returns
* must check the stream's status to detect any error which might have happened * the number of bytes sent. The caller must check the stream's status to
* subsequently to a successful send. * detect any error which might have happened subsequently to a successful send.
*/ */
static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf) static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf, size_t ofs, size_t max)
{ {
struct h2c *h2c = h2s->h2c; struct h2c *h2c = h2s->h2c;
struct h1m *h1m = &h2s->res; struct h1m *h1m = &h2s->res;
@ -3140,7 +3140,7 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
} }
new_frame: new_frame:
if (!buf->o) if (!max)
goto end; goto end;
chunk_reset(&outbuf); chunk_reset(&outbuf);
@ -3169,17 +3169,18 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
switch (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) { switch (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
case 0: /* no content length, read till SHUTW */ case 0: /* no content length, read till SHUTW */
size = buf->o; size = max;
h1m->curr_len = size; h1m->curr_len = size;
break; break;
case H1_MF_CLEN: /* content-length: read only h2m->body_len */ case H1_MF_CLEN: /* content-length: read only h2m->body_len */
size = buf->o; size = max;
if ((long long)size > h1m->curr_len) if ((long long)size > h1m->curr_len)
size = h1m->curr_len; size = h1m->curr_len;
break; break;
default: /* te:chunked : parse chunks */ default: /* te:chunked : parse chunks */
if (h1m->state == HTTP_MSG_CHUNK_CRLF) { if (h1m->state == HTTP_MSG_CHUNK_CRLF) {
ret = h1_skip_chunk_crlf(buf, -buf->o, 0); // FIXME: this one still uses the old buffer API and ignores <ofs>
ret = h1_skip_chunk_crlf(buf, -max, 0);
if (!ret) if (!ret)
goto end; goto end;
@ -3189,15 +3190,16 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
h2s_error(h2s, H2_ERR_INTERNAL_ERROR); h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
goto end; goto end;
} }
b_del(buf, ret); max -= ret;
ofs += ret;
total += ret; total += ret;
h1m->state = HTTP_MSG_CHUNK_SIZE; h1m->state = HTTP_MSG_CHUNK_SIZE;
} }
if (h1m->state == HTTP_MSG_CHUNK_SIZE) { if (h1m->state == HTTP_MSG_CHUNK_SIZE) {
unsigned int chunk; unsigned int chunk;
// FIXME: this one still uses the old buffer API and ignores <ofs>
ret = h1_parse_chunk_size(buf, -buf->o, 0, &chunk); ret = h1_parse_chunk_size(buf, -max, 0, &chunk);
if (!ret) if (!ret)
goto end; goto end;
@ -3211,7 +3213,8 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
size = chunk; size = chunk;
h1m->curr_len = chunk; h1m->curr_len = chunk;
h1m->body_len += chunk; h1m->body_len += chunk;
b_del(buf, ret); max -= ret;
ofs += ret;
total += ret; total += ret;
h1m->state = size ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS; h1m->state = size ? HTTP_MSG_DATA : HTTP_MSG_TRAILERS;
if (!size) if (!size)
@ -3232,8 +3235,8 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
* unblocked on window opening. Note: we don't implement padding. * unblocked on window opening. Note: we don't implement padding.
*/ */
if (size > buf->o) if (size > max)
size = buf->o; size = max;
if (size > h2s->mws) if (size > h2s->mws)
size = h2s->mws; size = h2s->mws;
@ -3271,7 +3274,7 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
/* copy whatever we can */ /* copy whatever we can */
blk1 = blk2 = NULL; // silence a maybe-uninitialized warning blk1 = blk2 = NULL; // silence a maybe-uninitialized warning
ret = b_getblk_nc(buf, &blk1, &len1, &blk2, &len2, 0, buf->o); ret = b_getblk_nc(buf, &blk1, &len1, &blk2, &len2, ofs, max);
if (ret == 1) if (ret == 1)
len2 = 0; len2 = 0;
@ -3328,7 +3331,8 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
/* consume incoming H1 response */ /* consume incoming H1 response */
if (size > 0) { if (size > 0) {
b_del(buf, size); max -= size;
ofs += size;
total += size; total += size;
h1m->curr_len -= size; h1m->curr_len -= size;
h2s->mws -= size; h2s->mws -= size;
@ -3348,7 +3352,9 @@ static size_t h2s_frt_make_resp_data(struct h2s *h2s, struct buffer *buf)
if (!(h1m->flags & H1_MF_CHNK)) { if (!(h1m->flags & H1_MF_CHNK)) {
// trim any possibly pending data (eg: inconsistent content-length) // trim any possibly pending data (eg: inconsistent content-length)
b_del(buf, buf->o); total += max;
ofs += max;
max = 0;
h1m->state = HTTP_MSG_DONE; h1m->state = HTTP_MSG_DONE;
} }
@ -3366,13 +3372,16 @@ static int h2_snd_buf(struct conn_stream *cs, struct buffer *buf, int flags)
{ {
struct h2s *h2s = cs->ctx; struct h2s *h2s = cs->ctx;
size_t total = 0; size_t total = 0;
size_t ret;
if (!(h2s->flags & H2_SF_OUTGOING_DATA) && buf->o) if (!(h2s->flags & H2_SF_OUTGOING_DATA) && buf->o)
h2s->flags |= H2_SF_OUTGOING_DATA; h2s->flags |= H2_SF_OUTGOING_DATA;
while (h2s->res.state < HTTP_MSG_DONE && buf->o) { while (h2s->res.state < HTTP_MSG_DONE && buf->o) {
if (h2s->res.state < HTTP_MSG_BODY) { if (h2s->res.state < HTTP_MSG_BODY) {
total += h2s_frt_make_resp_headers(h2s, buf); ret = h2s_frt_make_resp_headers(h2s, buf, 0, buf->o);
total += ret;
b_del(buf, ret);
if (h2s->st >= H2_SS_ERROR) if (h2s->st >= H2_SS_ERROR)
break; break;
@ -3381,7 +3390,9 @@ static int h2_snd_buf(struct conn_stream *cs, struct buffer *buf, int flags)
break; break;
} }
else if (h2s->res.state < HTTP_MSG_TRAILERS) { else if (h2s->res.state < HTTP_MSG_TRAILERS) {
total += h2s_frt_make_resp_data(h2s, buf); ret = h2s_frt_make_resp_data(h2s, buf, 0, buf->o);
total += ret;
b_del(buf, ret);
if (h2s->st >= H2_SS_ERROR) if (h2s->st >= H2_SS_ERROR)
break; break;
@ -3391,15 +3402,15 @@ static int h2_snd_buf(struct conn_stream *cs, struct buffer *buf, int flags)
} }
else if (h2s->res.state == HTTP_MSG_TRAILERS) { else if (h2s->res.state == HTTP_MSG_TRAILERS) {
/* consume the trailers if any (we don't forward them for now) */ /* consume the trailers if any (we don't forward them for now) */
int count = h1_measure_trailers(buf, buf->o); ret = h1_measure_trailers(buf, buf->o);
if (unlikely(count <= 0)) { if (unlikely((int)ret <= 0)) {
if (count < 0) if ((int)ret < 0)
h2s_error(h2s, H2_ERR_INTERNAL_ERROR); h2s_error(h2s, H2_ERR_INTERNAL_ERROR);
break; break;
} }
total += count; total += ret;
b_del(buf, count); b_del(buf, ret);
// trim any possibly pending data (eg: extra CR-LF, ...) // trim any possibly pending data (eg: extra CR-LF, ...)
b_del(buf, buf->o); b_del(buf, buf->o);