Bug #71263 [Opn->Ver]: fread() does not report decompression errors

From: Date: Fri, 17 Apr 2020 13:37:02 +0000
Subject: Bug #71263 [Opn->Ver]: fread() does not report decompression errors
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-226641@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=71263&edit=1

 ID:                 71263
 Updated by:         cmb@php.net
 Reported by:        salsi at icosaedro dot it
-Summary:            fread() does not report bzip2.decompress errors
+Summary:            fread() does not report decompression errors
-Status:             Open
+Status:             Verified
 Type:               Bug
 Package:            Streams related
 Operating System:   Slackware 14.1
 PHP Version:        Irrelevant
-Assigned To:        
+Assigned To:        cmb
 Block user comment: N
 Private report:     N

 New Comment:

The zlib.inflate filter has the same issue.


Previous Comments:
------------------------------------------------------------------------
[2020-04-17 12:13:03] cmb@php.net

The following pull request has been associated:

Patch Name: Fix #71263: fread() does not report bzip2.decompress errors
On GitHub:  https://github.com/php/php-src/pull/5406
Patch:      https://github.com/php/php-src/pull/5406.patch

------------------------------------------------------------------------
[2020-04-17 08:35:41] cmb@php.net

As of PHP 7.4.0, bzip2.decompress errors already cause fread() to
return false, due to a more general fix of the stream behavior[1].
This is still a silent failure, though.

[1] <https://www.php.net/manual/en/migration74.incompatible.php#migration74.incompatible.core.fread-fwrite>

------------------------------------------------------------------------
[2016-01-16 00:27:04] salsi at icosaedro dot it

Possibly related:

1. require* and include* do not detect input/output error
   https://bugs.php.net/bug.php?id=71385

2. fread() does not detect file access error
   https://bugs.php.net/bug.php?id=71384

In all these case it seems I/O errors fails to propagate through the chain of the stream functions,
either returning empty string or garbage, with severe safety and security risks both for front-end
and back-end processes.

------------------------------------------------------------------------
[2016-01-02 16:01:34] salsi at icosaedro dot it

Description:
------------
When the bzip2.decompress filter is applied to a stream, the fread() function does not detect
decoding errors on a corrupted file and keeps instead returning the empty string or garbage. The
following script illustrates 3 distinct cases.

Checking what really happens inside the php_bz2_decompress_filter() function, in 2 of the 3 cases
this function correctly detects an error in the corrupted file and returns PSFS_ERR_FATAL, but in
the script the fread() function always succeeds.

In one case the php_bz2_decompress_filter() function does not detect anything, but fread() succeeds
and returns the empty string.

In my opinion, if a decoding error is detected it is just like reading from a damaged file and
fread() should return FALSE and triggering E_WARNING (which this script would map to
ErrorException).

I've tested on PHP 7.0-dev, but I don't think the PHP version does really matters here.

Unsure if this bug is in some way related to #17651.


Test script:
---------------
<?php
// Bug report: bzip2.decompress fails to detect corrupted data.

// Set a safe environment:
error_reporting(-1);
ini_set("track_errors", "1");

/**
 * Maps errors to ErrorException.
 * @access private
 * @param int $errno
 * @param string $message
 * @return boolean
 * @throws ErrorException
 */
function my_error_handler($errno, $message /*, $filename, $lineno, $context */)
{
	throw new ErrorException($message); 
}

set_error_handler("my_error_handler");


/**
 * Testing reading corrupted BZIP2 file using bzip2.decompress filter.
 * @throws ErrorException
 */
function main()
{
	$plain = "The quick brown fox jumps over the lazy dog.";
	$fn = "test.bz2";
	$compressed = (string) bzcompress($plain);
	echo "Compressed len = ", strlen($compressed), "\n";
	
	// Set a random byte in the middle of the compressed data
	// --> php_bz2_decompress_filter() detects fatal error
	// --> fread() displays empty string then garbage, no errors detected:
	$compressed[strlen($compressed) - 15] = 'X';
	
	// Truncate the compressed data
	// --> php_bz2_decompress_filter() does not detect errors,
	// --> fread() displays the empty string:
//	$compressed = substr($compressed, 0, strlen($compressed) - 20);
	
	// Corrupted final CRC
	// --> php_bz2_decompress_filter() detects fatal error
	// --> fread() displays an empty string, then the correct plain text, no error detected:
//	$compressed[strlen($compressed)-2] = 'X';

	file_put_contents($fn, $compressed);
	
	$r = fopen($fn, "r");
	stream_filter_append($r, 'bzip2.decompress', STREAM_FILTER_READ);
	while( ! feof($r) ){
		$s = fread($r, 100);
		echo "read: "; var_dump($s);
	}
	fclose($r);
	unlink($fn);
}

main();
?>

Expected result:
----------------
Compressed len = 81
Fatal error: Uncaught ErrorException: fread() I/O error in /home/salsi/BZIP2-bug-report.php:24

(or possibly a more descriptive error message, although it might be difficult to do due to the
limitations of the filter interface)

Actual result:
--------------
Compressed len = 81
read: string(0) ""
read: string(43) "bthes ohe rpujumr.bthes ohe rpujumr.bthes o"



------------------------------------------------------------------------



--
Edit this bug report at https://bugs.php.net/bug.php?id=71263&edit=1


Thread (7 messages)

« previous php.bugs (#226641) next »