fix(dogstatsd): reject tag_length.end() <= MIN_TAG_LENGTH upfront#1875
Conversation
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.
a151d21 to
8f2643f
Compare
There was a problem hiding this comment.
💡 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".
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.
There was a problem hiding this comment.
💡 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".
…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.
45fb439 to
509221f
Compare
Summary
The dogstatsd
common::tags::Generatorwrapper reserves one byte of the on-wiretag_lengthfor the:separator before forwarding to the innercommon::tags::Generator, by buildingInclusive { min: start, max: end - 1 }. The inner generator requires a non-empty range whosestart() >= MIN_TAG_LENGTH = 3.Because only
maxis decremented (notmin), any range wherestart == endcollapses — the adjusted range becomesInclusive { N, N - 1 }(min > max). That includesConfRange::Constant(N)for anyN, andInclusive { min: N, max: N }. The inner generator then rejected the range, and the failure bubbled up asError::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:
tag_lengthis rejected whenendis not strictly greater than bothstartandMIN_TAG_LENGTH, via a dedicatedError::TagLengthRangeTooNarrowvariant 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 }).DogStatsD::newboundary ascrate::Error::Validation(message intact), instead of the previous catch-allStringGenerate. Other tag-generator failures keep the existingStringGeneratebehaviour.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::newfrom an external rig: the symptom was aStringGeneratefailure 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: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;tag_length_at_min_plus_one_accepted;collapsing_tag_length_surfaces_as_validation_error.cargo clippy -p lading-payload --all-targets— clean.cargo fmt --check— clean.Unreleased.