From 271de632302fc4cf25d82204a252b4e19bc474d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Mej=C3=ADa?= Date: Sat, 11 Jul 2026 10:07:05 +0200 Subject: [PATCH 1/5] AVRO-4275: [C] Fix signed integer overflow in block_count negation read_array_value() and read_map_value() negate block_count via multiplication by -1, which is undefined behavior for values that cannot be represented after negation. Replace with the overflow-safe -(x+1)+1 idiom (consistent with the C++ fix in AVRO-4228) and reject invalid results early. Add test_avro_4275 covering the overflow case and verifying that legitimate negative block counts still decode correctly. Assisted-by: OpenCode:claude-opus-4.6 --- lang/c/src/value-read.c | 20 +- lang/c/tests/CMakeLists.txt | 1 + lang/c/tests/test_avro_4275.c | 377 ++++++++++++++++++++++++++++++++++ 3 files changed, 396 insertions(+), 2 deletions(-) create mode 100644 lang/c/tests/test_avro_4275.c diff --git a/lang/c/src/value-read.c b/lang/c/src/value-read.c index b6b6e79fadd..68ffc5795c2 100644 --- a/lang/c/src/value-read.c +++ b/lang/c/src/value-read.c @@ -53,7 +53,15 @@ read_array_value(avro_reader_t reader, avro_value_t *dest) while (block_count != 0) { if (block_count < 0) { - block_count = block_count * -1; + /* Safe negation: avoid undefined behavior when + * block_count == INT64_MIN, since -INT64_MIN is not + * representable in int64_t (CWE-190). Use the + * -(x+1)+1 idiom to negate without overflow. */ + block_count = -(block_count + 1) + 1; + if (block_count <= 0) { + avro_set_error("Invalid array block count"); + return EINVAL; + } check_prefix(rval, avro_binary_encoding. read_long(reader, &block_size), "Cannot read array block size: "); @@ -89,7 +97,15 @@ read_map_value(avro_reader_t reader, avro_value_t *dest) while (block_count != 0) { if (block_count < 0) { - block_count = block_count * -1; + /* Safe negation: avoid undefined behavior when + * block_count == INT64_MIN, since -INT64_MIN is not + * representable in int64_t (CWE-190). Use the + * -(x+1)+1 idiom to negate without overflow. */ + block_count = -(block_count + 1) + 1; + if (block_count <= 0) { + avro_set_error("Invalid map block count"); + return EINVAL; + } check_prefix(rval, avro_binary_encoding. read_long(reader, &block_size), "Cannot read map block size: "); diff --git a/lang/c/tests/CMakeLists.txt b/lang/c/tests/CMakeLists.txt index 6b4164fa740..33cb34c2852 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_4275) diff --git a/lang/c/tests/test_avro_4275.c b/lang/c/tests/test_avro_4275.c new file mode 100644 index 00000000000..5a15cabee6b --- /dev/null +++ b/lang/c/tests/test_avro_4275.c @@ -0,0 +1,377 @@ +/* + * 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. + */ + +/** + * Regression test for INT64_MIN negation overflow in read_array_value() + * and read_map_value() (CWE-190). + * + * The Avro binary format encodes block counts as zigzag-encoded varints. + * A negative block count means the absolute value is the actual count, + * preceded by a byte-size field. When block_count == INT64_MIN, the + * negation overflows (undefined behavior in C). This test verifies that + * the decoder rejects such malformed input gracefully. + */ + +#include +#include +#include +#include +#include + +/* + * Zigzag encoding of INT64_MIN is 0xFFFFFFFFFFFFFFFF, which encodes as + * the 10-byte varint: FF FF FF FF FF FF FF FF FF 01 + */ +static const char int64min_block_count[] = { + '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', + '\xFF', '\xFF', '\xFF', '\xFF', '\x01' +}; + +/* + * A valid array with negative block count: [-3] means 3 items follow, + * preceded by the block byte-size. + * + * Layout: + * 05 = varint 5 = zigzag(-3) => block_count = -3 + * 06 = varint 6 = zigzag(3) => block_size = 3 bytes + * 14 28 3C = zigzag ints: 10, 20, 30 + * 00 = terminator (block_count = 0) + */ +static const char valid_neg_block_array[] = { + '\x05', /* block_count = -3 */ + '\x06', /* block_size = 3 */ + '\x14', '\x28', '\x3C', /* ints: 10, 20, 30 */ + '\x00' /* terminator */ +}; + +/* + * A valid map with negative block count: [-2] means 2 entries follow. + * + * Layout: + * 03 = varint 3 = zigzag(-2) => block_count = -2 + * 0E = varint 14 = zigzag(7) => block_size = 7 bytes + * 02 61 14 = key "a" (len=1, 'a'), value int 10 + * 02 62 28 = key "b" (len=1, 'b'), value int 20 + * 00 = terminator + */ +static const char valid_neg_block_map[] = { + '\x03', /* block_count = -2 */ + '\x0E', /* block_size = 7 */ + '\x02', '\x61', '\x14', /* key "a", value 10 */ + '\x02', '\x62', '\x28', /* key "b", value 20 */ + '\x00' /* terminator */ +}; + +static int test_array_int64min(void) +{ + int rc; + avro_schema_t schema = NULL; + avro_value_iface_t *iface = NULL; + avro_value_t value; + avro_reader_t reader = NULL; + + rc = avro_schema_from_json_literal( + "{\"type\":\"array\",\"items\":\"int\"}", &schema); + if (rc != 0) { + fprintf(stderr, "Failed to parse array schema: %s\n", + avro_strerror()); + return 1; + } + + iface = avro_generic_class_from_schema(schema); + if (iface == NULL) { + fprintf(stderr, "Failed to create array iface\n"); + avro_schema_decref(schema); + return 1; + } + + rc = avro_generic_value_new(iface, &value); + if (rc != 0) { + fprintf(stderr, "Failed to create array value: %s\n", + avro_strerror()); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + reader = avro_reader_memory(int64min_block_count, + sizeof(int64min_block_count)); + if (reader == NULL) { + fprintf(stderr, "Failed to create reader\n"); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + /* This MUST fail gracefully (return error) rather than looping + * unboundedly or triggering undefined behavior. */ + rc = avro_value_read(reader, &value); + if (rc == 0) { + fprintf(stderr, + "FAIL: INT64_MIN array block count was not rejected\n"); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + printf("PASS: INT64_MIN array block count rejected: %s\n", + avro_strerror()); + + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 0; +} + +static int test_map_int64min(void) +{ + int rc; + avro_schema_t schema = NULL; + avro_value_iface_t *iface = NULL; + avro_value_t value; + avro_reader_t reader = NULL; + + rc = avro_schema_from_json_literal( + "{\"type\":\"map\",\"values\":\"int\"}", &schema); + if (rc != 0) { + fprintf(stderr, "Failed to parse map schema: %s\n", + avro_strerror()); + return 1; + } + + iface = avro_generic_class_from_schema(schema); + if (iface == NULL) { + fprintf(stderr, "Failed to create map iface\n"); + avro_schema_decref(schema); + return 1; + } + + rc = avro_generic_value_new(iface, &value); + if (rc != 0) { + fprintf(stderr, "Failed to create map value: %s\n", + avro_strerror()); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + reader = avro_reader_memory(int64min_block_count, + sizeof(int64min_block_count)); + if (reader == NULL) { + fprintf(stderr, "Failed to create reader\n"); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + /* This MUST fail gracefully. */ + rc = avro_value_read(reader, &value); + if (rc == 0) { + fprintf(stderr, + "FAIL: INT64_MIN map block count was not rejected\n"); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + printf("PASS: INT64_MIN map block count rejected: %s\n", + avro_strerror()); + + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 0; +} + +static int test_valid_neg_block_array(void) +{ + int rc; + size_t count; + avro_schema_t schema = NULL; + avro_value_iface_t *iface = NULL; + avro_value_t value; + avro_reader_t reader = NULL; + + rc = avro_schema_from_json_literal( + "{\"type\":\"array\",\"items\":\"int\"}", &schema); + if (rc != 0) { + fprintf(stderr, "Failed to parse array schema: %s\n", + avro_strerror()); + return 1; + } + + iface = avro_generic_class_from_schema(schema); + if (iface == NULL) { + fprintf(stderr, "Failed to create array iface\n"); + avro_schema_decref(schema); + return 1; + } + + rc = avro_generic_value_new(iface, &value); + if (rc != 0) { + fprintf(stderr, "Failed to create array value: %s\n", + avro_strerror()); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + reader = avro_reader_memory(valid_neg_block_array, + sizeof(valid_neg_block_array)); + if (reader == NULL) { + fprintf(stderr, "Failed to create reader\n"); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + rc = avro_value_read(reader, &value); + if (rc != 0) { + fprintf(stderr, + "FAIL: valid negative block count array rejected: %s\n", + avro_strerror()); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + rc = avro_value_get_size(&value, &count); + if (rc != 0 || count != 3) { + fprintf(stderr, + "FAIL: expected 3 elements, got %zu (rc=%d)\n", + count, rc); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + printf("PASS: valid negative block count array decoded (%zu items)\n", + count); + + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 0; +} + +static int test_valid_neg_block_map(void) +{ + int rc; + size_t count; + avro_schema_t schema = NULL; + avro_value_iface_t *iface = NULL; + avro_value_t value; + avro_reader_t reader = NULL; + + rc = avro_schema_from_json_literal( + "{\"type\":\"map\",\"values\":\"int\"}", &schema); + if (rc != 0) { + fprintf(stderr, "Failed to parse map schema: %s\n", + avro_strerror()); + return 1; + } + + iface = avro_generic_class_from_schema(schema); + if (iface == NULL) { + fprintf(stderr, "Failed to create map iface\n"); + avro_schema_decref(schema); + return 1; + } + + rc = avro_generic_value_new(iface, &value); + if (rc != 0) { + fprintf(stderr, "Failed to create map value: %s\n", + avro_strerror()); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + reader = avro_reader_memory(valid_neg_block_map, + sizeof(valid_neg_block_map)); + if (reader == NULL) { + fprintf(stderr, "Failed to create reader\n"); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + rc = avro_value_read(reader, &value); + if (rc != 0) { + fprintf(stderr, + "FAIL: valid negative block count map rejected: %s\n", + avro_strerror()); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + rc = avro_value_get_size(&value, &count); + if (rc != 0 || count != 2) { + fprintf(stderr, + "FAIL: expected 2 map entries, got %zu (rc=%d)\n", + count, rc); + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 1; + } + + printf("PASS: valid negative block count map decoded (%zu entries)\n", + count); + + avro_reader_free(reader); + avro_value_decref(&value); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + return 0; +} + +int main(void) +{ + int failures = 0; + + printf("Testing INT64_MIN block count rejection (CWE-190)...\n\n"); + + failures += test_array_int64min(); + failures += test_map_int64min(); + failures += test_valid_neg_block_array(); + failures += test_valid_neg_block_map(); + + printf("\n%s: %d test(s) failed\n", + failures ? "FAIL" : "OK", failures); + + return failures ? EXIT_FAILURE : EXIT_SUCCESS; +} From e8b6f7459e2080eb4be7f69098cc6c7df810e0f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Mej=C3=ADa?= Date: Sat, 11 Jul 2026 10:17:39 +0200 Subject: [PATCH 2/5] AVRO-4275: Fix incomplete INT64_MIN overflow guard in block_count negation Replace the flawed -(block_count + 1) + 1 idiom (which still overflows on the final + 1 when block_count == INT64_MIN) with an explicit INT64_MIN guard followed by simple negation, in both read_array_value() and read_map_value(). Also fix the block_size in the map test vector from 7 to 6 to match the actual byte count of the encoded entries. Assisted-by: GitHub Copilot:claude-opus-4.6 --- lang/c/src/value-read.c | 20 ++++++++------------ lang/c/tests/test_avro_4275.c | 4 ++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/lang/c/src/value-read.c b/lang/c/src/value-read.c index 68ffc5795c2..b7bc2f7e443 100644 --- a/lang/c/src/value-read.c +++ b/lang/c/src/value-read.c @@ -53,15 +53,13 @@ read_array_value(avro_reader_t reader, avro_value_t *dest) while (block_count != 0) { if (block_count < 0) { - /* Safe negation: avoid undefined behavior when - * block_count == INT64_MIN, since -INT64_MIN is not - * representable in int64_t (CWE-190). Use the - * -(x+1)+1 idiom to negate without overflow. */ - block_count = -(block_count + 1) + 1; - if (block_count <= 0) { + /* Reject INT64_MIN: its negation is not + * representable in int64_t (CWE-190). */ + if (block_count == INT64_MIN) { avro_set_error("Invalid array block count"); return EINVAL; } + block_count = -block_count; check_prefix(rval, avro_binary_encoding. read_long(reader, &block_size), "Cannot read array block size: "); @@ -97,15 +95,13 @@ read_map_value(avro_reader_t reader, avro_value_t *dest) while (block_count != 0) { if (block_count < 0) { - /* Safe negation: avoid undefined behavior when - * block_count == INT64_MIN, since -INT64_MIN is not - * representable in int64_t (CWE-190). Use the - * -(x+1)+1 idiom to negate without overflow. */ - block_count = -(block_count + 1) + 1; - if (block_count <= 0) { + /* Reject INT64_MIN: its negation is not + * representable in int64_t (CWE-190). */ + if (block_count == INT64_MIN) { avro_set_error("Invalid map block count"); return EINVAL; } + block_count = -block_count; check_prefix(rval, avro_binary_encoding. read_long(reader, &block_size), "Cannot read map block size: "); diff --git a/lang/c/tests/test_avro_4275.c b/lang/c/tests/test_avro_4275.c index 5a15cabee6b..ca93caf6c28 100644 --- a/lang/c/tests/test_avro_4275.c +++ b/lang/c/tests/test_avro_4275.c @@ -63,14 +63,14 @@ static const char valid_neg_block_array[] = { * * Layout: * 03 = varint 3 = zigzag(-2) => block_count = -2 - * 0E = varint 14 = zigzag(7) => block_size = 7 bytes + * 0C = varint 12 = zigzag(6) => block_size = 6 bytes * 02 61 14 = key "a" (len=1, 'a'), value int 10 * 02 62 28 = key "b" (len=1, 'b'), value int 20 * 00 = terminator */ static const char valid_neg_block_map[] = { '\x03', /* block_count = -2 */ - '\x0E', /* block_size = 7 */ + '\x0C', /* block_size = 6 */ '\x02', '\x61', '\x14', /* key "a", value 10 */ '\x02', '\x62', '\x28', /* key "b", value 20 */ '\x00' /* terminator */ From 35b0bddda5b5fedea20f515fe315c2721a2d022b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Mej=C3=ADa?= Date: Sat, 11 Jul 2026 10:32:13 +0200 Subject: [PATCH 3/5] AVRO-4275: [C] Strengthen test assertions to check EINVAL Assert that the INT64_MIN block count tests fail with the specific EINVAL error code rather than just any non-zero return. This ensures the tests actually exercise the new guard rather than passing due to an unrelated failure downstream. Addresses review feedback on PR #3842. Assisted-by: GitHub Copilot:claude-opus-4.6 --- lang/c/tests/test_avro_4275.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lang/c/tests/test_avro_4275.c b/lang/c/tests/test_avro_4275.c index ca93caf6c28..f4755853ba2 100644 --- a/lang/c/tests/test_avro_4275.c +++ b/lang/c/tests/test_avro_4275.c @@ -30,6 +30,7 @@ #include #include #include +#include #include /* @@ -118,12 +119,13 @@ static int test_array_int64min(void) return 1; } - /* This MUST fail gracefully (return error) rather than looping - * unboundedly or triggering undefined behavior. */ + /* This MUST fail with EINVAL rather than looping unboundedly or + * triggering undefined behavior from negating INT64_MIN. */ rc = avro_value_read(reader, &value); - if (rc == 0) { + if (rc != EINVAL) { fprintf(stderr, - "FAIL: INT64_MIN array block count was not rejected\n"); + "FAIL: INT64_MIN array block count: expected EINVAL, " + "got rc=%d (%s)\n", rc, avro_strerror()); avro_reader_free(reader); avro_value_decref(&value); avro_value_iface_decref(iface); @@ -131,7 +133,7 @@ static int test_array_int64min(void) return 1; } - printf("PASS: INT64_MIN array block count rejected: %s\n", + printf("PASS: INT64_MIN array block count rejected with EINVAL: %s\n", avro_strerror()); avro_reader_free(reader); @@ -183,11 +185,13 @@ static int test_map_int64min(void) return 1; } - /* This MUST fail gracefully. */ + /* This MUST fail with EINVAL rather than looping unboundedly or + * triggering undefined behavior from negating INT64_MIN. */ rc = avro_value_read(reader, &value); - if (rc == 0) { + if (rc != EINVAL) { fprintf(stderr, - "FAIL: INT64_MIN map block count was not rejected\n"); + "FAIL: INT64_MIN map block count: expected EINVAL, " + "got rc=%d (%s)\n", rc, avro_strerror()); avro_reader_free(reader); avro_value_decref(&value); avro_value_iface_decref(iface); @@ -195,7 +199,7 @@ static int test_map_int64min(void) return 1; } - printf("PASS: INT64_MIN map block count rejected: %s\n", + printf("PASS: INT64_MIN map block count rejected with EINVAL: %s\n", avro_strerror()); avro_reader_free(reader); From 4de7e856c8503e5839debe005279f9a2b6b6d7af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Mej=C3=ADa?= Date: Sat, 11 Jul 2026 10:46:02 +0200 Subject: [PATCH 4/5] AVRO-4275: [C] Guard INT64_MIN in skip and consumer paths, fix char narrowing Apply the same INT64_MIN overflow guard to block_count negation in datum_skip.c (skip_array, skip_map) and consume-binary.c (read_array, read_map) which used the unsafe 'block_count * -1' idiom. Also cast 0xFF byte constants to char explicitly in the test to avoid implementation-defined narrowing on platforms with signed char. Addresses review feedback on PR #3842. Assisted-by: GitHub Copilot:claude-opus-4.6 --- lang/c/src/consume-binary.c | 12 ++++++++++-- lang/c/src/datum_skip.c | 12 ++++++++++-- lang/c/tests/test_avro_4275.c | 4 ++-- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lang/c/src/consume-binary.c b/lang/c/src/consume-binary.c index 5e1db20684f..3e15ee6ea1d 100644 --- a/lang/c/src/consume-binary.c +++ b/lang/c/src/consume-binary.c @@ -58,7 +58,11 @@ read_array(avro_reader_t reader, const avro_encoding_t * enc, while (block_count != 0) { if (block_count < 0) { - block_count = block_count * -1; + if (block_count == INT64_MIN) { + avro_set_error("Invalid array block count"); + return EINVAL; + } + block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read array block size: "); } @@ -101,7 +105,11 @@ read_map(avro_reader_t reader, const avro_encoding_t * enc, while (block_count != 0) { if (block_count < 0) { - block_count = block_count * -1; + if (block_count == INT64_MIN) { + avro_set_error("Invalid map block count"); + return EINVAL; + } + block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read map block size: "); } diff --git a/lang/c/src/datum_skip.c b/lang/c/src/datum_skip.c index e0ce561642e..d8adf924486 100644 --- a/lang/c/src/datum_skip.c +++ b/lang/c/src/datum_skip.c @@ -36,7 +36,11 @@ static int skip_array(avro_reader_t reader, const avro_encoding_t * enc, while (block_count != 0) { if (block_count < 0) { - block_count = block_count * -1; + if (block_count == INT64_MIN) { + avro_set_error("Invalid array block count"); + return EINVAL; + } + block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read array block size: "); } @@ -63,7 +67,11 @@ static int skip_map(avro_reader_t reader, const avro_encoding_t * enc, while (block_count != 0) { int64_t block_size; if (block_count < 0) { - block_count = block_count * -1; + if (block_count == INT64_MIN) { + avro_set_error("Invalid map block count"); + return EINVAL; + } + block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read map block size: "); } diff --git a/lang/c/tests/test_avro_4275.c b/lang/c/tests/test_avro_4275.c index f4755853ba2..b4d4bf0b78e 100644 --- a/lang/c/tests/test_avro_4275.c +++ b/lang/c/tests/test_avro_4275.c @@ -38,8 +38,8 @@ * the 10-byte varint: FF FF FF FF FF FF FF FF FF 01 */ static const char int64min_block_count[] = { - '\xFF', '\xFF', '\xFF', '\xFF', '\xFF', - '\xFF', '\xFF', '\xFF', '\xFF', '\x01' + (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, + (char)0xFF, (char)0xFF, (char)0xFF, (char)0xFF, 0x01 }; /* From 67816619c202bcd986fd3f43884ddc02962664b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Isma=C3=ABl=20Mej=C3=ADa?= Date: Sat, 11 Jul 2026 10:57:18 +0200 Subject: [PATCH 5/5] AVRO-4275: [C] Move INT64_MIN guard before consumer callbacks In consume-binary.c, the array_start_block and map_start_block callbacks were being invoked with the raw block_count before the INT64_MIN validation. Move the guard to immediately after read_long() so that an invalid block count is rejected before reaching any consumer callback. Applies to both the initial block_count read and the subsequent reads at the end of each loop iteration. Assisted-by: GitHub Copilot:claude-opus-4.6 --- lang/c/src/consume-binary.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lang/c/src/consume-binary.c b/lang/c/src/consume-binary.c index 3e15ee6ea1d..058202e6f52 100644 --- a/lang/c/src/consume-binary.c +++ b/lang/c/src/consume-binary.c @@ -53,15 +53,15 @@ read_array(avro_reader_t reader, const avro_encoding_t * enc, check_prefix(rval, enc->read_long(reader, &block_count), "Cannot read array block count: "); + if (block_count == INT64_MIN) { + avro_set_error("Invalid array block count"); + return EINVAL; + } check(rval, avro_consumer_call(consumer, array_start_block, 1, block_count, ud)); while (block_count != 0) { if (block_count < 0) { - if (block_count == INT64_MIN) { - avro_set_error("Invalid array block count"); - return EINVAL; - } block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read array block size: "); @@ -81,6 +81,10 @@ read_array(avro_reader_t reader, const avro_encoding_t * enc, check_prefix(rval, enc->read_long(reader, &block_count), "Cannot read array block count: "); + if (block_count == INT64_MIN) { + avro_set_error("Invalid array block count"); + return EINVAL; + } check(rval, avro_consumer_call(consumer, array_start_block, 0, block_count, ud)); } @@ -100,15 +104,15 @@ read_map(avro_reader_t reader, const avro_encoding_t * enc, check_prefix(rval, enc->read_long(reader, &block_count), "Cannot read map block count: "); + if (block_count == INT64_MIN) { + avro_set_error("Invalid map block count"); + return EINVAL; + } check(rval, avro_consumer_call(consumer, map_start_block, 1, block_count, ud)); while (block_count != 0) { if (block_count < 0) { - if (block_count == INT64_MIN) { - avro_set_error("Invalid map block count"); - return EINVAL; - } block_count = -block_count; check_prefix(rval, enc->read_long(reader, &block_size), "Cannot read map block size: "); @@ -143,6 +147,10 @@ read_map(avro_reader_t reader, const avro_encoding_t * enc, check_prefix(rval, enc->read_long(reader, &block_count), "Cannot read map block count: "); + if (block_count == INT64_MIN) { + avro_set_error("Invalid map block count"); + return EINVAL; + } check(rval, avro_consumer_call(consumer, map_start_block, 0, block_count, ud)); }