Fix header compression bug

This commit is contained in:
Tatsuhiro Tsujikawa 2013-07-26 01:34:28 +09:00
parent 48d7453a21
commit e496800cb7
3 changed files with 91 additions and 56 deletions

View File

@ -175,6 +175,7 @@ static int nghttp2_hd_context_init(nghttp2_hd_context *context,
{ {
int i; int i;
const char **ini_table; const char **ini_table;
context->bad = 0;
context->hd_table = malloc(sizeof(nghttp2_hd_entry*)* context->hd_table = malloc(sizeof(nghttp2_hd_entry*)*
NGHTTP2_INITIAL_HD_TABLE_SIZE); NGHTTP2_INITIAL_HD_TABLE_SIZE);
memset(context->hd_table, 0, sizeof(nghttp2_hd_entry*)* memset(context->hd_table, 0, sizeof(nghttp2_hd_entry*)*
@ -227,15 +228,15 @@ static void nghttp2_hd_context_free(nghttp2_hd_context *context)
nghttp2_hd_ws_entry *ent = &context->ws[i]; nghttp2_hd_ws_entry *ent = &context->ws[i];
switch(ent->cat) { switch(ent->cat) {
case NGHTTP2_HD_CAT_INDEXED: case NGHTTP2_HD_CAT_INDEXED:
--ent->indexed.entry->ref; if(--ent->indexed.entry->ref == 0) {
if(ent->indexed.entry->ref == 0) {
nghttp2_hd_entry_free(ent->indexed.entry); nghttp2_hd_entry_free(ent->indexed.entry);
free(ent->indexed.entry);
} }
break; break;
case NGHTTP2_HD_CAT_INDNAME: case NGHTTP2_HD_CAT_INDNAME:
--ent->indname.entry->ref; if(--ent->indname.entry->ref == 0) {
if(ent->indname.entry->ref == 0) {
nghttp2_hd_entry_free(ent->indname.entry); nghttp2_hd_entry_free(ent->indname.entry);
free(ent->indname.entry);
} }
break; break;
default: default:
@ -244,9 +245,9 @@ static void nghttp2_hd_context_free(nghttp2_hd_context *context)
} }
for(i = 0; i < context->refsetlen; ++i) { for(i = 0; i < context->refsetlen; ++i) {
nghttp2_hd_entry *ent = context->refset[i]; nghttp2_hd_entry *ent = context->refset[i];
--ent->ref; if(--ent->ref == 0) {
if(ent->ref == 0) {
nghttp2_hd_entry_free(ent); nghttp2_hd_entry_free(ent);
free(ent);
} }
} }
for(i = 0; i < context->hd_tablelen; ++i) { for(i = 0; i < context->hd_tablelen; ++i) {
@ -677,7 +678,7 @@ static int emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
{ {
int rv; int rv;
uint8_t *bufp; uint8_t *bufp;
size_t blocklen = count_encoded_length(ent->index + 1, 5) + size_t blocklen = count_encoded_length(ent->index + 1, 6) +
count_encoded_length(index, 8) + count_encoded_length(index, 8) +
count_encoded_length(valuelen, 8) + valuelen; count_encoded_length(valuelen, 8) + valuelen;
rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen); rv = ensure_write_buffer(buf_ptr, buflen_ptr, *offset_ptr, blocklen);
@ -685,7 +686,7 @@ static int emit_subst_indname_block(uint8_t **buf_ptr, size_t *buflen_ptr,
return rv; return rv;
} }
bufp = *buf_ptr + *offset_ptr; bufp = *buf_ptr + *offset_ptr;
bufp += encode_length(bufp, ent->index + 1, 5); bufp += encode_length(bufp, ent->index + 1, 6);
bufp += encode_length(bufp, index, 8); bufp += encode_length(bufp, index, 8);
bufp += encode_length(bufp, valuelen, 8); bufp += encode_length(bufp, valuelen, 8);
memcpy(bufp, value, valuelen); memcpy(bufp, value, valuelen);
@ -748,7 +749,10 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
nghttp2_nv *nv, size_t nvlen) nghttp2_nv *nv, size_t nvlen)
{ {
size_t i, offset; size_t i, offset;
int rv; int rv = 0;
if(deflater->bad) {
return NGHTTP2_ERR_HEADER_COMP;
}
create_workingset(deflater); create_workingset(deflater);
offset = nv_offset; offset = nv_offset;
for(i = 0; i < nvlen; ++i) { for(i = 0; i < nvlen; ++i) {
@ -765,11 +769,11 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
/* If nv[i] is found in hd_table, use Indexed Header repr */ /* If nv[i] is found in hd_table, use Indexed Header repr */
rv = add_workingset(deflater, ent); rv = add_workingset(deflater, ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent); rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else { } else {
/* Check name exists in hd_table */ /* Check name exists in hd_table */
@ -784,31 +788,32 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
rv = emit_indname_block(buf_ptr, buflen_ptr, &offset, ent, rv = emit_indname_block(buf_ptr, buflen_ptr, &offset, ent,
nv[i].value, nv[i].valuelen, 0); nv[i].value, nv[i].valuelen, 0);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else { } else {
nghttp2_hd_entry *new_ent; nghttp2_hd_entry *new_ent;
/* No need to increment ent->ref here */ /* No need to increment ent->ref here */
new_ent = add_hd_table_subst(deflater, &nv[i], ent->index); new_ent = add_hd_table_subst(deflater, &nv[i], ent->index);
if(!new_ent) { if(!new_ent) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
rv = add_workingset(deflater, new_ent); rv = add_workingset(deflater, new_ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
rv = emit_subst_indname_block(buf_ptr, buflen_ptr, &offset, rv = emit_subst_indname_block(buf_ptr, buflen_ptr, &offset,
new_ent, new_ent,
nv[i].value, nv[i].valuelen, nv[i].value, nv[i].valuelen,
new_ent->index); new_ent->index);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} }
} else { } else {
rv = emit_newname_block(buf_ptr, buflen_ptr, &offset, &nv[i], 0); rv = emit_newname_block(buf_ptr, buflen_ptr, &offset, &nv[i], 0);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} }
} }
@ -821,11 +826,14 @@ ssize_t nghttp2_hd_deflate_hd(nghttp2_hd_context *deflater,
rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset, rv = emit_indexed_block(buf_ptr, buflen_ptr, &offset,
ws_ent->indexed.entry); ws_ent->indexed.entry);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} }
} }
return offset - nv_offset; return offset - nv_offset;
fail:
deflater->bad = 1;
return rv;
} }
static ssize_t build_nv_array(nghttp2_hd_context *inflater, static ssize_t build_nv_array(nghttp2_hd_context *inflater,
@ -875,8 +883,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_nv **nva_ptr, nghttp2_nv **nva_ptr,
uint8_t *in, size_t inlen) uint8_t *in, size_t inlen)
{ {
int rv; int rv = 0;
uint8_t *last = in + inlen; uint8_t *last = in + inlen;
if(inflater->bad) {
return NGHTTP2_ERR_HEADER_COMP;
}
create_workingset(inflater); create_workingset(inflater);
for(; in != last;) { for(; in != last;) {
uint8_t c = *in; uint8_t c = *in;
@ -886,13 +897,13 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
ssize_t index; ssize_t index;
in = decode_length(&index, in, last, 7); in = decode_length(&index, in, last, 7);
if(index < 0) { if(index < 0) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
ws_ent = find_in_workingset_by_index(inflater, index); ws_ent = find_in_workingset_by_index(inflater, index);
if(ws_ent) { if(ws_ent) {
assert(ws_ent->cat == NGHTTP2_HD_CAT_INDEXED); assert(ws_ent->cat == NGHTTP2_HD_CAT_INDEXED);
--ws_ent->indexed.entry->ref; if(--ws_ent->indexed.entry->ref == 0) {
if(ws_ent->indexed.entry->ref == 0) {
nghttp2_hd_entry_free(ws_ent->indexed.entry); nghttp2_hd_entry_free(ws_ent->indexed.entry);
free(ws_ent->indexed.entry); free(ws_ent->indexed.entry);
} }
@ -900,12 +911,13 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
} else { } else {
nghttp2_hd_entry *ent; nghttp2_hd_entry *ent;
if(inflater->hd_tablelen <= index) { if(inflater->hd_tablelen <= index) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
ent = inflater->hd_table[index]; ent = inflater->hd_table[index];
rv = add_workingset(inflater, ent); rv = add_workingset(inflater, ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} }
} else if(c == 0x60u || c == 0x40u) { } else if(c == 0x60u || c == 0x40u) {
@ -914,17 +926,20 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_nv nv; nghttp2_nv nv;
ssize_t namelen, valuelen; ssize_t namelen, valuelen;
if(++in == last) { if(++in == last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
in = decode_length(&namelen, in, last, 8); in = decode_length(&namelen, in, last, 8);
if(namelen < 0 || in + namelen > last) { if(namelen < 0 || in + namelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
nv.name = in; nv.name = in;
in += namelen; in += namelen;
in = decode_length(&valuelen, in, last, 8); in = decode_length(&valuelen, in, last, 8);
if(valuelen < 0 || in + valuelen > last) { if(valuelen < 0 || in + valuelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
nv.namelen = namelen; nv.namelen = namelen;
nv.value = in; nv.value = in;
@ -938,11 +953,12 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
if(ent) { if(ent) {
rv = add_workingset(inflater, ent); rv = add_workingset(inflater, ent);
} else { } else {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
} }
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else if((c & 0x60u) == 0x60u || (c & 0x40) == 0x40u) { } else if((c & 0x60u) == 0x60u || (c & 0x40) == 0x40u) {
/* Literal Header without Indexing - indexed name or Literal /* Literal Header without Indexing - indexed name or Literal
@ -953,16 +969,19 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
ssize_t index; ssize_t index;
in = decode_length(&index, in, last, 5); in = decode_length(&index, in, last, 5);
if(index < 0) { if(index < 0) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
--index; --index;
if(inflater->hd_tablelen <= index) { if(inflater->hd_tablelen <= index) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
ent = inflater->hd_table[index]; ent = inflater->hd_table[index];
in = decode_length(&valuelen, in , last, 8); in = decode_length(&valuelen, in , last, 8);
if(valuelen < 0 || in + valuelen > last) { if(valuelen < 0 || in + valuelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
value = in; value = in;
in += valuelen; in += valuelen;
@ -984,11 +1003,12 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
if(new_ent) { if(new_ent) {
rv = add_workingset(inflater, new_ent); rv = add_workingset(inflater, new_ent);
} else { } else {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
} }
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else if(c == 0) { } else if(c == 0) {
/* Literal Header with substitution indexing - new name */ /* Literal Header with substitution indexing - new name */
@ -996,21 +1016,25 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_nv nv; nghttp2_nv nv;
ssize_t namelen, valuelen, subindex; ssize_t namelen, valuelen, subindex;
if(++in == last) { if(++in == last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
in = decode_length(&namelen, in, last, 8); in = decode_length(&namelen, in, last, 8);
if(namelen < 0 || in + namelen > last) { if(namelen < 0 || in + namelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
nv.name = in; nv.name = in;
in += namelen; in += namelen;
in = decode_length(&subindex, in, last, 8); in = decode_length(&subindex, in, last, 8);
if(subindex < 0) { if(subindex < 0) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
in = decode_length(&valuelen, in, last, 8); in = decode_length(&valuelen, in, last, 8);
if(valuelen < 0 || in + valuelen > last) { if(valuelen < 0 || in + valuelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
nv.value = in; nv.value = in;
nv.namelen = namelen; nv.namelen = namelen;
@ -1021,10 +1045,11 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
if(new_ent) { if(new_ent) {
rv = add_workingset(inflater, new_ent); rv = add_workingset(inflater, new_ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else { } else {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
} else { } else {
/* Literal Header with substitution indexing - indexed name */ /* Literal Header with substitution indexing - indexed name */
@ -1034,20 +1059,24 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
nghttp2_nv nv; nghttp2_nv nv;
in = decode_length(&index, in, last, 6); in = decode_length(&index, in, last, 6);
if(index < 0) { if(index < 0) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
--index; --index;
if(inflater->hd_tablelen <= index) { if(inflater->hd_tablelen <= index) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
ent = inflater->hd_table[index]; ent = inflater->hd_table[index];
in = decode_length(&subindex, in, last, 8); in = decode_length(&subindex, in, last, 8);
if(subindex < 0) { if(subindex < 0) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
in = decode_length(&valuelen, in, last, 8); in = decode_length(&valuelen, in, last, 8);
if(valuelen < 0 || in + valuelen > last) { if(valuelen < 0 || in + valuelen > last) {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
++ent->ref; ++ent->ref;
nv.name = ent->nv.name; nv.name = ent->nv.name;
@ -1063,14 +1092,18 @@ ssize_t nghttp2_hd_inflate_hd(nghttp2_hd_context *inflater,
if(new_ent) { if(new_ent) {
rv = add_workingset(inflater, new_ent); rv = add_workingset(inflater, new_ent);
if(rv < 0) { if(rv < 0) {
return rv; goto fail;
} }
} else { } else {
return NGHTTP2_ERR_HEADER_COMP; rv = NGHTTP2_ERR_HEADER_COMP;
goto fail;
} }
} }
} }
return build_nv_array(inflater, nva_ptr); return build_nv_array(inflater, nva_ptr);
fail:
inflater->bad = 1;
return rv;
} }
int nghttp2_hd_end_headers(nghttp2_hd_context *context) int nghttp2_hd_end_headers(nghttp2_hd_context *context)
@ -1081,19 +1114,17 @@ int nghttp2_hd_end_headers(nghttp2_hd_context *context)
nghttp2_hd_ws_entry *ws_ent = &context->ws[i]; nghttp2_hd_ws_entry *ws_ent = &context->ws[i];
switch(ws_ent->cat) { switch(ws_ent->cat) {
case NGHTTP2_HD_CAT_INDEXED: case NGHTTP2_HD_CAT_INDEXED:
if(ws_ent->indexed.checked == 0 || ws_ent->indexed.entry->ref == 1) { if(ws_ent->indexed.checked) {
--ws_ent->indexed.entry->ref; context->refset[context->refsetlen++] = ws_ent->indexed.entry;
if(ws_ent->indexed.entry->ref == 0) { } else {
if(--ws_ent->indexed.entry->ref == 0) {
nghttp2_hd_entry_free(ws_ent->indexed.entry); nghttp2_hd_entry_free(ws_ent->indexed.entry);
free(ws_ent->indexed.entry); free(ws_ent->indexed.entry);
} }
} else {
context->refset[context->refsetlen++] = ws_ent->indexed.entry;
} }
break; break;
case NGHTTP2_HD_CAT_INDNAME: case NGHTTP2_HD_CAT_INDNAME:
--ws_ent->indname.entry->ref; if(--ws_ent->indname.entry->ref == 0) {
if(ws_ent->indname.entry->ref == 0) {
nghttp2_hd_entry_free(ws_ent->indname.entry); nghttp2_hd_entry_free(ws_ent->indname.entry);
free(ws_ent->indname.entry); free(ws_ent->indname.entry);
} }

View File

@ -113,6 +113,10 @@ typedef struct {
is the sum of length of name/value in hd_table + is the sum of length of name/value in hd_table +
NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */ NGHTTP2_HD_ENTRY_OVERHEAD bytes overhead per each entry. */
uint16_t hd_table_bufsize; uint16_t hd_table_bufsize;
/* If inflate/deflate error occurred, this value is set to 1 and
further invocation of inflate/deflate will fail with
NGHTTP2_ERR_HEADER_COMP. */
uint8_t bad;
} nghttp2_hd_context; } nghttp2_hd_context;
/* /*

View File

@ -51,9 +51,9 @@ void test_nghttp2_hd_deflate(void)
MAKE_NV("hello", "world")}; MAKE_NV("hello", "world")};
nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"), nghttp2_nv nva2[] = {MAKE_NV(":path", "/script.js"),
MAKE_NV(":scheme", "https")}; MAKE_NV(":scheme", "https")};
nghttp2_nv nva3[] = {MAKE_NV(":path", "/style.css"), nghttp2_nv nva3[] = {MAKE_NV("cookie", "k1=v1"),
MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k2=v2"),
MAKE_NV("cookie", "k2=v2")}; MAKE_NV("via", "proxy")};
nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"), nghttp2_nv nva4[] = {MAKE_NV(":path", "/style.css"),
MAKE_NV("cookie", "k1=v1"), MAKE_NV("cookie", "k1=v1"),
MAKE_NV("cookie", "k1=v1")}; MAKE_NV("cookie", "k1=v1")};
@ -116,7 +116,7 @@ void test_nghttp2_hd_deflate(void)
encode duplicates. Only first one is encoded. */ encode duplicates. Only first one is encoded. */
blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva4, blocklen = nghttp2_hd_deflate_hd(&deflater, &buf, &buflen, nv_offset, nva4,
sizeof(nva4)/sizeof(nghttp2_nv)); sizeof(nva4)/sizeof(nghttp2_nv));
CU_ASSERT(blocklen == 0); CU_ASSERT(blocklen > 0);
nghttp2_hd_end_headers(&deflater); nghttp2_hd_end_headers(&deflater);
CU_ASSERT(2 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset, CU_ASSERT(2 == nghttp2_hd_inflate_hd(&inflater, &resnva, buf + nv_offset,