Skip to content

fix(dogstatsd): reject tag_length.end() <= MIN_TAG_LENGTH upfront#1875

Merged
blt merged 4 commits into
mainfrom
blt/fix-dogstatsd-tag-length-wrapper-underflow
May 29, 2026
Merged

fix(dogstatsd): reject tag_length.end() <= MIN_TAG_LENGTH upfront#1875
blt merged 4 commits into
mainfrom
blt/fix-dogstatsd-tag-length-wrapper-underflow

Conversation

@blt
Copy link
Copy Markdown
Collaborator

@blt blt commented May 16, 2026

Summary

The dogstatsd common::tags::Generator wrapper reserves one byte of the on-wire tag_length for the : separator before forwarding to the inner common::tags::Generator, by building Inclusive { min: start, max: end - 1 }. The inner generator requires a non-empty range whose start() >= MIN_TAG_LENGTH = 3.

Because only max is decremented (not min), any range where start == end collapses — the adjusted range becomes Inclusive { N, N - 1 } (min > max). That includes ConfRange::Constant(N) for any N, and Inclusive { min: N, max: N }. The inner generator then rejected the range, and the failure bubbled up as Error::StringGenerate, which drops the message and points callers at pool generation rather than at the tag-length range that was actually at fault.

This change:

  • Generalizes the upfront validation: a tag_length is rejected when end is not strictly greater than both start and MIN_TAG_LENGTH, via a dedicated Error::TagLengthRangeTooNarrow variant that names the offending range. This catches every collapsing case — values at/below the minimum (Constant(3), Inclusive { 3, 3 }) and constant/single-value ranges above the minimum (Constant(4), Inclusive { 100, 100 }).
  • Propagates that variant through the public DogStatsD::new boundary as crate::Error::Validation (message intact), instead of the previous catch-all StringGenerate. Other tag-generator failures keep the existing StringGenerate behaviour.

Generation semantics are intentionally unchanged (no fingerprint churn): collapsing ranges are rejected with a clear error rather than being silently reinterpreted.

Discovered while fuzzing DogStatsD::new from an external rig: the symptom was a StringGenerate failure at specific seeds, with no indication that the root cause was the configured tag-length range collapsing.

Test plan

  • cargo test -p lading-payload — passes, including:
    • wrapper rejection tests: tag_length_constant_at_min_rejected, tag_length_inclusive_min_equals_max_at_min_rejected, tag_length_constant_above_min_rejected, tag_length_inclusive_single_value_above_min_rejected;
    • acceptance test for the smallest valid range: tag_length_at_min_plus_one_accepted;
    • table-driven public-path test over collapsing configs: collapsing_tag_length_surfaces_as_validation_error.
  • cargo clippy -p lading-payload --all-targets — clean.
  • cargo fmt --check — clean.
  • CHANGELOG entry added under Unreleased.

@blt blt requested a review from a team as a code owner May 16, 2026 00:38
The dogstatsd `common::tags::Generator` wrapper subtracts one from
`tag_length.end()` to account for the on-wire `:` separator before
forwarding to the inner generator. The inner generator requires
`start() >= MIN_TAG_LENGTH = 3`. When the caller's `tag_length.end()`
equalled `MIN_TAG_LENGTH` -- e.g. `Constant(3)` or `Inclusive { 3, 3 }` --
the wrapper produced `Inclusive { 3, 2 }` and the inner rejected it on
`min <= max`. That rejection surfaced upstream as `Error::StringGenerate`,
which dropped the underlying message and pointed callers at pool
generation rather than the config range that was actually at fault.

Validate the constraint at the wrapper entrypoint and return a dedicated
`Error::TagLengthEndTooSmall` variant naming the offending value and the
minimum required. Tests cover both the rejection cases and the smallest
accepted `end` value.
@blt blt force-pushed the blt/fix-dogstatsd-tag-length-wrapper-underflow branch from a151d21 to 8f2643f Compare May 28, 2026 23:42
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f2643f332

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lading_payload/src/dogstatsd/common/tags.rs Outdated
blt added 2 commits May 28, 2026 23:51
The dedicated Error::TagLengthEndTooSmall added by the parent commit was
still swallowed at the public DogStatsD::new boundary: MemberGenerator::new
mapped every tags::Generator::new failure to crate::Error::StringGenerate,
so a tag_length of Constant(3) or Inclusive { min: 3, max: 3 } continued to
reach external callers as the misleading StringGenerate.

Match the TagLengthEndTooSmall variant explicitly and convert it to
crate::Error::Validation, preserving its message. Other tag-generator
failures keep the existing StringGenerate behaviour. Adds a regression test
exercising the public path.

Addresses PR review feedback.
The three tag_length boundary tests repeated the same 7-arg
tags::Generator::new call, varying only the tag_length range. Replace the
small_string_pool helper with generator_with_tag_length, which holds every
other argument fixed and returns the construction result, so each test
states only the range it exercises.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3a38d74737

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lading_payload/src/dogstatsd/common/tags.rs Outdated
…subtract

The upfront check only rejected tag_length.end() <= MIN_TAG_LENGTH, but the
wrapper subtracts one from end (not start) when forwarding to the inner
generator. Any range with start == end above the minimum -- Constant(4),
Inclusive { min: 100, max: 100 }, etc. -- therefore still produced an adjusted
Inclusive { min: N, max: N - 1 }, which the inner generator rejected as
InvalidConstruction and MemberGenerator mapped to the misleading StringGenerate.

Generalize the check to reject when end is not strictly greater than both
start and MIN_TAG_LENGTH, renaming the variant to TagLengthRangeTooNarrow to
reflect the broader constraint. Extend the unit tests with the constant-above-min
and single-value cases and make the public-path test table-driven over the
collapsing configs.

Addresses PR review feedback.
@blt blt force-pushed the blt/fix-dogstatsd-tag-length-wrapper-underflow branch from 45fb439 to 509221f Compare May 29, 2026 00:09
Copy link
Copy Markdown
Contributor

@goxberry goxberry left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid.

@blt blt merged commit e98e305 into main May 29, 2026
31 checks passed
@blt blt deleted the blt/fix-dogstatsd-tag-length-wrapper-underflow branch May 29, 2026 05:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants