iemejia commented on code in PR #3856:
URL: https://github.com/apache/avro/pull/3856#discussion_r3567477041
##########
lang/php/lib/DataFile/AvroDataIOReader.php:
##########
@@ -220,18 +234,69 @@ private function readBlockHeader(): string|int
return $this->decoder->readLong();
}
+ /**
+ * The maximum number of bytes a single block is allowed to decompress to.
+ */
+ private static function maxDecompressLength(): int
+ {
+ $value = getenv(self::MAX_DECOMPRESS_LENGTH_ENV);
+ if (false !== $value && ctype_digit($value) && (int) $value > 0) {
+ return (int) $value;
+ }
+
+ return self::DEFAULT_MAX_DECOMPRESS_LENGTH;
+ }
+
+ /**
+ * @throws AvroDataIODecompressionSizeException if the length exceeds the
limit
+ */
+ private static function checkDecompressLength(int $length, int
$maxLength): void
+ {
+ if ($length > $maxLength) {
+ throw new AvroDataIODecompressionSizeException($maxLength);
+ }
+ }
+
/**
* @throws AvroException
*/
private function gzUncompress(string $compressed): string
{
- $datum = gzinflate($compressed);
+ $maxLength = self::maxDecompressLength();
+ $context = inflate_init(ZLIB_ENCODING_RAW);
+ if (false === $context) {
+ throw new AvroException('deflate uncompression failed.');
+ }
- if (false === $datum) {
- throw new AvroException('gzip uncompression failed.');
+ // Inflate in chunks and check the running total after each step so an
+ // over-large (or malicious) block is rejected before its whole output
is
+ // accumulated (each inflated chunk is still materialized), while
genuine
+ // decompression errors (inflate_add === false) are reported
distinctly.
+ // Pieces are collected and joined once at the end to avoid repeatedly
+ // reallocating a growing result string.
+ $pieces = [];
+ $total = 0;
+ $length = strlen($compressed);
+ for ($offset = 0; $offset < $length; $offset +=
self::INFLATE_CHUNK_SIZE) {
+ $piece = substr($compressed, $offset, self::INFLATE_CHUNK_SIZE);
+ $out = @inflate_add($context, $piece);
+ if (false === $out) {
+ throw new AvroException('deflate uncompression failed.');
Review Comment:
Fixed — removed the @ from inflate_add(); the false-return check + exception
is kept, so zlib warnings are no longer suppressed.
##########
lang/php/lib/DataFile/AvroDataIOReader.php:
##########
@@ -220,18 +234,69 @@ private function readBlockHeader(): string|int
return $this->decoder->readLong();
}
+ /**
+ * The maximum number of bytes a single block is allowed to decompress to.
+ */
+ private static function maxDecompressLength(): int
+ {
+ $value = getenv(self::MAX_DECOMPRESS_LENGTH_ENV);
+ if (false !== $value && ctype_digit($value) && (int) $value > 0) {
+ return (int) $value;
+ }
+
+ return self::DEFAULT_MAX_DECOMPRESS_LENGTH;
+ }
+
+ /**
+ * @throws AvroDataIODecompressionSizeException if the length exceeds the
limit
+ */
+ private static function checkDecompressLength(int $length, int
$maxLength): void
+ {
+ if ($length > $maxLength) {
+ throw new AvroDataIODecompressionSizeException($maxLength);
+ }
+ }
+
/**
* @throws AvroException
*/
private function gzUncompress(string $compressed): string
{
- $datum = gzinflate($compressed);
+ $maxLength = self::maxDecompressLength();
+ $context = inflate_init(ZLIB_ENCODING_RAW);
+ if (false === $context) {
+ throw new AvroException('deflate uncompression failed.');
+ }
- if (false === $datum) {
- throw new AvroException('gzip uncompression failed.');
+ // Inflate in chunks and check the running total after each step so an
+ // over-large (or malicious) block is rejected before its whole output
is
+ // accumulated (each inflated chunk is still materialized), while
genuine
+ // decompression errors (inflate_add === false) are reported
distinctly.
+ // Pieces are collected and joined once at the end to avoid repeatedly
+ // reallocating a growing result string.
+ $pieces = [];
+ $total = 0;
+ $length = strlen($compressed);
+ for ($offset = 0; $offset < $length; $offset +=
self::INFLATE_CHUNK_SIZE) {
+ $piece = substr($compressed, $offset, self::INFLATE_CHUNK_SIZE);
+ $out = @inflate_add($context, $piece);
+ if (false === $out) {
+ throw new AvroException('deflate uncompression failed.');
+ }
+ $pieces[] = $out;
+ $total += strlen($out);
+ self::checkDecompressLength($total, $maxLength);
}
- return $datum;
+ $out = @inflate_add($context, '', ZLIB_FINISH);
+ if (false === $out) {
+ throw new AvroException('deflate uncompression failed.');
Review Comment:
Fixed — removed the @ from the ZLIB_FINISH inflate_add() call too.
--
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]