php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #53306 php crashes with segfault when DTrace "exception-thrown" probe fires
Submitted: 2010-11-13 23:12 UTC Modified: 2010-11-17 22:41 UTC
From: mike at harschsystems dot com Assigned: dsp (profile)
Status: Closed Package: Reproducible crash
PHP Version: trunk-SVN-2010-11-13 (snap) OS: Mac OS X and Solaris
Private report: No CVE-ID: None
 [2010-11-13 23:12 UTC] mike at harschsystems dot com
Description:
------------
When DTrace is present, and a DTrace consumer has enabled the "php*:::exception-
thrown" probe, php will crash when the probe fires, due to a null reference passed 
to zend_get_object_classname() from within the probe context.  The code within the 
DTrace probe context (inside zend_throw_exception_internal() ) doesn't check that 
the 'exception' argument is non-NULL.  The test described here obviously creates 
an instance where 'exception' is NULL, and when the enabled probe fires - the 
enabled code calls zend_get_object_classname() with a null argument, resulting in 
the segfault.

Test script:
---------------
In order to reproduce this, you must be running on a system that supports DTrace (OS X, Solaris, FreeBSD?), and php must have been built with --enable-dtrace.

The following script will trigger the exception codepath we're interested in:
<html><head><title>Test for PHP Exceptions</title></head><body>
<?php
	function my_func($my_arg) {
		if ($my_arg == 0)
			throw new Exception('You cannot do that.');
	}

	try {
		echo '<p>attempting to call my_func with my_arg == 0';

		my_func(0);

		echo 'this will not be executed';

	} catch (Exception $e) {
		echo "<p>caught exception: " . $e->getMessage();
	} 
?>
</body></html>

This will run fine when DTrace hasn't enabled the exception-thrown probe, but if we run the following command (as root) at the time that the above script is requested, php will crash.

# dtrace -n 'php*:::exception-thrown {}'

The attached patch shows how the problem could be avoided - though I'd like to hear from someone familiar with the Zend framework - to see if there may be an upstream bug that's causing the NULL value to come into zend_throw_exception_internal() in the first place.  If this is expected behavior, we should anticipate it and provide appropriate handling within the DTrace probe.



Expected result:
----------------
PHP shouldn't crash.

Actual result:
--------------
PHP crashes as shown:


Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x0000000000000008
0x000000010156818b in zend_get_object_classname (object=0x0, 
class_name=0x7fff5fbfeb60, 
class_name_len=0x7fff5fbfeb6c) at /Users/michaelharsch/Desktop/php-trunk-
201011131530/Zend/zend_API.c:253
253          if (Z_OBJ_HT_P(object)->get_class_name == NULL ||
(gdb) bt
#0  0x000000010156818b in zend_get_object_classname (object=0x0, 
class_name=0x7fff5fbfeb60, 
class_name_len=0x7fff5fbfeb6c) at /Users/michaelharsch/Desktop/php-trunk-
201011131530/Zend/zend_API.c:253
#1  0x0000000101589e2e in zend_throw_exception_internal (exception=0x0) at 
/Users/michaelharsch/Desktop/php-trunk-
201011131530/Zend/zend_exceptions.c:90
#2  0x00000001015a6543 in zend_do_fcall_common_helper_SPEC 
(execute_data=0x10051a0d8) at zend_vm_execute.h:735
#3  0x00000001015acc98 in ZEND_DO_FCALL_SPEC_CONST_HANDLER 
(execute_data=0x10051a0d8) at zend_vm_execute.h:2015
#4  0x00000001015a4075 in execute (op_array=0x10054f030) at 
zend_vm_execute.h:410
#5  0x000000010154e6e5 in dtrace_execute (op_array=0x10054f030) at 
/Users/michaelharsch/Desktop/php-trunk-
201011131530/Zend/zend_dtrace.c:75
#6  0x00000001015674c6 in zend_execute_scripts (type=8, retval=0x0, 
file_count=3) at 
/Users/michaelharsch/Desktop/php-trunk-201011131530/Zend/zend.c:1195
#7  0x00000001014d28e3 in php_execute_script (primary_file=0x7fff5fbff810) at 
/Users/michaelharsch/Desktop/php-trunk-
201011131530/main/main.c:2340
#8  0x00000001016a39c5 in php_handler (r=0x10098a2a8) at 
/Users/michaelharsch/Desktop/php-trunk-
201011131530/sapi/apache2handler/sapi_apache2.c:667
#9  0x00000001000021db in ap_run_handler ()
#10 0x0000000100002aba in ap_invoke_handler ()
#11 0x000000010002f738 in ap_process_request ()
#12 0x000000010002bfa9 in ap_process_http_connection ()
#13 0x0000000100013737 in ap_run_process_connection ()
#14 0x0000000100013bd1 in ap_process_connection ()
#15 0x00000001000363f2 in child_main ()
#16 0x00000001000364dc in make_child ()
#17 0x0000000100036aaf in ap_mpm_run ()
#18 0x000000010000a821 in main ()


Patches

exception_fix2.patch (last revision 2010-11-13 22:23 UTC by mike at harschsystems dot com)

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2010-11-13 23:19 UTC] felipe@php.net
-Status: Open +Status: Feedback
 [2010-11-13 23:19 UTC] felipe@php.net
Where is the patch?
 [2010-11-13 23:25 UTC] mike at harschsystems dot com
sorry, just uploaded patch file "exception_fix2.patch"
 [2010-11-13 23:31 UTC] felipe@php.net
-Status: Feedback +Status: Assigned -Assigned To: +Assigned To: dsp
 [2010-11-14 00:42 UTC] mike at harschsystems dot com
With the patch in place, I can use the following DTrace script to see exceptions 
getting thrown and caught (differentiating those with null input from those with 
valid pointers):


#!/usr/sbin/dtrace -s
#pragma D option quiet

php*:::exception-thrown
/arg0 == NULL/
{
	printf("PHP Exception Thrown. Classname: Unknown\n");
}

php*:::exception-thrown
/arg0 != NULL/
{
	printf("PHP Exception Thrown. ");
	printf("Classname: %s\n", copyinstr(arg0));
}

php*:::exception-caught
{
	printf("PHP Exception Caught. ");
	printf("Classname: %s\n", copyinstr(arg0));
}


Running this script with the trivial exception script mentioned in this bug, 
produces the following output:

# ./php_exception.d 
PHP Exception Thrown. Classname: Exception
PHP Exception Thrown. Classname: Unknown
PHP Exception Caught. Classname: Exception
^C

So, we see that 2 exceptions are being thrown (one with a valid pointer and one 
with a null pointer), and one exception is caught.
 [2010-11-17 22:41 UTC] felipe@php.net
Automatic comment from SVN on behalf of felipe
Revision: http://svn.php.net/viewvc/?view=revision&amp;revision=305472
Log: - Fixed bug #53306 (php crashes with segfault when DTrace &quot;exception-thrown&quot; probe fires)
  patch by: mike at harschsystems dot com
 [2010-11-17 22:41 UTC] felipe@php.net
-Status: Assigned +Status: Closed
 [2010-11-17 22:41 UTC] felipe@php.net
This bug has been fixed in SVN.

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.

Thanks for the patch!
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 10:01:26 2024 UTC