php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #32171 Userspace stream wrapper crashes PHP
Submitted: 2005-03-03 12:37 UTC Modified: 2005-05-16 10:50 UTC
From: jr at terragate dot net Assigned: tony2001
Status: Closed Package: Reproducible crash
PHP Version: 5.* OS: *
Private report: No CVE-ID:
 [2005-03-03 12:37 UTC] jr at terragate dot net
Description:
------------
PEAR's HTTP_WebDAV_Client causes PHP 5 to trow a deprecated warning as Exception:

Fatal error: Uncaught exception 'Exception' with message 'is_a(): Deprecated. Please use the instanceof operator' in /usr/lib/php/PEAR.php:248
Stack trace:
#0 /usr/lib/php/PEAR.php(248): is_a(true, 'PEAR_Error')
#1 /usr/lib/php/HTTP/Request.php(591): HTTP_Request->isError(true)
#2 /usr/lib/php/HTTP/WebDAV/Client/Stream.php(796): HTTP_Request->sendRequest()
#3 /usr/lib/php/HTTP/WebDAV/Client/Stream.php(446): HTTP_WebDAV_Client_Stream->_check_options()
#4 /var/www/localhost/htdocs/crash.php(5): HTTP_WebDAV_Client_Stream->dir_opendir('webdav://test:t...', 4)
#5 /var/www/localhost/htdocs/crash.php(5): DirectoryIterator->__construct('webdav://test:t...')
#6 {main}
  thrown in /usr/lib/php/PEAR.php on line 248

Is this a bug or a feature (deprecation messages thrown as Exceptions)?

Changing the is_a in PEAR.php to instanceof causes a segfault.

Reproduce code:
---------------
<?php

require_once('HTTP/WebDAV/Client.php');

$it = new DirectoryIterator('webdav://test:test@webdav-host/');

echo 'Done';

?>

for the 'deprecation exception' and a additional change for the segfault:

PEAR.php ~line 248:

if ($data instanceof PEAR_Error) {

instead of:

if (is_a($data, 'PEAR_Error')) {

This requires a valid webdav URL (I can provide one if necessary).

Expected result:
----------------
Clean shutdown

Actual result:
--------------
[Thread debugging using libthread_db enabled]
[New Thread 16384 (LWP 22648)]
Done
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 16384 (LWP 22648)]
0x00000019 in ?? ()
#0  0x00000019 in ?? ()
#1  0x0829ca06 in _php_stream_free (stream=0x8652714, close_options=3)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/main/streams/streams.c:351
#2  0x081b0d74 in spl_ce_dir_object_free_storage (object=0x85e067c)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/ext/spl/spl_directory.c:66
#3  0x082e939d in zend_objects_store_del_ref (zobject=0x85f4430)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_objects_API.c:159
#4  0x082cc90f in _zval_dtor (zvalue=0x85da6ac,
    __zend_filename=0x84a0800 "/var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_execute_API.c", __zend_lineno=392)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_variables.c:61
#5  0x082c19cb in _zval_ptr_dtor (zval_ptr=0x85ddda8,
    __zend_filename=0x85f4430 "xA_\b\uffff\001^\b", __zend_lineno=140461104)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_execute_API.c:392
#6  0x082ccce2 in _zval_ptr_dtor_wrapper (zval_ptr=0x85f4430)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_variables.c:193
#7  0x082d976f in zend_hash_apply_deleter (ht=0x85131b0, p=0x85ddd9c)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_hash.c:574
#8  0x082d707c in zend_hash_graceful_reverse_destroy (ht=0x85131b0)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_hash.c:640
#9  0x082c123a in shutdown_executor ()
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend_execute_API.c:208
#10 0x082cddde in zend_deactivate ()
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/Zend/zend.c:818
#11 0x08288352 in php_request_shutdown (dummy=0x0)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/main/main.c:1212
#12 0x083111b7 in main (argc=2, argv=0xbffff844)
    at /var/tmp/portage/php-5.0.3/work/php-5.0.3/sapi/cli/php_cli.c:1046


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2005-03-03 15:05 UTC] jr at terragate dot net
I was able to create a smaller example for the exception:

<?php

class StreamWrapper
{
  public function dir_opendir($path, $options) {
    return is_a(null, 'AKnownOrUnknownClass');
  }

}

stream_wrapper_register('test', 'StreamWrapper');
$it = new DirectoryIterator('test://path/');

?>

This will raise the following exception:

Fatal error: Uncaught exception 'Exception' with message 'is_a(): Deprecated. Please use the instanceof operator' in /var/www/localhost/htdocs/splexception.php:6
Stack trace:
#0 /var/www/localhost/htdocs/splexception.php(6): is_a(NULL, 'AKnownOrUnknown...')
#1 /var/www/localhost/htdocs/splexception.php(13): StreamWrapper->dir_opendir('test://path/', 4)
#2 /var/www/localhost/htdocs/splexception.php(13): DirectoryIterator->__construct('test://path/')
#3 {main}
  thrown in /var/www/localhost/htdocs/splexception.php on line 6


This problem does not occur when using opendir so this seems to be SPL related. 

Category changed accordingly 

I wasn't able to reproduce the instanceof crash with this small snippet.
 [2005-03-03 17:13 UTC] jr at terragate dot net
After digging a while in the PHP source I found the reason for the thrown exception.

The SPL sets the php error handling mode to EH_THROW. 

But currently php_error_cb throws everything as exception, even notices (E_NOTICE), warnings (E_WARNING) and strict warnings caused by E_STRICT.

IMHO those shouldn't be thrown as exception. 

I've made a path that fixes this. E_NOTICE, E_WARNING and E_STRICT will be handled the same way as in EH_NORMAL.

Probably E_USER_NOTICE and E_USER_WARNING should be added.

http://www.terragate.net/~jr/no_notices_and_warnings_as_exception.diff
 [2005-03-04 16:34 UTC] sniper@php.net
Please try using this CVS snapshot:

  http://snaps.php.net/php5-latest.tar.gz
 
For Windows:
 
  http://snaps.php.net/win32/php5-win32-latest.zip


 [2005-03-04 18:48 UTC] jr at terragate dot net
I already tested the bug against yesterdays snapshot of 
5.0.x.

Using the 5.1.0 snap does not resolve the exception 
issue. 

I will test the instanceof segfault on monday against 
the 5.1 branch.

Maybe I should create a seperate bug for it.

 

voyager:~/Downloads/php5-200503041530/result/bin jr$ 
uname -a
Darwin voyager.starbase12.sfn 7.8.0 Darwin Kernel 
Version 7.8.0: Wed Dec 22 14:26:17 PST 2004; root:xnu/
xnu-517.11.1.obj~1/RELEASE_PPC  Power Macintosh powerpc
voyager:~/Downloads/php5-200503041530/result/bin jr$ ./
php -v
PHP 5.1.0-dev (cgi) (built: Mar  4 2005 18:33:26) 
(DEBUG)
Copyright (c) 1997-2004 The PHP Group
Zend Engine v2.1.0-dev, Copyright (c) 1998-2004 Zend 
Technologies
voyager:~/Downloads/php5-200503041530/result/bin jr$ ./
php test.php 
Content-type: text/html
X-Powered-By: PHP/5.1.0-dev

<br />
<b>Fatal error</b>:  Uncaught exception 'Exception' with 
message 'is_a(): Deprecated. Please use the instanceof 
operator' in /Volumes/Data/Users/jr/Downloads/php5
-200503041530/result/bin/test.php:6
Stack trace:
#0 /Volumes/Data/Users/jr/Downloads/php5-200503041530/
result/bin/test.php(12): StreamWrapper-
>dir_opendir(NULL, 'AKnownOrUnknown...')
#1 /Volumes/Data/Users/jr/Downloads/php5-200503041530/
result/bin/test.php(12): DirectoryIterator-
>__construct('test://path/', 4)
#2 {main}
  thrown in <b>/Volumes/Data/Users/jr/Downloads/php5
-200503041530/result/bin/test.php</b> on line <b>6</
b><br />
/Volumes/Data/Users/jr/Downloads/php5-200503041530/
result/bin/test.php(6) : Fatal error - Uncaught 
exception 'Exception' with message 'is_a(): Deprecated. 
Please use the instanceof operator' in /Volumes/Data/
Users/jr/Downloads/php5-200503041530/result/bin/
test.php:6
Stack trace:
#0 /Volumes/Data/Users/jr/Downloads/php5-200503041530/
result/bin/test.php(12): StreamWrapper-
>dir_opendir(NULL, 'AKnownOrUnknown...')
#1 /Volumes/Data/Users/jr/Downloads/php5-200503041530/
result/bin/test.php(12): DirectoryIterator-
>__construct('test://path/', 4)
#2 {main}
  thrown
 [2005-03-06 16:21 UTC] sniper@php.net
Please don't open more reports about same issue. (and when you report bugs, put the LATEST version in the 'Version' field' so we don't have to waste time asking if you tested the latest version..)

 [2005-03-07 11:25 UTC] jr at terragate dot net
I tested the instanceof segfault against the 5.1 branch and it segfaults too. 

But I had to change a is_a in HTTP/Request.php to instanceof because the 'notice exception' was thrown there this time.

I wasn't able to reproduce the segfault with a smaller test case by using HTTP/Request.php myself (PEAR's WebDAV Wrapper) nor using instanceof inside a small stream wrapper.

Initially I tested the bug with 5.0.3 but tried a snap a few hours later. Sorry for not updating the version field.
 [2005-03-09 14:40 UTC] helly@php.net
Did i get that correct that all works frin when you use instanceof ? If so all is fine. Also what happens if you stick with is_a but set error mode to 0?
 [2005-03-09 17:52 UTC] jr at terragate dot net
Finally I was able to create a smaller test case for the segfault (with error_reporting = E_ALL):

<?php

class StreamWrapper
{
  public function dir_opendir($path, $options) {
    return TRUE;
  }

  public function dir_readdir()
  {
    return FALSE;
  }
}

stream_wrapper_register('test', 'StreamWrapper');
$it = new DirectoryIterator('test://path/');

echo "Done\n";

?>

Trace:

(gdb) r crash.php
Starting program: /usr/local/bin/php crash.php
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
[Thread debugging using libthread_db enabled]
[New Thread 16384 (LWP 15212)]
Done

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 16384 (LWP 15212)]
0x00000019 in ?? ()
Current language:  auto; currently asm
(gdb) bt
#0  0x00000019 in ?? ()
#1  0x081abc73 in _php_stream_free (stream=0x82fdce4, close_options=3)
    at /root/compile/php5-STABLE-200503091530/main/streams/streams.c:351
#2  0x080b3fb8 in spl_ce_dir_object_free_storage (object=0x82f76c4)
    at /root/compile/php5-STABLE-200503091530/ext/spl/spl_directory.c:66
#3  0x081fa906 in zend_objects_store_del_ref (zobject=0x82fd4e4)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_objects_API.c:159
#4  0x081deb76 in _zval_dtor (zvalue=0x82fd4e4,
    __zend_filename=0x82549e0 "/root/compile/php5-STABLE-200503091530/Zend/zend_execute_API.c", __zend_lineno=392)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_variables.c:61
#5  0x081d36f8 in _zval_ptr_dtor (zval_ptr=0x82fdda0,
    __zend_filename=0x8255940 "/root/compile/php5-STABLE-200503091530/Zend/zend_variables.c", __zend_lineno=193)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_execute_API.c:392
#6  0x081dee88 in _zval_ptr_dtor_wrapper (zval_ptr=0x82fdda0)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_variables.c:193
#7  0x081e8f13 in zend_hash_apply_deleter (ht=0x82761d0, p=0x82fdd94)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_hash.c:574
#8  0x081e9164 in zend_hash_graceful_reverse_destroy (ht=0x82761d0)
    at /root/compile/php5-STABLE-200503091530/Zend/zend_hash.c:640
#9  0x081d302f in shutdown_executor ()
    at /root/compile/php5-STABLE-200503091530/Zend/zend_execute_API.c:208
#10 0x081e0264 in zend_deactivate ()
    at /root/compile/php5-STABLE-200503091530/Zend/zend.c:817
#11 0x081996e1 in php_request_shutdown (dummy=0x0)
    at /root/compile/php5-STABLE-200503091530/main/main.c:1214
#12 0x082155d0 in main (argc=2, argv=0xbffff844)
    at /root/compile/php5-STABLE-200503091530/sapi/cli/php_cli.c:1046


The script will be fully executed but php segfaults on shutdown. The behavior in the complex test case (with the WebDAV stream wrapper) was the same: Using instaneof instead of is_a caused the script to be fully executed but with a segfault on shutdown.

To answer your second question I modified the test case above:

<?php

class StreamWrapper
{
  public function dir_opendir($path, $options) {
    is_a(null, 'AKnownOrUnknownClass');
    return TRUE;
  }

  public function dir_readdir()
  {
    return FALSE;
  }
}

stream_wrapper_register('test', 'StreamWrapper');
$it = new DirectoryIterator('test://path/');

echo "Done\n";

?>

Running this script with error_reporting set to E_ALL (or even E_ALL & ~E_NOTICE & ~E_STRICT) will lead to the behaviour already mentioned (deprecation warning thrown as exception).

Running the script with error_reporting = 0 will terminate the script with exit code 0377 and without outputting 'Done'.

Using gdb I figured out that php_error_cb is still called with the deprecation warning and zend_throw_exception will abort the script.

We have two issues here:

1. A wrong free causing a segfault on shutdown
2. PHP notices and warnings thrown as exception

I dont't know what to do with the segfault (my knowledge about PHP's internals is too limited to debug this yet).

IMHO the second problem could be solved in 2 ways:

1. Modifying php_error_cb's behavior (as my patch does)
2. Do not set error_mode to EH_THROW in spl_directory.c if a user space stream wrapper is used.
 [2005-05-11 16:43 UTC] tony2001@php.net
It has nothing to do with SPL, it's stream-related problem.
Reassigned to myself, patch pending..
 [2005-05-16 10:50 UTC] tony2001@php.net
This bug has been fixed in CVS.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.
 
Thank you for the report, and for helping us make PHP better.


 
PHP Copyright © 2001-2014 The PHP Group
All rights reserved.
Last updated: Fri Apr 18 13:02:15 2014 UTC