litespeedtech / litespeedtech/ls-qpack

Decoder accepts a Sign bit of 1 when Required Insert Count is 0 (RFC 9204 §4.5.1.2 MUST)

Open
#90 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C
Stars
86
Forks
36
PR merge metrics
No merged PRs in 30d

Description

# 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`:

```c
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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lsqpack.c at the PREFIX_STATE_READ_DELTA_BASE_IDX state and compare the Required Insert Count zero path with RFC 9204 §4.5.1.2. Use the listed byte sequences as reproductions; done means each field section with Sign bit 1 and Required Insert Count 0 is rejected rather than decoded successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
networking
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.