iemejia commented on code in PR #3862:
URL: https://github.com/apache/avro/pull/3862#discussion_r3565239843
##########
lang/ruby/lib/avro/io.rb:
##########
@@ -103,10 +110,40 @@ def read_string
end
def read(len)
- # Read n bytes
+ # Read n bytes. Reject a declared length that exceeds the bytes
+ # actually remaining before allocating for it, to guard against an
+ # out-of-memory attack from a malicious or truncated input. The check
+ # is only applied to larger reads; smaller reads and stream readers
that
+ # cannot report their size fall back to reading directly.
+ if len < 0
+ # A negative length would make IO#read return the rest of the stream,
+ # which bypasses the size check and can allocate without bound.
+ raise AvroError, "Cannot read a negative number of bytes: #{len}"
+ end
+ if len > MAX_UNCHECKED_READ
+ remaining = bytes_remaining
+ if remaining && len > remaining
+ raise AvroError, "Cannot read #{len} bytes, only #{remaining}
remaining"
+ end
+ end
@reader.read(len)
end
+ # Number of bytes still available to read, or nil when the reader cannot
+ # report its size. Used to reject a declared length or collection block
+ # count that exceeds the data actually available before allocating for
it.
+ def bytes_remaining
+ return nil unless @reader.respond_to?(:size) &&
@reader.respond_to?(:tell)
+ size = @reader.size
+ pos = @reader.tell
+ return nil unless size.is_a?(Integer) && pos.is_a?(Integer)
+ size - pos
Review Comment:
Fixed in ea1ac32: bytes_remaining clamps a negative result to 0 via [size -
pos, 0].max, so a truncated file yields 'no bytes available' instead of a
negative count.
##########
lang/ruby/lib/avro/io.rb:
##########
@@ -318,6 +355,7 @@ def read_array(writers_schema, readers_schema, decoder)
block_count = -block_count
_block_size = decoder.read_long
end
+ ensure_collection_available(decoder, block_count,
min_bytes_per_element(writers_schema.items))
Review Comment:
Fixed in ea1ac32: read_array computes the per-element minimum once per
invocation and reuses it across blocks.
##########
lang/ruby/lib/avro/io.rb:
##########
@@ -337,6 +375,8 @@ def read_map(writers_schema, readers_schema, decoder)
block_count = -block_count
_block_size = decoder.read_long
end
+ # Map keys are strings (>= 1 byte length prefix) plus the value.
+ ensure_collection_available(decoder, block_count, 1 +
min_bytes_per_element(writers_schema.values))
Review Comment:
Fixed in ea1ac32: read_map computes 1 + min-bytes once per invocation and
reuses it across blocks.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]