Null pointer deref (segfault) in spl_autoload via ob_start
| Bug #70290 | Null pointer deref (segfault) in spl_autoload via ob_start | ||||
|---|---|---|---|---|---|
| Submitted: | 2015-08-18 10:24 UTC | Modified: | 2015-08-23 08:44 UTC | ||
| From: | hugh at allthethings dot co dot nz | Assigned: | laruence (profile) | ||
| Status: | Closed | Package: | Reproducible crash | ||
| PHP Version: | 5.6.12 | OS: | Linux | ||
| Private report: | No | CVE-ID: | None | ||
[2015-08-18 10:24 UTC] 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
Patches
Pull Requests
History
AllCommentsChangesGit/SVN commits
[2015-08-19 10:49 UTC] laruence@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: laruence
[2015-08-19 10:59 UTC] hugh at allthethings dot co dot nz
[2015-08-23 05:42 UTC] stas@php.net
-Type: Security +Type: Bug
[2015-08-23 05:42 UTC] stas@php.net
[2015-08-23 08:44 UTC] hugh at allthethings dot co dot nz
[2015-12-26 05:47 UTC] hugh at allthethings dot co dot nz
[2016-01-11 20:53 UTC] hugh at allthethings dot co dot nz