From 7800ae2e2bbcd732461b0fc467939462591e91f9 Mon Sep 17 00:00:00 2001 From: Guillermo Facundo Colunga Date: Fri, 13 Mar 2026 12:07:42 +0100 Subject: [PATCH 1/2] snappy: fix raw block data misidentified as framed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The framed/streaming snappy format starts with a stream identifier frame whose first byte is 0xFF. The previous check only tested this single byte, but raw/block snappy data can also have 0xFF as its first byte when the varint-encoded uncompressed length happens to satisfy (size % 128 == 127 && size >= 128). This caused intermittent decompression failures for Prometheus Remote Write payloads (which use raw snappy) — roughly 1 in 128 requests would be misrouted into the framed parser and rejected. Validate the full 10-byte stream identifier header (0xFF + 3-byte length 0x000006 + "sNaPpY") instead of just the first byte. Signed-off-by: Guillermo Facundo Colunga Co-Authored-By: Claude Opus 4.6 (1M context) --- src/flb_snappy.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/flb_snappy.c b/src/flb_snappy.c index c4c16b0ade6..089ae3b15c5 100644 --- a/src/flb_snappy.c +++ b/src/flb_snappy.c @@ -29,6 +29,8 @@ #include +#include + int flb_snappy_compress(char *in_data, size_t in_len, char **out_data, size_t *out_len) { @@ -139,7 +141,25 @@ int flb_snappy_uncompress_framed_data(char *in_data, size_t in_len, struct cfl_list chunks; struct flb_snappy_data_chunk *chunk; - if (*((uint8_t *) in_data) != FLB_SNAPPY_FRAME_TYPE_STREAM_IDENTIFIER) { + /* + * Distinguish raw/block snappy from the framed/streaming format. + * + * The framed format always starts with a stream identifier frame: + * byte 0: 0xFF (stream identifier chunk type) + * bytes 1-3: 0x06 0x00 0x00 (length = 6, little-endian 24-bit) + * bytes 4-9: "sNaPpY" + * + * A single first-byte check is insufficient because raw snappy data + * encodes the uncompressed length as a varint whose first byte can + * be 0xFF (e.g. when the uncompressed size mod 128 == 127 and >= 128). + * Checking the full 10-byte header avoids this false positive. + */ + if (in_len < 10 || + *((uint8_t *) &in_data[0]) != 0xFF || + *((uint8_t *) &in_data[1]) != 0x06 || + *((uint8_t *) &in_data[2]) != 0x00 || + *((uint8_t *) &in_data[3]) != 0x00 || + memcmp(&in_data[4], FLB_SNAPPY_STREAM_IDENTIFIER_STRING, 6) != 0) { return flb_snappy_uncompress(in_data, in_len, out_data, out_len); } From d1b8f034f385b68056674f818c1e334780c33022 Mon Sep 17 00:00:00 2001 From: Guillermo Facundo Colunga Date: Fri, 13 Mar 2026 17:05:52 +0100 Subject: [PATCH 2/2] snappy: fix multi-chunk framed data silently dropped MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After the frame parsing loop accumulates the total decompressed length in aggregated_data_length, the variable was incorrectly reset to zero before being used for the output buffer allocation. This caused the multi-chunk code path to skip allocation entirely, producing a NULL output buffer and zero length with a success return code — silently dropping all decompressed data. Remove the erroneous reset so the accumulated length is preserved and used for the aggregation buffer allocation. Also gate the allocation on result == 0 and free any allocated buffer on error, preventing a memory leak when a multi-chunk stream fails mid-way (e.g. checksum mismatch after one valid chunk). Signed-off-by: Guillermo Facundo Colunga Co-Authored-By: Claude Opus 4.6 (1M context) --- src/flb_snappy.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/flb_snappy.c b/src/flb_snappy.c index 089ae3b15c5..81922321ac1 100644 --- a/src/flb_snappy.c +++ b/src/flb_snappy.c @@ -302,7 +302,6 @@ int flb_snappy_uncompress_framed_data(char *in_data, size_t in_len, } aggregated_data_buffer = NULL; - aggregated_data_length = 0; if (compressed_chunk_count == 1 && uncompressed_chunk_count == 0 && @@ -322,7 +321,7 @@ int flb_snappy_uncompress_framed_data(char *in_data, size_t in_len, flb_free(chunk); } else { - if (aggregated_data_length > 0) { + if (aggregated_data_length > 0 && result == 0) { aggregated_data_buffer = flb_calloc(aggregated_data_length, sizeof(char)); @@ -357,6 +356,12 @@ int flb_snappy_uncompress_framed_data(char *in_data, size_t in_len, } } + if (result != 0) { + flb_free(aggregated_data_buffer); + aggregated_data_buffer = NULL; + aggregated_data_offset = 0; + } + *out_data = (char *) aggregated_data_buffer; *out_len = aggregated_data_offset;