From efd1ebc7f2530af7a1e86580e9f01d1e46b5cac7 Mon Sep 17 00:00:00 2001 From: digi-scrypt Date: Fri, 5 Jun 2026 20:46:21 +0530 Subject: [PATCH 1/4] check snappy block length before crc trailer in decode_snappy --- lang/c/src/codec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lang/c/src/codec.c b/lang/c/src/codec.c index 176fb21d657..fa926628ebe 100644 --- a/lang/c/src/codec.c +++ b/lang/c/src/codec.c @@ -142,6 +142,11 @@ static int decode_snappy(avro_codec_t c, void * data, int64_t len) uint32_t crc; size_t outlen; + if (len < 4) { + avro_set_error("Snappy block is too small to contain a CRC32 checksum"); + return 1; + } + if (snappy_uncompressed_length((const char*)data, len-4, &outlen) != SNAPPY_OK) { avro_set_error("Uncompressed length error in snappy"); return 1; From afda7979d52c70bf633d51ab3108091504a61ce9 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Mon, 22 Jun 2026 19:43:06 +0530 Subject: [PATCH 2/4] add test for undersized snappy blocks and fix guard indentation --- lang/c/src/codec.c | 4 +- lang/c/tests/CMakeLists.txt | 1 + lang/c/tests/test_avro_3807.c | 87 +++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 lang/c/tests/test_avro_3807.c diff --git a/lang/c/src/codec.c b/lang/c/src/codec.c index fa926628ebe..182aff57ec3 100644 --- a/lang/c/src/codec.c +++ b/lang/c/src/codec.c @@ -143,8 +143,8 @@ static int decode_snappy(avro_codec_t c, void * data, int64_t len) size_t outlen; if (len < 4) { - avro_set_error("Snappy block is too small to contain a CRC32 checksum"); - return 1; + avro_set_error("Snappy block is too small to contain a CRC32 checksum"); + return 1; } if (snappy_uncompressed_length((const char*)data, len-4, &outlen) != SNAPPY_OK) { diff --git a/lang/c/tests/CMakeLists.txt b/lang/c/tests/CMakeLists.txt index 6b4164fa740..b137a6c2c6e 100644 --- a/lang/c/tests/CMakeLists.txt +++ b/lang/c/tests/CMakeLists.txt @@ -88,3 +88,4 @@ add_avro_test_checkmem(test_avro_1691) add_avro_test_checkmem(test_avro_1906) add_avro_test_checkmem(test_avro_1904) add_avro_test_checkmem(test_avro_4246) +add_avro_test_checkmem(test_avro_3807) diff --git a/lang/c/tests/test_avro_3807.c b/lang/c/tests/test_avro_3807.c new file mode 100644 index 00000000000..f5d85de2539 --- /dev/null +++ b/lang/c/tests/test_avro_3807.c @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to you under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "avro.h" +#include "avro_private.h" +#include "codec.h" +#include +#include +#include + +/* + * A snappy block carries a four byte CRC32 trailer, so anything shorter + * than that cannot be a valid block. The length comes straight from the + * container file and decode_snappy used to compute len - 4 before checking + * it, which underflows for blocks of 0 to 3 bytes and reads out of bounds. + * Make sure those undersized blocks are rejected rather than decoded. + */ + +int main(void) +{ +#ifdef SNAPPY_CODEC + int i; + + avro_codec_t codec = (avro_codec_t) avro_new(struct avro_codec_t_); + if (codec == NULL) { + fprintf(stderr, "Cannot allocate codec\n"); + return EXIT_FAILURE; + } + + if (avro_codec(codec, "snappy") != 0) { + fprintf(stderr, "Cannot create snappy codec: %s\n", + avro_strerror()); + avro_freet(struct avro_codec_t_, codec); + return EXIT_FAILURE; + } + + /* + * Use an exact sized heap buffer for every length so a checker such as + * valgrind catches any read past it. The bytes have the varint + * continuation bit set so that, without the guard, the underflowed + * len-4 lets snappy keep reading the length prefix off the end of the + * block. + */ + for (i = 0; i < 4; i++) { + size_t size = (i == 0) ? 1 : (size_t) i; + char *buf = (char *) avro_malloc(size); + if (buf == NULL) { + avro_codec_reset(codec); + avro_freet(struct avro_codec_t_, codec); + return EXIT_FAILURE; + } + memset(buf, 0xff, size); + + if (avro_codec_decode(codec, buf, i) == 0) { + fprintf(stderr, + "snappy block of %d bytes should be rejected\n", + i); + avro_free(buf, size); + avro_codec_reset(codec); + avro_freet(struct avro_codec_t_, codec); + return EXIT_FAILURE; + } + + avro_free(buf, size); + } + + avro_codec_reset(codec); + avro_freet(struct avro_codec_t_, codec); +#else + fprintf(stderr, "Snappy codec not available, skipping\n"); +#endif + return EXIT_SUCCESS; +} From e8386ac94d37949c7c272a3b92f37c5c7d1ebc05 Mon Sep 17 00:00:00 2001 From: Martin Grigorov Date: Mon, 22 Jun 2026 18:25:28 +0300 Subject: [PATCH 3/4] Fix formatting of error handling in codec.c Use one tab for indentation as the rest of the file --- lang/c/src/codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/c/src/codec.c b/lang/c/src/codec.c index 182aff57ec3..8bc1acc193d 100644 --- a/lang/c/src/codec.c +++ b/lang/c/src/codec.c @@ -143,8 +143,8 @@ static int decode_snappy(avro_codec_t c, void * data, int64_t len) size_t outlen; if (len < 4) { - avro_set_error("Snappy block is too small to contain a CRC32 checksum"); - return 1; + avro_set_error("Snappy block is too small to contain a CRC32 checksum"); + return 1; } if (snappy_uncompressed_length((const char*)data, len-4, &outlen) != SNAPPY_OK) { From 844692bfa038cbf5196f15d2c9b9b8debc9c1710 Mon Sep 17 00:00:00 2001 From: dxbjavid Date: Tue, 23 Jun 2026 19:17:17 +0530 Subject: [PATCH 4/4] reject snappy block length above SIZE_MAX before decode --- lang/c/src/codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/c/src/codec.c b/lang/c/src/codec.c index 8bc1acc193d..6d37899161f 100644 --- a/lang/c/src/codec.c +++ b/lang/c/src/codec.c @@ -142,7 +142,7 @@ static int decode_snappy(avro_codec_t c, void * data, int64_t len) uint32_t crc; size_t outlen; - if (len < 4) { + if (len < 4 || (uint64_t)len > SIZE_MAX) { avro_set_error("Snappy block is too small to contain a CRC32 checksum"); return 1; }