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;
+}

Reply via email to