Skip to content

Commit 6e2dd42

Browse files
authored
fix: clamp Extend size hint so HeaderMap reserve cannot overflow (#833)
1 parent 68e0abb commit 6e2dd42

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/header/map.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2133,12 +2133,16 @@ impl<T> Extend<(Option<HeaderName>, T)> for HeaderMap<T> {
21332133
// Reserve the entire hint lower bound if the map is empty.
21342134
// Otherwise reserve half the hint (rounded up), so the map
21352135
// will only resize twice in the worst case.
2136-
let reserve = if self.is_empty() {
2136+
let hint = if self.is_empty() {
21372137
iter.size_hint().0
21382138
} else {
21392139
(iter.size_hint().0 + 1) / 2
21402140
};
21412141

2142+
// Clamp the hint so an over-estimate cannot overflow `reserve`.
2143+
let max_reserve = usable_capacity(MAX_SIZE).saturating_sub(self.entries.len());
2144+
let reserve = hint.min(max_reserve);
2145+
21422146
self.reserve(reserve);
21432147

21442148
// The structure of this is a bit weird, but it is mostly to make the
@@ -2189,12 +2193,16 @@ impl<T> Extend<(HeaderName, T)> for HeaderMap<T> {
21892193
// will only resize twice in the worst case.
21902194
let iter = iter.into_iter();
21912195

2192-
let reserve = if self.is_empty() {
2196+
let hint = if self.is_empty() {
21932197
iter.size_hint().0
21942198
} else {
21952199
(iter.size_hint().0 + 1) / 2
21962200
};
21972201

2202+
// Clamp the hint so an over-estimate cannot overflow `reserve`.
2203+
let max_reserve = usable_capacity(MAX_SIZE).saturating_sub(self.entries.len());
2204+
let reserve = hint.min(max_reserve);
2205+
21982206
self.reserve(reserve);
21992207

22002208
for (k, v) in iter {

tests/header_map.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ fn with_capacity_overflow() {
5555
HeaderMap::<u32>::with_capacity(24_577);
5656
}
5757

58+
#[test]
59+
fn extend_size_hint_above_capacity() {
60+
// A `HeaderMap` may hold more values than the table can index when many
61+
// values are appended under one name, so an exact size hint can exceed the
62+
// largest `reserve` request. Extending must not panic in that case.
63+
let name = HeaderName::from_static("h");
64+
let value = HeaderValue::from_static("0");
65+
let pairs: Vec<(HeaderName, HeaderValue)> =
66+
std::iter::repeat_with(|| (name.clone(), value.clone()))
67+
.take(24_577)
68+
.collect();
69+
70+
let map = HeaderMap::from_iter(pairs);
71+
assert_eq!(map.len(), 24_577);
72+
assert_eq!(map.keys_len(), 1);
73+
}
74+
5875
#[test]
5976
#[should_panic]
6077
fn reserve_overflow() {

0 commit comments

Comments
 (0)