From ebb0d33aaf766cbfa0df2f50deb9a39d4c2aebf3 Mon Sep 17 00:00:00 2001 From: piyush-s15 Date: Fri, 14 Aug 2026 13:45:20 +0600 Subject: [PATCH] fix(statsd source): OBE-10714 avoid panic on non-char-boundary gauge slice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parser::parse's "g" (gauge) branch stripped a leading sign character with a byte-index slice (parts[0][1..]) after checking only whether the first *character* was an ASCII digit. Any gauge value beginning with a multi-byte UTF-8 character (e.g. `±`) made that slice land inside the character's bytes, panicking with "byte index 1 is not a char boundary". In UDP mode (the default statsd config) this panic propagates through handle_errors's catch_unwind to abort_tx to a full Vector process shutdown, so a single unauthenticated UDP datagram (e.g. `x:\xc2\xb11|g`) took down every pipeline sharing the process. Replace the byte-index slice with str::get(1..), which returns None (=> ParseError::Malformed) instead of panicking when the offset isn't on a char boundary. The existing StatsdDeserializer/statsd_udp error paths already handle Malformed without aborting the source, so no other files need to change. Co-Authored-By: Claude Sonnet 5 --- src/sources/statsd/parser.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sources/statsd/parser.rs b/src/sources/statsd/parser.rs index 5afa97d535..4da0bf9c28 100644 --- a/src/sources/statsd/parser.rs +++ b/src/sources/statsd/parser.rs @@ -94,7 +94,10 @@ impl Parser { { parts[0].parse()? } else { - parts[0][1..].parse()? + parts[0] + .get(1..) + .ok_or(ParseError::Malformed("invalid gauge prefix"))? + .parse()? }; match parse_direction(parts[0])? { @@ -423,6 +426,16 @@ mod test { ); } + #[test] + fn gauge_with_multibyte_prefix_does_not_panic() { + // OBE-10714: a gauge value whose first character is a multi-byte UTF-8 codepoint (here + // `±`, U+00B1, 2 bytes) must not panic on a non-char-boundary byte slice. + assert!(matches!( + unsanitized_parse("x:\u{00B1}1|g"), + Err(ParseError::Malformed(_)) + )); + } + #[test] fn sets() { assert_event_data_eq!(