Skip to content

HeaderMap::extend can panic before consuming a malformed iterator #845

Description

@xc01

I found one http panic path that still looks worth mentioning as a small robustness hardening issue.

This depends on a custom iterator that reports an impossible size_hint lower bound. That is not a normal iterator, so I would not frame this as a typical application-input bug. Still, it is reachable from safe code through the public HeaderMap::extend API, and the panic happens before any item is consumed.

I checked the nearby comments/docs. The adjacent comment explains the reserve heuristic, but I did not find a # Panics note for this Extend<(HeaderName, T)> implementation covering an overflowing size_hint.

Version checked: http 1.4.0

Main example

Area Panic site Trigger
HeaderMap::extend src/header/map.rs:2194 A non-empty map is extended with an iterator whose lower-bound size_hint() is usize::MAX, so (hint + 1) / 2 overflows.

Relevant code:

impl<T> Extend<(HeaderName, T)> for HeaderMap<T> {
    fn extend<I: IntoIterator<Item = (HeaderName, T)>>(&mut self, iter: I) {
        let iter = iter.into_iter();

        let reserve = if self.is_empty() {
            iter.size_hint().0
        } else {
            (iter.size_hint().0 + 1) / 2
        };

        self.reserve(reserve);

        for (k, v) in iter {
            self.append(k, v);
        }
    }
}

Minimal reproducer shape:

use http::header::{HeaderName, HeaderValue};
use http::HeaderMap;

struct BadHintIter;

impl Iterator for BadHintIter {
    type Item = (HeaderName, HeaderValue);

    fn next(&mut self) -> Option<Self::Item> {
        None
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::MAX, None)
    }
}

#[test]
fn extend_overflowing_size_hint_panics() {
    let result = std::panic::catch_unwind(|| {
        let mut map = HeaderMap::new();
        map.insert("x-seed", HeaderValue::from_static("1"));

        map.extend(BadHintIter);
    });

    assert!(result.is_err());
}

Actual behavior:

Debug/test builds panic with integer overflow at src/header/map.rs:2194.

Expected behavior:

Either avoid the unchecked addition, or document that HeaderMap::extend assumes a well-formed iterator size_hint.

Possible fix direction:

let lower = iter.size_hint().0;
let reserve = if self.is_empty() {
    lower
} else {
    lower.saturating_add(1) / 2
};

or use checked_add and fall back to MAX_SIZE/try_reserve behavior.

Generated cases I would not lead with

Authority::from_static

The generated tests also hit invalid static authority strings such as non-ASCII authority text and "@".

This is already documented:

/// # Panics
///
/// This function panics if the argument contains invalid characters or
/// is empty.
pub const fn from_static(src: &'static str) -> Self

I would not report these as undocumented panics.

HeaderName::from_static

Invalid static header names such as uppercase names or names containing { are also already documented:

/// # Panics
///
/// This function panics when the static string is a invalid header.
pub const fn from_static(src: &'static str) -> HeaderName

The docs even include should_panic examples, so I would not include this as a robustness report.

HeaderValue::from_static

Invalid static header values are documented too:

/// # Panics
///
/// This function panics if the argument contains invalid header value
/// characters.
pub const fn from_static(src: &'static str) -> HeaderValue

Entry::or_insert_with at max capacity

This panic is real and reproducible when the map is already at max capacity, but the Entry docs already say:

/// # Panics
///
/// This method panics if capacity exceeds max `HeaderMap` capacity

I would only mention it as related behavior if maintainers ask about capacity-limit handling.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions