Copilot commented on code in PR #3862:
URL: https://github.com/apache/avro/pull/3862#discussion_r3566582745
##########
lang/ruby/lib/avro/io.rb:
##########
@@ -496,12 +550,98 @@ def skip_record(writers_schema, decoder)
end
private
- def skip_blocks(decoder, &blk)
+ # Minimum number of bytes a single value of the given schema can occupy
on
+ # the wire. Used to reject an array/map block count that could not be
+ # backed by the bytes remaining. A type that can encode to zero bytes
+ # (null) returns 0, which disables the check for it (so an array of nulls
+ # is not falsely rejected).
+ def min_bytes_per_element(schema, visited = nil)
+ visited ||= {}.compare_by_identity
+ case schema.type_sym
+ when :null then 0
+ when :float then 4
+ when :double then 8
+ when :fixed then schema.size
+ when :record, :error, :request
+ return 0 if visited[schema]
+ visited[schema] = true
+ total = schema.fields.sum { |field|
min_bytes_per_element(field.type, visited) }
+ visited.delete(schema)
+ total
+ else
+ # boolean, int, long, bytes, string, enum, union, array, map: >= 1
byte
+ # (a union encodes at least a 1-byte branch index).
+ 1
+ end
+ end
+
+ # Reject a collection (array or map) block that could drive an unbounded
+ # allocation, before iterating. A block whose declared element count
could
+ # not be backed by the bytes actually remaining is rejected; a block of
+ # zero-byte elements (where the bytes-remaining check does not apply) is
+ # bounded by a cumulative item cap; and every collection is bounded by a
+ # structural cap. Returns the running total across blocks.
+ #
+ # Both limits default to the same values as the other Avro SDKs and can
be
+ # overridden (to a single value capping both) via the
+ # AVRO_MAX_COLLECTION_ITEMS environment variable.
+ DEFAULT_MAX_COLLECTION_ITEMS = 10_000_000 # Zero-byte element cap.
+ DEFAULT_MAX_COLLECTION_STRUCTURAL = 2147483639 # Integer.MAX_VALUE - 8.
+
+ def collection_limits
+ env = Integer(ENV['AVRO_MAX_COLLECTION_ITEMS'], exception: false)
+ if env && env >= 0
+ [env, env]
+ else
+ [DEFAULT_MAX_COLLECTION_ITEMS, DEFAULT_MAX_COLLECTION_STRUCTURAL]
+ end
+ end
+
+ def ensure_collection_available(decoder, total, count,
min_bytes_per_element)
+ # A negative count is corrupt/malicious data (it can also arise from
+ # overflow when negating a negative block count); reject it explicitly.
+ raise CollectionSizeError, "Invalid negative collection block count:
#{count}" if count < 0
Review Comment:
The comment about a negative count arising from overflow when negating a
negative block count is not accurate in Ruby (Integer is arbitrary-precision,
so negation doesn’t overflow). This can confuse future maintainers about when
`count < 0` is actually possible here.
--
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]