Skip to content

W3C Baggage extract has no size or entry-count cap #992

@tonghuaroot

Description

@tonghuaroot

Summary

otel_propagator_baggage:extract/5 walks the inbound baggage HTTP header through string:lexemes(String, [$,]) and lists:foldl/3 without any byte or entry-count cap. Each entry is percent-decoded into a fresh binary and inserted into the accumulator map. The W3C Baggage specification recommends 8192 bytes and 180 entries; this propagator enforces neither.

This is a spec-compliance bug and a BEAM hardening item: an oversized baggage header (legitimately malformed or not) causes the calling process to spend an unbounded amount of scheduler time and per-process heap on the parse. The default propagator configuration {text_map_propagators, [trace_context, baggage]} registers otel_propagator_baggage on every instrumented HTTP entry point, so any Cowboy/Elli/Mochiweb service with OpenTelemetry trace propagation enabled inherits the same shape.

The maintainers reviewed this off-list as GHSA-64w2-whjg-q7q7 and declined to treat it as a security advisory, but asked for it to be opened as a regular issue/PR (https://github.com/open-telemetry/opentelemetry-erlang/security/advisories/GHSA-64w2-whjg-q7q7).

Reference: other OpenTelemetry SDKs already enforce these caps

SDK File Byte cap Entry cap
Java SDK 1.62.0 W3CBaggagePropagator 8192 180
Go propagation/baggage.go 8192 (maxBytes) 64 (maxMembers)
.NET BaggagePropagator.cs 8192 (MaxBaggageLength) 180 (MaxBaggageItems)
C++ baggage.h FromHeader 8192 (kMaxSize) 180 (kMaxKeyValuePairs)
Erlang otel_propagator_baggage.erl none none

Current code

apps/opentelemetry_api/src/otel_propagator_baggage.erl lines 81-94:

extract(Ctx, Carrier, _CarrierKeysFun, CarrierGet, _Options) ->
    case CarrierGet(?BAGGAGE_HEADER, Carrier) of
        undefined ->
            Ctx;
        String ->
            Pairs = string:lexemes(String, [$,]),
            DecodedBaggage =
                lists:foldl(fun(Pair, Acc) ->
                                    [Key, Value] = string:split(Pair, "="),
                                    Acc#{decode_key(Key) => decode_value(Value)}
                            end, #{}, Pairs),
            otel_baggage:set_to(Ctx, DecodedBaggage)
    end.

string:lexemes(String, [$,]) allocates the full list of pair-binaries up front, lists:foldl then walks every pair and calls decode_key / decode_value (each builds a fresh binary), and the accumulator map keeps growing. None of byte_size(String), length(Pairs), or accumulator size is bounded. The [Key, Value] = string:split(...) match is also non-exhaustive: a single malformed pair (no =) crashes the extract.

Observed behavior

Tested against opentelemetry_api 1.4.0 installed from Hex.pm into a fresh rebar3 project on Erlang/OTP 27 erts-15.2.7.8:

N=1000     (9.8 KB header)      extract: 35.7 ms       mem 426 KB
N=50000    (660 KB header)      extract: 33.6 seconds  mem 22 MB
N=100000   (1.4 MB header)      no output before 120s timeout

A single header thus consumes tens of seconds of scheduler time and tens of megabytes of per-process heap; repeated requests scale that linearly. Wrapping the call in a separate process does not help because the work is CPU-bound inside lists:foldl.

Suggested fix

Mirror the Java SDK 1.62.0 caps (MAX_BAGGAGE_BYTES = 8192, MAX_BAGGAGE_ENTRIES = 180):

  • Reject headers larger than 8192 bytes before string:lexemes/2 walks them.
  • Stop decoding after 180 entries regardless of how many pairs lexemes produced.
  • While there, replace the non-exhaustive [Key, Value] = string:split(Pair, "=") match with a case that skips malformed pairs instead of crashing the extract.

PR with that change and a otel_propagator_baggage_SUITE covering both caps is filed alongside this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions