Sec Bug->Bug #70290 [Csd]: Null pointer deref (segfault) in spl_autoload via ob_start
| From: | stas@php.net | Date: | Sun, 23 Aug 2015 05:42:15 +0000 |
| Subject: | Sec Bug->Bug #70290 [Csd]: Null pointer deref (segfault) in spl_autoload via ob_start | ||
| References: | 1 | Groups: | php.bugs |
| Request: | Send a blank email to php-bugs+get-195435@lists.php.net to get a copy of this message | ||
Edit report at https://bugs.php.net/bug.php?id=70290&edit=1
ID: 70290
Updated by: stas@php.net
Reported by: hugh at allthethings dot co dot nz
Summary: Null pointer deref (segfault) in spl_autoload via
ob_start
Status: Closed
-Type: Security
+Type: Bug
Package: Reproducible crash
Operating System: Linux
PHP Version: 5.6.12
Assigned To: laruence
Block user comment: N
Private report: Y
New Comment:
This doesn't look like a security issue, this is a specially crafted code which is extremely
unlikely to happen in real application. So I do not think it needs a CVE.
Previous Comments:
------------------------------------------------------------------------
[2015-08-19 10:59:43] hugh at allthethings dot co dot nz
Will there be a CVE assigned for this?
Cheers,
Hugh
------------------------------------------------------------------------
[2015-08-19 10:49:31] laruence@php.net
fixed in https://github.com/php/php-src/commit/b584b513983319be170f02828bc7c12850b40320
------------------------------------------------------------------------
[2015-08-18 10:24:31] hugh at allthethings dot co dot nz
Description:
------------
Found this using afl-fuzz, see http://lcamtuf.coredump.cx/afl/
Affects 5.6.12, 5.5.28, (couldn't compile 5.4, and EOL is basically now so meh).
To reproduce, compile PHP normally, then run ./sapi/cli/php with the test script
<?php ob_start( spl_autoload ); ?> 1
You should get a segfault.
The test case required to have SOME output displayed to screen before the ob_start is called, hence
the 1 at the end of the test script (only if a file called 9 exists in the current directory and is
a valid php file with a class of no name (I had <?php class {} ?>... (this is because ob_start
will call spl_autoload with an empty first argument (output buffer), and second argument of 9 (the
phase)), otherwise if the file doesn't exist, removing the 1 will also make it crash. I've
just included it here for completeness...).
The bug happens because if the spl_autoload doesn't load anything valid, then it tries to throw
an exception, but in that test it uses active_opline, which is (*EG(opline_ptr)), which is
(*(executor_state.opline_ptr)) or something, which is null.
Patch to fix would either be:
diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c
index 6b886b7..3424b90 100644
--- a/ext/spl/php_spl.c
+++ b/ext/spl/php_spl.c
@@ -358,7 +358,7 @@ PHP_FUNCTION(spl_autoload)
* The "scope" is determined by an opcode, if it is ZEND_FETCH_CLASS we
know function was called indirectly by
* the Zend engine.
*/
- if (active_opline->opcode != ZEND_FETCH_CLASS) {
+ if (EG(opline_ptr) && active_opline->opcode != ZEND_FETCH_CLASS) {
zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Class %s
could not be loaded", class_name);
} else {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Class %s could not be
loaded", class_name);
or some code to not let you use a internal function like that. Thoughts?
Test script:
---------------
<?php ob_start( spl_autoload ); ?> 1
Expected result:
----------------
No crash
Actual result:
--------------
(gdb) bt
#0 0x00000000004e4c02 in zif_spl_autoload (ht=2, return_value=0x7ffff7fe2940,
return_value_ptr=0x7fffffffd218, this_ptr=0x0, return_value_used=1)
at /root/php-src/ext/spl/php_spl.c:361
#1 0x0000000000638aa3 in zend_call_function (fci=0x7ffff7fe3838,
fci_cache=0x7ffff7fe3880) at /root/php-src/Zend/zend_execute_API.c:847
#2 0x000000000065936e in zend_fcall_info_call (fci=0x7ffff7fe3838,
fcc=0x7ffff7fe3880, retval_ptr_ptr=0x7fffffffd218, args=0x0)
at /root/php-src/Zend/zend_API.c:3397
#3 0x00000000005dc58d in php_output_handler_op (handler=0x7ffff7fe38c0,
context=0x7fffffffd2b0) at /root/php-src/main/output.c:977
#4 0x00000000005dcf2d in php_output_stack_pop (flags=1)
at /root/php-src/main/output.c:1244
#5 0x00000000005db0da in php_output_end_all () at /root/php-src/main/output.c:351
#6 0x00000000005c515f in php_request_shutdown (dummy=0x0)
at /root/php-src/main/main.c:1840
#7 0x000000000076ee5f in do_cli (argc=2, argv=0xb747c0)
at /root/php-src/sapi/cli/php_cli.c:1177
#8 0x000000000076f67b in main (argc=2, argv=0xb747c0)
at /root/php-src/sapi/cli/php_cli.c:1378
(gdb) i r
rax 0x0 0
rbx 0x7fffffffd218 140737488343576
rcx 0xffffffffffffff60 -160
rdx 0x6b8 1720
rsi 0x7ffff7fe2900 140737354017024
rdi 0xb74a80 12012160
rbp 0x7fffffffcec0 0x7fffffffcec0
rsp 0x7fffffffce40 0x7fffffffce40
r8 0x3 3
r9 0x7ffff74957b8 140737342166968
r10 0x7fffffffc970 140737488341360
r11 0x246 582
r12 0x418830 4294704
r13 0x7fffffffe9f0 140737488349680
r14 0x0 0
r15 0x0 0
rip 0x4e4c02 0x4e4c02 <zif_spl_autoload+514>
eflags 0x10246 [ PF ZF IF RF ]
cs 0x33 51
ss 0x2b 43
ds 0x0 0
es 0x0 0
fs 0x0 0
gs 0x0 0
(gdb) x/i $rip
=> 0x4e4c02 <zif_spl_autoload+514>: mov (%rax),%rax
With ASAN compiled
ASAN:SIGSEGV
=================================================================
==15374== ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x0000005f8f07 sp
0x7fffd148dd50 bp 0x7fffd148def0 T0)
AddressSanitizer can not provide additional info.
#0 0x5f8f06 (/root/php-src/sapi/cli/php+0x5f8f06)
#1 0x990c83 (/root/php-src/sapi/cli/php+0x990c83)
#2 0x9ee97c (/root/php-src/sapi/cli/php+0x9ee97c)
#3 0x88762b (/root/php-src/sapi/cli/php+0x88762b)
#4 0x8891c7 (/root/php-src/sapi/cli/php+0x8891c7)
#5 0x883d96 (/root/php-src/sapi/cli/php+0x883d96)
#6 0x84be0a (/root/php-src/sapi/cli/php+0x84be0a)
#7 0xd22665 (/root/php-src/sapi/cli/php+0xd22665)
#8 0xd23c73 (/root/php-src/sapi/cli/php+0xd23c73)
#9 0x7fcdef217ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
#10 0x418cf8 (/root/php-src/sapi/cli/php+0x418cf8)
==15374== ABORTING
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=70290&edit=1