Copilot commented on code in PR #3864:
URL: https://github.com/apache/avro/pull/3864#discussion_r3565112875


##########
lang/perl/lib/Avro/BinaryDecoder.pm:
##########
@@ -132,10 +132,95 @@ sub decode_bytes {
     my $class = shift;
     my $reader = pop;
     my $size = decode_long($class, undef, undef, $reader);
+    _ensure_available($reader, $size);
     $reader->read(my $buf, $size);
     return $buf;
 }
 
+## 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. Only enforced for larger reads and when the reader can
+## report its size via seek/tell; smaller reads and non-seekable readers fall
+## through to a direct read.
+use constant _MAX_UNCHECKED_READ => 1024 * 1024;
+
+## Number of bytes still available to read, or undef 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. Readers
+## that do not support seeking to the end (e.g. a streaming decompressor) 
return
+## undef, so the check is simply skipped for them.
+sub _bytes_remaining {
+    my ($reader) = @_;
+    my $current = eval { $reader->tell };
+    return if !defined $current || $current < 0;
+    return eval {
+        $reader->seek(0, 2)         # SEEK_END
+            or die "seek to end failed\n";
+        my $end = $reader->tell;
+        $reader->seek($current, 0); # SEEK_SET, restore position
+        die "unknown end position\n" if !defined $end || $end < 0;
+        $end - $current;
+    };
+}

Review Comment:
   `_bytes_remaining` uses a single `eval` around the seek/tell/restore 
sequence. If `tell` after seeking to end dies, or if restoring the position 
fails, the function returns undef and the decoder may continue with the reader 
left at EOF, corrupting subsequent decoding. The size check should (1) always 
attempt to restore the original position and (2) treat restore failure as a 
hard error rather than silently skipping the check.



##########
lang/perl/lib/Avro/BinaryDecoder.pm:
##########
@@ -296,13 +383,16 @@ sub decode_map {
     my $block_count = decode_long($class, @_);
     my $writer_values = $writer_schema->values;
     my $reader_values = $reader_schema->values;
+    # Map keys are strings (>= 1 byte length prefix) plus the value.
+    my $min_bytes = 1 + _min_bytes_per_element($writer_values);

Review Comment:
   The map block size check currently assumes keys add only 1 byte (the length 
prefix), but this decoder later rejects empty keys (`length $key` check). Given 
that constraint, the minimum on-wire key encoding is at least 2 bytes (1-byte 
length prefix + 1 byte data). Using 1 here makes the pre-check weaker than it 
can be and may miss some truncated-map cases that could be rejected before 
iterating.



##########
lang/perl/lib/Avro/BinaryDecoder.pm:
##########
@@ -132,10 +132,95 @@ sub decode_bytes {
     my $class = shift;
     my $reader = pop;
     my $size = decode_long($class, undef, undef, $reader);
+    _ensure_available($reader, $size);
     $reader->read(my $buf, $size);

Review Comment:
   `decode_bytes` trusts the decoded length prefix and will pass negative sizes 
through to `read`, which can cause incorrect behavior / misalignment. Avro 
bytes/string lengths must be non-negative, so reject a negative decoded length 
explicitly before continuing.



-- 
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]

Reply via email to