diff --git a/lang/c/src/schema.c b/lang/c/src/schema.c index a4d8e9f898a..fda62a6617f 100644 --- a/lang/c/src/schema.c +++ b/lang/c/src/schema.c @@ -538,7 +538,14 @@ const char *avro_schema_enum_get(const avro_schema_t enump, st_data_t data; char *sym; } val; - st_lookup(avro_schema_to_enum(enump)->symbols, index, &val.data); + /* + * Return NULL for an unknown index rather than an uninitialized + * pointer: st_lookup() leaves val.data untouched when the key is + * not found. + */ + if (!st_lookup(avro_schema_to_enum(enump)->symbols, index, &val.data)) { + return NULL; + } return val.sym; } diff --git a/lang/c/src/value-read.c b/lang/c/src/value-read.c index b6b6e79fadd..923f5563183 100644 --- a/lang/c/src/value-read.c +++ b/lang/c/src/value-read.c @@ -329,10 +329,23 @@ read_value(avro_reader_t reader, avro_value_t *dest) case AVRO_ENUM: { + avro_schema_t schema = avro_value_get_schema(dest); int64_t val; check_prefix(rval, avro_binary_encoding. read_long(reader, &val), "Cannot read enum value: "); + /* + * Reject an out-of-range ordinal read from the input + * before it is used to look up a symbol; otherwise the + * lookup fails and an uninitialized pointer would be + * returned to the caller (see avro_schema_enum_get). + */ + if (val < 0 || + val >= avro_schema_enum_number_of_symbols(schema)) { + avro_set_error("Enum value %ld is out of range", + (long) val); + return EINVAL; + } return avro_value_set_enum(dest, val); } diff --git a/lang/c/tests/CMakeLists.txt b/lang/c/tests/CMakeLists.txt index 6b4164fa740..b11b78ed9c6 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_4344) diff --git a/lang/c/tests/test_avro_4344.c b/lang/c/tests/test_avro_4344.c new file mode 100644 index 00000000000..3c4be76c631 --- /dev/null +++ b/lang/c/tests/test_avro_4344.c @@ -0,0 +1,95 @@ +/* + * 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. + */ + +/* + * AVRO-4344: reading an out-of-range enum ordinal must return an error + * rather than continuing with an out-of-range index (which previously led + * avro_schema_enum_get() to return an uninitialized pointer). + */ + +#include +#include +#include + +static avro_schema_t +make_enum_schema(void) +{ + /* Two symbols -> valid ordinals are 0 and 1. */ + avro_schema_t schema = avro_schema_enum("suit"); + avro_schema_enum_symbol_append(schema, "SPADES"); + avro_schema_enum_symbol_append(schema, "HEARTS"); + return schema; +} + +static int +read_enum(avro_value_iface_t *iface, const char *buf, size_t len) +{ + avro_reader_t reader = avro_reader_memory(buf, len); + avro_value_t value; + int rval; + + avro_generic_value_new(iface, &value); + rval = avro_value_read(reader, &value); + avro_value_decref(&value); + avro_reader_free(reader); + return rval; +} + +int main(void) +{ + avro_schema_t schema = make_enum_schema(); + avro_value_iface_t *iface = avro_generic_class_from_schema(schema); + + /* A valid ordinal (1, zig-zag 0x02) must read successfully. */ + { + const char buf[] = { 0x02 }; + if (read_enum(iface, buf, sizeof(buf)) != 0) { + fprintf(stderr, "Valid enum ordinal failed to read: %s\n", + avro_strerror()); + exit(EXIT_FAILURE); + } + } + + /* An out-of-range positive ordinal (5, zig-zag 0x0A) must error. */ + { + const char buf[] = { 0x0a }; + if (read_enum(iface, buf, sizeof(buf)) == 0) { + fprintf(stderr, "Out-of-range enum ordinal was accepted\n"); + exit(EXIT_FAILURE); + } + } + + /* A negative ordinal (-1, zig-zag 0x01) must error. */ + { + const char buf[] = { 0x01 }; + if (read_enum(iface, buf, sizeof(buf)) == 0) { + fprintf(stderr, "Negative enum ordinal was accepted\n"); + exit(EXIT_FAILURE); + } + } + + /* avro_schema_enum_get() returns NULL (not garbage) for a bad index. */ + if (avro_schema_enum_get(schema, 99) != NULL) { + fprintf(stderr, "avro_schema_enum_get did not return NULL for " + "an out-of-range index\n"); + exit(EXIT_FAILURE); + } + + avro_value_iface_decref(iface); + avro_schema_decref(schema); + exit(EXIT_SUCCESS); +}