Author: dcreager Date: Wed Dec 18 13:31:53 2013 New Revision: 1551941 URL: http://svn.apache.org/r1551941 Log: AVRO-1237. C: Verify union discriminant when reading Avro data files.
Test case submitted by Michael Cooper. Michael's original fix was to perform a bounds check in avro_value_set_branch, but I've opted for a solution in avro_value_read. This has two benefits: the check is only performed when reading a discriminant value from an unsafe source, and it doesn't impose an extra burden on anyone writing their own custom value implementations. Added: avro/trunk/lang/c/tests/avro-1237-bad-union-discriminant.avro (with props) avro/trunk/lang/c/tests/avro-1237-good.avro (with props) avro/trunk/lang/c/tests/test_avro_1237.c Modified: avro/trunk/CHANGES.txt avro/trunk/lang/c/src/value-read.c avro/trunk/lang/c/tests/CMakeLists.txt Modified: avro/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=1551941&r1=1551940&r2=1551941&view=diff ============================================================================== --- avro/trunk/CHANGES.txt (original) +++ avro/trunk/CHANGES.txt Wed Dec 18 13:31:53 2013 @@ -79,6 +79,9 @@ Trunk (not yet released) AVRO-1358. C: Hide symbols that aren't in the public API. (dcreager) + AVRO-1237. C: Bounds-check union discriminant when reading a data file. + (Michael Cooper via dcreager) + Avro 1.7.5 (12 August 2013) NEW FEATURES Modified: avro/trunk/lang/c/src/value-read.c URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/src/value-read.c?rev=1551941&r1=1551940&r2=1551941&view=diff ============================================================================== --- avro/trunk/lang/c/src/value-read.c (original) +++ avro/trunk/lang/c/src/value-read.c Wed Dec 18 13:31:53 2013 @@ -160,13 +160,18 @@ read_union_value(avro_reader_t reader, a { int rval; int64_t discriminant; + avro_schema_t union_schema; + int64_t branch_count; avro_value_t branch; check_prefix(rval, avro_binary_encoding. read_long(reader, &discriminant), "Cannot read union discriminant: "); - if (discriminant < 0) { + union_schema = avro_value_get_schema(dest); + branch_count = avro_schema_union_size(union_schema); + + if (discriminant < 0 || discriminant >= branch_count) { avro_set_error("Invalid union discriminant value: (%d)", discriminant); return 1; Modified: avro/trunk/lang/c/tests/CMakeLists.txt URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/CMakeLists.txt?rev=1551941&r1=1551940&r2=1551941&view=diff ============================================================================== --- avro/trunk/lang/c/tests/CMakeLists.txt (original) +++ avro/trunk/lang/c/tests/CMakeLists.txt Wed Dec 18 13:31:53 2013 @@ -54,6 +54,7 @@ add_avro_test(test_avro_1034) add_avro_test(test_avro_1084) add_avro_test(test_avro_1087) add_avro_test(test_avro_1165) +add_avro_test(test_avro_1237) add_avro_test(test_avro_1238) add_avro_test(test_avro_1279) add_avro_test(test_avro_data) Added: avro/trunk/lang/c/tests/avro-1237-bad-union-discriminant.avro URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/avro-1237-bad-union-discriminant.avro?rev=1551941&view=auto ============================================================================== Binary file - no diff available. Propchange: avro/trunk/lang/c/tests/avro-1237-bad-union-discriminant.avro ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: avro/trunk/lang/c/tests/avro-1237-good.avro URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/avro-1237-good.avro?rev=1551941&view=auto ============================================================================== Binary file - no diff available. Propchange: avro/trunk/lang/c/tests/avro-1237-good.avro ------------------------------------------------------------------------------ svn:mime-type = application/octet-stream Added: avro/trunk/lang/c/tests/test_avro_1237.c URL: http://svn.apache.org/viewvc/avro/trunk/lang/c/tests/test_avro_1237.c?rev=1551941&view=auto ============================================================================== --- avro/trunk/lang/c/tests/test_avro_1237.c (added) +++ avro/trunk/lang/c/tests/test_avro_1237.c Wed Dec 18 13:31:53 2013 @@ -0,0 +1,112 @@ +/* + * 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 + * + * http://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 <avro.h> +#include <stdio.h> +#include <stdlib.h> + +#define check_exit(call) \ + do { \ + int __rc = call; \ + if (__rc != 0) { \ + fprintf(stderr, "Unexpected error:\n %s\n %s\n", \ + avro_strerror(), #call); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define expect_error(call) \ + do { \ + int __rc = call; \ + if (__rc == 0) { \ + fprintf(stderr, "Expected an error:\n %s\n", #call); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +#define check_expected_value(actual, expected) \ + do { \ + if (!avro_value_equal_fast((actual), (expected))) { \ + char *actual_json; \ + char *expected_json; \ + avro_value_to_json((actual), 1, &actual_json); \ + avro_value_to_json((expected), 1, &expected_json); \ + fprintf(stderr, "Expected %s\nGot %s\n", \ + expected_json, actual_json); \ + free(actual_json); \ + free(expected_json); \ + exit(EXIT_FAILURE); \ + } \ + } while (0) + +int main(void) +{ + avro_schema_t schema; + avro_file_reader_t reader; + avro_value_iface_t *iface; + avro_value_t actual; + avro_value_t expected; + avro_value_t branch; + + schema = avro_schema_union(); + avro_schema_union_append(schema, avro_schema_null()); + avro_schema_union_append(schema, avro_schema_int()); + + iface = avro_generic_class_from_schema(schema); + avro_generic_value_new(iface, &actual); + avro_generic_value_new(iface, &expected); + + + /* First read the contents of the good file. */ + + check_exit(avro_file_reader("avro-1237-good.avro", &reader)); + + check_exit(avro_file_reader_read_value(reader, &actual)); + check_exit(avro_value_set_branch(&expected, 0, &branch)); + check_exit(avro_value_set_null(&branch)); + check_expected_value(&actual, &expected); + + check_exit(avro_file_reader_read_value(reader, &actual)); + check_exit(avro_value_set_branch(&expected, 1, &branch)); + check_exit(avro_value_set_int(&branch, 100)); + check_expected_value(&actual, &expected); + + check_exit(avro_file_reader_close(reader)); + + + /* Then read from the malformed file. */ + + check_exit(avro_file_reader + ("avro-1237-bad-union-discriminant.avro", &reader)); + + check_exit(avro_file_reader_read_value(reader, &actual)); + check_exit(avro_value_set_branch(&expected, 0, &branch)); + check_exit(avro_value_set_null(&branch)); + check_expected_value(&actual, &expected); + + expect_error(avro_file_reader_read_value(reader, &actual)); + + check_exit(avro_file_reader_close(reader)); + + + /* Clean up and exit */ + avro_value_decref(&actual); + avro_value_decref(&expected); + avro_value_iface_decref(iface); + avro_schema_decref(schema); + exit(EXIT_SUCCESS); +}