php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #71417 fread() does not detect decoding errors from filter zlib.inflate
Submitted: 2016-01-20 02:57 UTC Modified: 2017-12-09 11:02 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:0 of 1 (0.0%)
From: salsi at icosaedro dot it Assigned:
Status: Open Package: Streams related
PHP Version: master-Git-2016-01-20 (Git) OS: Slackware 14.1
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2016-01-20 02:57 UTC] salsi at icosaedro dot it
Description:
------------
When the zlib.inflate filter is applied to a gzip 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:

1. data corrupted
2. file truncated (commented away)
3. invalid CRC    (commented away)
4. invalid length (commented away)

For comparison, the comments in the script also report what the 'gzip' command tells about these corrupted files.
Also the gzdecode() function works properly and always triggers error "data error".

In all the 4 cases above, no errors nor exceptions are generated, although the data read are either corrupted or truncated.

Unsure if this bug is in some way related to bug #71263 (same issues with the bzip2.decompress filter).


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

// Set a safe environment:
error_reporting(-1);

// Maps errors to ErrorException.
function my_error_handler($errno, $message)
{ 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-zlib-inflate.gz";
	$compressed = (string) gzencode($plain);
	
	// 0. No corruption.
	// $ php test-zlib-inflate.php
	// --> read: string(44) "The quick brown fox jumps over the lazy dog."
	// $ gzip test-zlib-inflate.gz
	// (generates the file test-zlib-inflate with correct content)
	
	// 1. Set a random byte in the middle of the compressed data.
	// $ php test-zlib-inflate.php
	// --> read: string(0) ""
	// --> read: string(44) "The quick brown fox jumps over the lazx8dog."
	// $ gzip test-zlib-inflate.gz
	// gzip: test-zlib-inflate.gz: invalid compressed data--crc error
//	$compressed[strlen($compressed) - 15] = 'X';
	
	// 2. Truncate the compressed data.
	// $ php test-zlib-inflate.php
	// --> read: string(32) "The quick brown fox jumps over t"
	// $ gzip test-zlib-inflate.gz
	// gzip: test-zlib-inflate.gz: unexpected end of file
//	$compressed = substr($compressed, 0, strlen($compressed) - 20);
	
	// 3. Corrupted final CRC.
	// $ php test-zlib-inflate.php
	// --> read: string(0) ""
	// --> read: string(44) "The quick brown fox jumps over the lazy dog."
	// $ gzip test-zlib-inflate.gz
	// gzip: test-zlib-inflate.gz: invalid compressed data--crc error
//	$compressed[strlen($compressed)-5] = 'X';
	
	// 4. Corrupted final length.
	// $ php test-zlib-inflate.phpread: string(0) ""
	// read: string(44) "The quick brown fox jumps over the lazy dog."
	// $ gunzip test-zlib-inflate.gz 
	// gzip: test-zlib-inflate.gz: invalid compressed data--length error
	$compressed[strlen($compressed)-2] = 'X';
	
	// The gzdecode() function applied to the corrupted compressed data always
	// detects the error:
	// --> gzdecode(): PHP Fatal error:  Uncaught ErrorException: gzdecode(): data error in ...
//	echo "gzdecode(): ", rawurldecode(gzdecode($compressed)), "\n";

	file_put_contents($fn, $compressed);
	
	$r = fopen($fn, "r");
	stream_filter_append($r, 'zlib.inflate', STREAM_FILTER_READ, array('window' => 15+16));
	while( ! feof($r) ){
		$s = fread($r, 100);
		echo "read: "; var_dump($s);
	}
	fclose($r);
//	unlink($fn);
}

main();
?>

Expected result:
----------------
Fatal error: Uncaught ErrorException: fread() I/O error in ...

(or possibly a more descriptive error message similar to one of those returned by the gzip command)


Actual result:
--------------
read: string(0) ""
read: string(44) "The quick brown fox jumps over the lazx8dog."


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-01-20 11:06 UTC] salsi at icosaedro dot it
...and in general errors that are certainly generated internally to the zlib library are not reported to the user's application, resulting in missing or no data read at all.

This happens, for example, when an highly compressible stream (say, a source program at least 40 KB long) that was compressed with a large window (window=15+16), is then decompressed with a smaller window (window=9+16). In this case fread() simply returns the empty string before the feof() (source code available).

At this point these issues seem more related to the general implementation of the stream filters that to a specific filter, but I don't know enough of the internals stuff to tell for sure.

FIX: in the test script above any reference to "bzip2" in the comments should be read "zlib" :->
 [2017-12-09 11:02 UTC] salsi at icosaedro dot it
Probably related to bug #71384 -- see for the general discussion about missing error propagation in the streams layer.
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC