This is an automated email from the ASF dual-hosted git repository.
RyanSkraba pushed a commit to branch branch-1.12
in repository https://gitbox.apache.org/repos/asf/avro.git
The following commit(s) were added to refs/heads/branch-1.12 by this push:
new ba832f245d AVRO-4246: [c] Fix memory leak on failed decoding (#3732)
ba832f245d is described below
commit ba832f245dddeed06e6272fcbaceb89fe8c55e1a
Author: Fábio Lucas Carneiro <[email protected]>
AuthorDate: Tue Jun 2 20:04:24 2026 +0200
AVRO-4246: [c] Fix memory leak on failed decoding (#3732)
* AVRO-4246: Add 'AVRO_READ_OR_FREE' macro.
* AVRO-4246: Replace 'AVRO_READ' by 'AVRO_READ_OR_FREE' in 'read_string'
and 'read_bytes' functions.
* AVRO-4246: Add 'test_avro_4246.c' to test suite.
---------
Co-authored-by: flpereir <[email protected]>
Co-authored-by: kwenzh <[email protected]>
---
lang/c/src/encoding.h | 2 +
lang/c/src/encoding_binary.c | 4 +-
lang/c/tests/CMakeLists.txt | 1 +
lang/c/tests/test_avro_4246.c | 223 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 228 insertions(+), 2 deletions(-)
diff --git a/lang/c/src/encoding.h b/lang/c/src/encoding.h
index 6333d588df..b90c91dd5c 100644
--- a/lang/c/src/encoding.h
+++ b/lang/c/src/encoding.h
@@ -96,6 +96,8 @@ typedef struct avro_encoding_t avro_encoding_t;
{ int rval = avro_write( writer, buf, len ); if(rval) return rval; }
#define AVRO_READ(reader, buf, len) \
{ int rval = avro_read( reader, buf, len ); if(rval) return rval; }
+#define AVRO_READ_OR_FREE(reader, buf, len) \
+{ int rval = avro_read( reader, buf, len ); if(rval) { avro_free(buf, 0); buf
= NULL; return rval; } }
#define AVRO_SKIP(reader, len) \
{ int rval = avro_skip( reader, len); if (rval) return rval; }
diff --git a/lang/c/src/encoding_binary.c b/lang/c/src/encoding_binary.c
index 1fc5f0c9a7..4b5d35e52d 100644
--- a/lang/c/src/encoding_binary.c
+++ b/lang/c/src/encoding_binary.c
@@ -132,8 +132,8 @@ static int read_bytes(avro_reader_t reader, char **bytes,
int64_t * len)
avro_set_error("Cannot allocate buffer for bytes value");
return ENOMEM;
}
- AVRO_READ(reader, *bytes, *len);
(*bytes)[*len] = '\0';
+ AVRO_READ_OR_FREE(reader, *bytes, *len);
return 0;
}
@@ -182,7 +182,7 @@ static int read_string(avro_reader_t reader, char **s,
int64_t *len)
return ENOMEM;
}
(*s)[str_len] = '\0';
- AVRO_READ(reader, *s, str_len);
+ AVRO_READ_OR_FREE(reader, *s, str_len);
return 0;
}
diff --git a/lang/c/tests/CMakeLists.txt b/lang/c/tests/CMakeLists.txt
index 3200164770..6b4164fa74 100644
--- a/lang/c/tests/CMakeLists.txt
+++ b/lang/c/tests/CMakeLists.txt
@@ -87,3 +87,4 @@ add_avro_test_checkmem(test_avro_1379)
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)
diff --git a/lang/c/tests/test_avro_4246.c b/lang/c/tests/test_avro_4246.c
new file mode 100644
index 0000000000..d29b2c3bbc
--- /dev/null
+++ b/lang/c/tests/test_avro_4246.c
@@ -0,0 +1,223 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <avro.h>
+
+#define STRING_SCHEMA_LITERAL "\"string\""
+#define INT_SCHEMA_LITERAL "\"int\""
+
+typedef struct
+{
+ char data[256];
+ size_t size;
+} encoded_buf_t;
+
+static int encode_int(
+ avro_value_iface_t *iface,
+ int32_t number,
+ encoded_buf_t *out_buf)
+{
+ int rc;
+ avro_value_t value;
+ avro_writer_t writer = NULL;
+
+ memset(out_buf, 0, sizeof(*out_buf));
+
+ rc = avro_generic_value_new(iface, &value);
+ if (rc != 0)
+ {
+ fprintf(stderr, "encode_int: avro_generic_value_new failed: %s\n",
+ avro_strerror());
+ return rc;
+ }
+
+ rc = avro_value_set_int(&value, number);
+ if (rc != 0)
+ {
+ fprintf(stderr, "encode_int: avro_value_set_int failed: %s\n",
+ avro_strerror());
+ avro_value_decref(&value);
+ return rc;
+ }
+
+ writer = avro_writer_memory(out_buf->data, sizeof(out_buf->data));
+ if (writer == NULL)
+ {
+ fprintf(stderr, "encode_int: avro_writer_memory failed\n");
+ avro_value_decref(&value);
+ return -1;
+ }
+
+ rc = avro_value_write(writer, &value);
+ if (rc != 0)
+ {
+ fprintf(stderr, "encode_int: avro_value_write failed: %s\n",
+ avro_strerror());
+ avro_writer_free(writer);
+ avro_value_decref(&value);
+ return rc;
+ }
+
+ out_buf->size = avro_writer_tell(writer);
+
+ avro_writer_free(writer);
+ avro_value_decref(&value);
+ return 0;
+}
+
+static int decode_as_string(
+ avro_value_iface_t *string_iface,
+ const void *buf,
+ size_t buf_size,
+ const char *label)
+{
+ int rc;
+ avro_reader_t reader = NULL;
+ avro_value_t decoded;
+ const char *text = NULL;
+ size_t text_size = 0;
+
+ rc = avro_generic_value_new(string_iface, &decoded);
+ if (rc != 0)
+ {
+ fprintf(stderr, "%s: avro_generic_value_new failed: %s\n",
+ label, avro_strerror());
+ return rc;
+ }
+
+ reader = avro_reader_memory((const char *)buf, buf_size);
+ if (reader == NULL)
+ {
+ fprintf(stderr, "%s: avro_reader_memory failed\n", label);
+ avro_value_decref(&decoded);
+ return -1;
+ }
+
+ rc = avro_value_read(reader, &decoded);
+ if (rc != 0)
+ {
+ fprintf(stderr, "%s: avro_value_read failed as expected? rc=%d
err=%s\n",
+ label, rc, avro_strerror());
+ avro_reader_free(reader);
+ avro_value_decref(&decoded);
+ return rc;
+ }
+
+ rc = avro_value_get_string(&decoded, &text, &text_size);
+ if (rc != 0)
+ {
+ fprintf(stderr, "%s: avro_value_get_string failed: %s\n",
+ label, avro_strerror());
+ avro_reader_free(reader);
+ avro_value_decref(&decoded);
+ return rc;
+ }
+
+ printf("%s: decoded successfully: \"%s\" (%zu bytes)\n",
+ label, text, text_size);
+
+ avro_reader_free(reader);
+ avro_value_decref(&decoded);
+ return 0;
+}
+
+int main(void)
+{
+ int rc;
+ int ret = EXIT_SUCCESS;
+
+ avro_schema_t string_schema = NULL;
+ avro_schema_t int_schema = NULL;
+
+ avro_value_iface_t *string_iface = NULL;
+ avro_value_iface_t *int_iface = NULL;
+
+ encoded_buf_t encoded_string;
+ encoded_buf_t encoded_int;
+
+ rc = avro_schema_from_json_literal(STRING_SCHEMA_LITERAL, &string_schema);
+ if (rc != 0)
+ {
+ fprintf(stderr, "Failed to parse string schema: %s\n",
avro_strerror());
+ return EXIT_FAILURE;
+ }
+
+ rc = avro_schema_from_json_literal(INT_SCHEMA_LITERAL, &int_schema);
+ if (rc != 0)
+ {
+ fprintf(stderr, "Failed to parse int schema: %s\n", avro_strerror());
+ avro_schema_decref(string_schema);
+ return EXIT_FAILURE;
+ }
+
+ string_iface = avro_generic_class_from_schema(string_schema);
+ if (string_iface == NULL)
+ {
+ fprintf(stderr, "Failed to create string iface\n");
+ avro_schema_decref(int_schema);
+ avro_schema_decref(string_schema);
+ return EXIT_FAILURE;
+ }
+
+ int_iface = avro_generic_class_from_schema(int_schema);
+ if (int_iface == NULL)
+ {
+ fprintf(stderr, "Failed to create int iface\n");
+ avro_value_iface_decref(string_iface);
+ avro_schema_decref(int_schema);
+ avro_schema_decref(string_schema);
+ return EXIT_FAILURE;
+ }
+
+ rc = encode_int(int_iface, 12345, &encoded_int);
+ if (rc != 0)
+ {
+ fprintf(stderr, "Encoding int failed\n");
+ avro_value_iface_decref(int_iface);
+ avro_value_iface_decref(string_iface);
+ avro_schema_decref(int_schema);
+ avro_schema_decref(string_schema);
+ return EXIT_FAILURE;
+ }
+
+ printf("Encoded int size: %zu bytes\n", encoded_int.size);
+
+ rc = decode_as_string(string_iface,
+ encoded_int.data,
+ encoded_int.size,
+ "int payload as string");
+ if (rc == 0)
+ {
+ fprintf(stderr, "Unexpected success decoding int payload as string\n");
+ ret = EXIT_FAILURE;
+ }
+ else
+ {
+ printf("int payload as string: failed cleanly, as expected\n");
+ }
+
+ avro_value_iface_decref(int_iface);
+ avro_value_iface_decref(string_iface);
+ avro_schema_decref(int_schema);
+ avro_schema_decref(string_schema);
+
+ return ret;
+}