Decoder accepts a Sign bit of 1 when Required Insert Count is 0 (RFC 9204 §4.5.1.2 MUST)
Version: 2.7.0 (9156770); the relevant code is unchanged back through at least 2.6.x.
Summary
lsqpack_dec_read_header accepts an encoded field section whose prefix has the Delta Base Sign bit set to 1 while the Required Insert Count is 0. RFC 9204 §4.5.1.2 requires:
An endpoint MUST treat a field block with a Sign bit of 1 as invalid if the value of Required Insert Count is less than or equal to the value of Delta Base.
When Required Insert Count is 0, every non-negative Delta Base satisfies RIC <= Delta Base, so a Sign bit of 1 is always invalid. The decoder returns success instead.
Mechanism
In the prefix state machine (lsqpack.c, PREFIX_STATE_READ_DELTA_BASE_IDX), the Sign bit is only consulted when a dynamic-table reference was signalled (HBRC_LARGEST_REF_SET). With Required Insert Count 0 the else branch runs and sets the base unconditionally, discarding DELB.sign:
case PREFIX_STATE_READ_DELTA_BASE_IDX:
r = lsqpack_dec_int(&buf, end, prefix_bits, &DELB.value, &DELB.dec_int_state);
if (r == 0)
{
if (read_ctx->hbrc_flags & HBRC_LARGEST_REF_SET)
{
if (DELB.sign)
read_ctx->hbrc_base_index =
ID_MINUS(read_ctx->hbrc_largest_ref, DELB.value + 1);
else
read_ctx->hbrc_base_index =
ID_PLUS(read_ctx->hbrc_largest_ref, DELB.value);
}
else /* Sign bit is never checked here */
read_ctx->hbrc_base_index = 0;
...
Reproduction
Each of these field sections is accepted (verified with the decoder returning :scheme: https); RFC 9204 §4.5.1.2 requires each to be rejected. Byte 1 is Required Insert Count = 0; byte 2 has the Sign bit (0x80) set with the low 7 bits carrying Delta Base; the remainder is a well-formed representation (0xd7 = Indexed Field Line, static table entry 23, :scheme: https).
00 80 d7
00 81 d7
00 87 d7
00 fe d7
00 ff 00 d7 (Delta Base spelled with a continuation octet)
00 ff 49 d7
Note
Found by a differential decoding oracle run against a third-party HTTP/3 implementation; every other divergence it surfaced adjudicated in ls-qpack's favor or was a deliberate layering choice on our side. This is the one that came out the other way. Reporting it upstream as a courtesy — we do not depend on ls-qpack directly.
Decoder accepts a Sign bit of 1 when Required Insert Count is 0 (RFC 9204 §4.5.1.2 MUST)
Version: 2.7.0 (
9156770); the relevant code is unchanged back through at least 2.6.x.Summary
lsqpack_dec_read_headeraccepts an encoded field section whose prefix has the Delta Base Sign bit set to 1 while the Required Insert Count is 0. RFC 9204 §4.5.1.2 requires:When Required Insert Count is 0, every non-negative Delta Base satisfies
RIC <= Delta Base, so a Sign bit of 1 is always invalid. The decoder returns success instead.Mechanism
In the prefix state machine (
lsqpack.c,PREFIX_STATE_READ_DELTA_BASE_IDX), the Sign bit is only consulted when a dynamic-table reference was signalled (HBRC_LARGEST_REF_SET). With Required Insert Count 0 theelsebranch runs and sets the base unconditionally, discardingDELB.sign:Reproduction
Each of these field sections is accepted (verified with the decoder returning
:scheme: https); RFC 9204 §4.5.1.2 requires each to be rejected. Byte 1 is Required Insert Count = 0; byte 2 has the Sign bit (0x80) set with the low 7 bits carrying Delta Base; the remainder is a well-formed representation (0xd7= Indexed Field Line, static table entry 23,:scheme: https).Note
Found by a differential decoding oracle run against a third-party HTTP/3 implementation; every other divergence it surfaced adjudicated in ls-qpack's favor or was a deliberate layering choice on our side. This is the one that came out the other way. Reporting it upstream as a courtesy — we do not depend on ls-qpack directly.