From 89fb9db03478b793be7188c5683c6560c2eff115 Mon Sep 17 00:00:00 2001 From: Steven Aerts Date: Fri, 24 Oct 2025 12:46:57 +0000 Subject: [PATCH] AVRO-4193 support for empty bytes When avro-c tries to encode an empty byte array it will avro_malloc(0) which on some architectures will return NULL. Make sure this is not interpreted as an error or or dereferenced causing a segfault. --- lang/c/src/datum.c | 4 +-- lang/c/src/value-json.c | 4 +-- lang/c/tests/test_avro_data.c | 53 +++++++++++++++++++++++++++-------- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/lang/c/src/datum.c b/lang/c/src/datum.c index 5307c7a8385..0dc201b9613 100644 --- a/lang/c/src/datum.c +++ b/lang/c/src/datum.c @@ -156,7 +156,7 @@ static avro_datum_t avro_bytes_private(char *bytes, int64_t size, avro_datum_t avro_bytes(const char *bytes, int64_t size) { char *bytes_copy = (char *) avro_malloc(size); - if (!bytes_copy) { + if (!bytes_copy && size) { avro_set_error("Cannot copy bytes content"); return NULL; } @@ -197,7 +197,7 @@ int avro_bytes_set(avro_datum_t datum, const char *bytes, const int64_t size) { int rval; char *bytes_copy = (char *) avro_malloc(size); - if (!bytes_copy) { + if (!bytes_copy && size) { avro_set_error("Cannot copy bytes content"); return ENOMEM; } diff --git a/lang/c/src/value-json.c b/lang/c/src/value-json.c index 7927c14dd84..6b505a9ce41 100644 --- a/lang/c/src/value-json.c +++ b/lang/c/src/value-json.c @@ -62,7 +62,7 @@ encode_utf8_bytes(const void *src, size_t src_len, // Allocate a new buffer for the UTF-8 string and fill it in. uint8_t *dest8 = (uint8_t *) avro_malloc(utf8_len); - if (dest8 == NULL) { + if (dest8 == NULL && utf8_len) { avro_set_error("Cannot allocate JSON bytes buffer"); return ENOMEM; } @@ -126,7 +126,7 @@ avro_value_to_json_t(const avro_value_t *value) return NULL; } - json_t *result = json_stringn_nocheck((const char *) encoded, encoded_size); + json_t *result = json_stringn_nocheck((const char *) encoded ? encoded : "", encoded_size); avro_free(encoded, encoded_size); if (result == NULL) { avro_set_error("Cannot allocate JSON bytes"); diff --git a/lang/c/tests/test_avro_data.c b/lang/c/tests/test_avro_data.c index 3a26c67e242..ac7c637929f 100644 --- a/lang/c/tests/test_avro_data.c +++ b/lang/c/tests/test_avro_data.c @@ -40,18 +40,20 @@ test_allocator(void *ud, void *ptr, size_t osize, size_t nsize) AVRO_UNUSED(osize); if (nsize == 0) { - size_t *size = ((size_t *) ptr) - 1; - if (osize != *size) { - fprintf(stderr, - "Error freeing %p:\n" - "Size passed to avro_free (%" PRIsz ") " - "doesn't match size passed to " - "avro_malloc (%" PRIsz ")\n", - ptr, osize, *size); - abort(); - //exit(EXIT_FAILURE); + if (ptr) { + size_t *size = ((size_t *) ptr) - 1; + if (osize != *size) { + fprintf(stderr, + "Error freeing %p:\n" + "Size passed to avro_free (%" PRIsz ") " + "doesn't match size passed to " + "avro_malloc (%" PRIsz ")\n", + ptr, osize, *size); + abort(); + //exit(EXIT_FAILURE); + } + free(size); } - free(size); return NULL; } else { size_t real_size = nsize + sizeof(size_t); @@ -214,6 +216,34 @@ static int test_bytes(void) return 0; } +static int test_empty_bytes(void) +{ + char bytes[] = { }; + avro_schema_t writer_schema = avro_schema_bytes(); + avro_datum_t datum; + avro_datum_t expected_datum; + + datum = avro_givebytes(bytes, sizeof(bytes), NULL); + write_read_check(writer_schema, datum, NULL, NULL, "bytes"); + test_json(datum, "\"\""); + avro_datum_decref(datum); + avro_schema_decref(writer_schema); + + datum = avro_givebytes(NULL, 0, NULL); + avro_givebytes_set(datum, bytes, sizeof(bytes), NULL); + expected_datum = avro_givebytes(bytes, sizeof(bytes), NULL); + if (!avro_datum_equal(datum, expected_datum)) { + fprintf(stderr, + "Expected equal bytes instances.\n"); + exit(EXIT_FAILURE); + } + avro_datum_decref(datum); + avro_datum_decref(expected_datum); + + avro_schema_decref(writer_schema); + return 0; +} + static int test_int32(void) { int i; @@ -657,6 +687,7 @@ int main(void) { "string", test_string}, { "bytes", test_bytes}, { + "empty_bytes", test_empty_bytes}, { "int", test_int32}, { "long", test_int64}, { "float", test_float}, {