php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #60671 fread does not fail when operating on a write only stream
Submitted: 2012-01-06 14:29 UTC Modified: 2017-10-24 07:58 UTC
Votes:3
Avg. Score:3.7 ± 0.9
Reproduced:2 of 2 (100.0%)
Same Version:1 (50.0%)
Same OS:1 (50.0%)
From: james dot turner dot phpninja at gmail dot com Assigned:
Status: Open Package: Streams related
PHP Version: 5.3.8 OS: Ubuntu 11.04
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2012-01-06 14:29 UTC] james dot turner dot phpninja at gmail dot com
Description:
------------
fread does not throw or generate any error when attempting to read from a write only file stream.

Test script:
---------------
<?php

$tmp = tempnam(sys_get_temp_dir(), 'test_');

$stream = fopen($tmp, 'w');
$data = "";

while(!feof($stream)){
  if(false === ($data .= fread($stream, 8192))){
    break;
  };
}

?>

Expected result:
----------------
Either feof to return false indicating end of file
Or fread erroring or returning false as a result of attempting to read a write-only stream.

Actual result:
--------------
An infinite loop will occur.
feof will never end (doesn't reach the end of the file because it's in write mode)
fread will never error despite attempting to read from a write only stream.

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-01-06 16:01 UTC] phpmpan at mpan dot pl
CONFIRMED for both 5.3.8 and 5.3.7 on Arch64, and for 5.3.4 on an unknown Linux.

Note however, that the test script provided by OP is wrong. It should be:
------------ BEGIN CODE ------------
$tmp = tempnam(sys_get_temp_dir(), 'test_');

$stream = fopen($tmp, 'w');
$data = "";

while(!feof($stream)){
  if(false === ($data = fread($stream, 8192))){
    break;         // ^---- no dot here
  };
}
------------- END CODE -------------
OP's code will fail regardless of the bug, because .= always produces a string.
 [2012-01-06 21:17 UTC] cataphract@php.net
-Status: Open +Status: Bogus
 [2012-01-06 21:17 UTC] cataphract@php.net
This is not a bug. fread only returns false if given invalid arguments. The bug is that you try to read from a stream that's write-only.

This C program has analogous behavior:

#include<stdio.h>

int main(void)
{
    FILE *f = fopen("/tmp/foobaz", "w");
    printf("feof: %d\n", feof(f));
    printf("read: %zd\n", fread((char[100]) {}, 1, 100, f));
    printf("feof: %d\n", feof(f));
    return 0;
}

gcc --std=c99 h.c && ./a.out
feof: 0
read: 0
feof: 0
 [2012-01-08 02:51 UTC] phpmpan at mpan dot pl
Let me change your code just a bit:
-------------------------------------------
#include<stdio.h>

int main(void)
{
    FILE *f = fopen("/tmp/foobaz", "w");
    printf("feof: %d\n", feof(f));
    printf("ferror: %d\n", ferror(f)); // <- HERE
    printf("read: %zd\n", fread((char[100]) {}, 1, 100, f));
    printf("feof: %d\n", feof(f));
    printf("ferror: %d\n", ferror(f)); // <- AND HERE
    return 0;
}

gcc --std=c99 h.c && ./a.out
feof: 0
ferror: 0
read: 0
feof: 0
ferror: 1

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

In PHP there is no `ferror`. `feof` does work of both stdio's `feof` and `ferror`, as described in the documentation [1]: "Returns TRUE if the file pointer is at EOF or an error occurs".
Therefore, if we use analogy to stdio, `feof` should return `TRUE` in this case.

[1] http://pl.php.net/manual/en/function.feof.php
 [2012-01-08 09:29 UTC] james dot turner dot phpninja at gmail dot com
I feel I should say that just because the behaviour is analogous to the way the underlying C works does not make it correct nor useful.
While I concur that one should not really attempt to read from a write-only stream, I DO feel that any attempt to perform such an action should warrant an exception/warning/error of some sort. This is currently not the case and as a result can lead to potential indefinite loops given recommended usage in the PHP documentation.
Perhaps this should be a feature/change request?
 [2012-01-08 21:45 UTC] phpmpan at mpan dot pl
You are right: no one should try to read from write-only stream. Also no one should try to divide by 0 and no one should pass something other than stream to `fread`. But people do it all the time and that's why errors, warnings, exceptions and status codes exist. Using an invalid type of a stream is an error too and should be reported.

If returning `FALSE` from `fread` breaks something, than I agree: `fread` bahaviour should not be changed. However a warning does not break anything, so it can be emitted. Since `php_stream` stores `fopen` flags in `mode`, I believe this can be done easily:
--------------- BEGIN DIFF ---------------
diff php5.3-201201041830/ext/standard/file.c php5.3-201201041830-modified/ext/standard/file.c
1893a1894,1898
> 	
> 	if (strpbrk(stream->mode, "+r") == NULL) { /* r or any + mode is fine */
> 	    php_error_docref(NULL TSRMLS_CC, E_WARNING,
> 	          "Reading from a write-only stream");
> 	}
---------------- END DIFF ----------------
After this modification some of ext/standard/tests/file/007* tests will fail, but this is expected.

I agree that this case is a feature request.
 [2012-01-10 13:38 UTC] cataphract@php.net
All right, I'll give a bit more thought to this.
 [2012-01-10 13:38 UTC] cataphract@php.net
-Status: Bogus +Status: Open -Assigned To: +Assigned To: cataphract
 [2016-01-03 00:05 UTC] salsi at icosaedro dot it
The problem here is more general, as there is no way to detect file access errors because, as already stated, the ferror() function of the libc has no a corresponding implementation under PHP. In my opinion, on an application language as PHP is, such a low level function should not exist and instead should be fread() to return FALSE when it fails reading from a file because a I/O error of any kind happened, and should emit E_WARNING with appropriate message. The same should apply to any other file access function as well.

If it may be useful, i wrote this script that simulates a I/O error on Linux, and illustrates how the access errors cannot be detected from PHP code:

<?php
// fread() fails to detect I/O error, bug #60671.
// Test code working on Linux only.

//
//  Set a safe environment:
//

error_reporting(-1);
ini_set("track_errors", "1");


function my_error_handler($errno, $message /*, $filename, $lineno, $context */)
{
	throw new ErrorException("$errno: $message"); 
}

set_error_handler("my_error_handler");


function main()
{
	// Simulate I/O error in Linux (credits http://unix.stackexchange.com/a/6302):
	// reading of the first 512-bytes block of /proc/self/mem causes EIO in libc.
	// $ cat /proc/self/mem
	// cat: /proc/self/mem: Input/output error
	$r = fopen("/proc/self/mem", "r");
	while( ! feof($r) ){
		$s = fread($r, 100);
		echo "read: "; var_dump($s);
	}
	fclose($r);
	// Outcome: no errors whatsoever, only displays:
	// read: string(0) ""
}

main();
 [2017-10-24 07:58 UTC] kalle@php.net
-Status: Assigned +Status: Open -Assigned To: cataphract +Assigned To:
 
PHP Copyright © 2001-2018 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC