Skip to content

Commit 8cfb02c

Browse files
committed
Restricte variable integer decoding to uint32 to prevent run-away computation
1 parent 77e36a9 commit 8cfb02c

3 files changed

Lines changed: 23 additions & 1 deletion

File tree

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ dev
1717

1818
- Headers marked as `sensitive` will no longer log their value at DEBUG level. Instead a placeholder value of `SENSITIVE_REDACTED` is logged.
1919
- Fixed perfect match missed for headers with empty values.
20+
- Restricted variable integer decoding to uint32 to prevent run-away computation. With thanks to `Hiroki Nishino`_.
2021

22+
.. _Hiroki Nishino: https://github.com/tawAsh1
2123

2224
4.1.0 (2025-01-22)
2325
------------------

src/hpack/hpack.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
INDEX_NEVER = b"\x10"
2323
INDEX_INCREMENTAL = b"\x40"
2424

25+
VARINT_MAX_LENGTH = 5 # octets, enough for encoding prefix + uint32
26+
2527
# Precompute 2^i for 1-8 for use in prefix calcs.
2628
# Zero index is not used but there to save a subtraction
2729
# as prefix numbers are not zero indexed.
@@ -106,6 +108,17 @@ def decode_integer(data: bytes | memoryview, prefix_bits: int) -> tuple[int, int
106108
break
107109
shift += 7
108110

111+
if index > VARINT_MAX_LENGTH: # sanity check to prevent infinite loops
112+
# We have consumed more than enough bytes for a typical unsigned integer.
113+
# The maximum size is not defined in HPACK RFC7541 Section 5.1:
114+
# > This integer representation allows for values of indefinite size. It
115+
# > is also possible for an encoder to send a large number of zero
116+
# > values, which can waste octets and could be used to overflow integer
117+
# > values. Integer encodings that exceed implementation limits -- in
118+
# > value or octet length -- MUST be treated as decoding errors.
119+
msg = f"Variable integer representation is too long: {data!r}"
120+
raise HPACKDecodingError(msg)
121+
109122
except IndexError as err:
110123
msg = f"Unable to decode HPACK integer representation from {data!r}"
111124
raise HPACKDecodingError(msg) from err

tests/test_encode_decode.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def test_decode_either_succeeds_or_raises_error(self, data, prefix_bits):
125125
assert consumed > 0
126126

127127
@given(
128-
integer=integers(min_value=0),
128+
integer=integers(min_value=0, max_value=2**32),
129129
prefix_bits=integers(min_value=1, max_value=8)
130130
)
131131
def test_encode_decode_round_trips(self, integer, prefix_bits):
@@ -138,3 +138,10 @@ def test_encode_decode_round_trips(self, integer, prefix_bits):
138138
)
139139
assert integer == decoded_integer
140140
assert consumed > 0
141+
142+
def test_decode_too_long_fails(self):
143+
"""
144+
If the variable integer representation is too long, the decoder fails.
145+
"""
146+
with pytest.raises(HPACKDecodingError):
147+
decode_integer(b'\xff\xff\xff\xff\xff\xff', 5)

0 commit comments

Comments
 (0)