diff --git a/lang/c/src/avro/io.h b/lang/c/src/avro/io.h index ffbb68dc57f..c05961d3938 100644 --- a/lang/c/src/avro/io.h +++ b/lang/c/src/avro/io.h @@ -52,6 +52,15 @@ avro_reader_memory_set_source(avro_reader_t reader, const char *buf, int64_t len void avro_writer_memory_set_dest(avro_writer_t writer, const char *buf, int64_t len); + +/* + * Returns the number of bytes available to read from the reader. + * For memory readers, returns remaining bytes (len - read). + * For file readers, returns buffered bytes available. + * Returns -1 for unknown reader types. + */ +int64_t avro_max_read(avro_reader_t reader); + int avro_read(avro_reader_t reader, void *buf, int64_t len); int avro_skip(avro_reader_t reader, int64_t len); int avro_write(avro_writer_t writer, void *buf, int64_t len); diff --git a/lang/c/src/encoding.h b/lang/c/src/encoding.h index 6333d588df7..db4e4d4c972 100644 --- a/lang/c/src/encoding.h +++ b/lang/c/src/encoding.h @@ -99,6 +99,9 @@ typedef struct avro_encoding_t avro_encoding_t; #define AVRO_SKIP(reader, len) \ { int rval = avro_skip( reader, len); if (rval) return rval; } +#define AVRO_READ_OR_FREE(reader, buf, len, mem_size) \ +{ int rval = avro_read( reader, buf, len ); if(rval) { if(buf) avro_free(buf, mem_size); buf = NULL; return rval; } } + extern const avro_encoding_t avro_binary_encoding; /* in * encoding_binary */ diff --git a/lang/c/src/encoding_binary.c b/lang/c/src/encoding_binary.c index 96dacea5836..8d7cd0e3d85 100644 --- a/lang/c/src/encoding_binary.c +++ b/lang/c/src/encoding_binary.c @@ -125,19 +125,31 @@ static int64_t size_int(avro_writer_t writer, const int32_t i) static int read_bytes(avro_reader_t reader, char **bytes, int64_t * len) { int rval; + int64_t max_available = -1; check_prefix(rval, read_long(reader, len), "Cannot read bytes length: "); if (*len < 0) { avro_set_error("Invalid bytes length: %" PRId64, *len); return EINVAL; } + + max_available = avro_max_read(reader); + if (max_available >= 0 && str_len > max_available) { + avro_set_error("String length %" PRId64 " is greater than available buffer size %" PRId64, + str_len, max_available); + return ERANGE; + } + + *bytes = (char *) avro_malloc(*len + 1); if (!*bytes) { 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, *len+1); + return 0; } @@ -180,6 +192,7 @@ size_bytes(avro_writer_t writer, const char *bytes, const int64_t len) static int read_string(avro_reader_t reader, char **s, int64_t *len) { int64_t str_len = 0; + int64_t max_available = -1; int rval; check_prefix(rval, read_long(reader, &str_len), "Cannot read string length: "); @@ -187,6 +200,14 @@ static int read_string(avro_reader_t reader, char **s, int64_t *len) avro_set_error("Invalid string length: %" PRId64, str_len); return EINVAL; } + + max_available = avro_max_read(reader); + if (max_available >= 0 && str_len > max_available) { + avro_set_error("String length %" PRId64 " is greater than available buffer size %" PRId64, + str_len, max_available); + return ERANGE; + } + *len = str_len + 1; *s = (char *) avro_malloc(*len); if (!*s) { @@ -194,7 +215,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, *len); return 0; } diff --git a/lang/c/src/io.c b/lang/c/src/io.c index c1e2f5dc9b1..92b253379f6 100644 --- a/lang/c/src/io.c +++ b/lang/c/src/io.c @@ -274,6 +274,20 @@ int avro_read(avro_reader_t reader, void *buf, int64_t len) return EINVAL; } + +int64_t avro_max_read(avro_reader_t reader) +{ + if (is_memory_io(reader)) { + struct _avro_reader_memory_t *mem_reader = avro_reader_to_memory(reader); + return mem_reader->len - mem_reader->read; + } else if (is_file_io(reader)) { + struct _avro_reader_file_t *file_reader = avro_reader_to_file(reader); + return bytes_available(file_reader); + } + return -1; +} + + static int avro_skip_memory(struct _avro_reader_memory_t *reader, int64_t len) { if (len > 0) {