php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #74301 Free() invalid pointer when wddx decodes empty boolean element
Submitted: 2017-03-23 17:17 UTC Modified: 2017-10-15 22:47 UTC
From: nguyenluan dot vnn at gmail dot com Assigned:
Status: Duplicate Package: WDDX related
PHP Version: 5.6.30 OS:
Private report: No CVE-ID: None
 [2017-03-23 17:17 UTC] nguyenluan dot vnn at gmail dot com
Description:
------------
I found a problem with wddx extension while processing boolean element with empty value.

Please refer to this code:
static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
{
        ...
	} else if (!strcmp(name, EL_BOOLEAN)) {
		int i;

		if (atts) for (i = 0; atts[i]; i++) {
			if (!strcmp(atts[i], EL_VALUE) && atts[i+1] && atts[i+1][0]) {
				ent.type = ST_BOOLEAN;
				SET_STACK_VARNAME;

				ALLOC_ZVAL(ent.data);
				INIT_PZVAL(ent.data);
				Z_TYPE_P(ent.data) = IS_BOOL;
				wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
				php_wddx_process_data(user_data, atts[i+1], strlen(atts[i+1]));
				break;
			}
		} else {    (1) code to process empty value
			ent.type = ST_BOOLEAN;
			SET_STACK_VARNAME;
			ZVAL_FALSE(&ent.data);    (2) bug here
			wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
		}
	}
        ...
}

When opening boolean element with empty value php_wddx_push_element() function try to set an empty st_entry.data and push it to the wddx_stack using ZVAL_FALSE.

In this case, I guess the ZVAL_FALSE failed to identify data type and set wrong value to st_entry ent structure, it sets 0x300000000 to st_entry ent.data.

Later, when wddx_stack_destroy(&stack) is called in php_wddx_deserialize_ex(), efree() will try to free this invalid st_entry ent.data pointer and crashes PHP.

static int wddx_stack_destroy(wddx_stack *stack)
{
	register int i;

	if (stack->elements) {
		for (i = 0; i < stack->top; i++) {
			if (((st_entry *)stack->elements[i])->data
					&& ((st_entry *)stack->elements[i])->type != ST_FIELD)	{
				zval_ptr_dtor(&((st_entry *)stack->elements[i])->data);
			}
			if (((st_entry *)stack->elements[i])->varname) {
				efree(((st_entry *)stack->elements[i])->varname);    (3) Free and crash here
			}
			efree(stack->elements[i]);
		}
		efree(stack->elements);
	}
	return SUCCESS;
}

Please refer to the gdb trace back and PHP test script for more information.

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

$xml = <<<EOF
<boolean>
EOF;
	$wddx = wddx_deserialize($xml);
	var_dump($wddx);
?>

Expected result:
----------------
No crash. Return NULL.

Actual result:
--------------
user@user:~/Desktop/php-5.6.30_pure$ gdb -q sapi/cli/php
Reading symbols from sapi/cli/php...done.
warning: File "/home/user/Desktop/php-5.6.30_pure/.gdbinit" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
	add-auto-load-safe-path /home/user/Desktop/php-5.6.30_pure/.gdbinit
line to your configuration file "/home/user/.gdbinit".
To completely disable this security protection add
	set auto-load safe-path /
line to your configuration file "/home/user/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual.  E.g., run from the shell:
	info "(gdb)Auto-loading safe path"
gdb-peda$ b wddx.c:816
Breakpoint 1 at 0x7c5a69: file /home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c, line 816.
gdb-peda$ r ../crash1.php 
Starting program: /home/user/Desktop/php-5.6.30_pure/sapi/cli/php ../crash1.php

 [----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
RCX: 0x7fffffffa6d0 --> 0x4000000000 ('')
RDX: 0x0 
RSI: 0xd36cd1 --> 0x6e61656c6f6f62 ('boolean')
RDI: 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
RBP: 0x7fffffffa3a0 --> 0x7fffffffa420 --> 0x120e9d0 --> 0x120ed90 --> 0x0 
RSP: 0x7fffffffa2e0 --> 0x0 
RIP: 0x7c5a69 (<php_wddx_push_element+2126>:	mov    rax,QWORD PTR [rbp-0x70])
R8 : 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
R9 : 0x120ffb1 ("boolean>")
R10: 0x7ffff72eeb88 --> 0x120f5b0 --> 0x0 
R11: 0x7ffff73c8820 (<xmlStrdup>:	push   rbp)
R12: 0x120ffb0 ("<boolean>")
R13: 0x120ffb8 --> 0x3e ('>')
R14: 0x1 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7c5a57 <php_wddx_push_element+2108>:	jne    0x7c5861 <php_wddx_push_element+1606>
   0x7c5a5d <php_wddx_push_element+2114>:	jmp    0x7c66bb <php_wddx_push_element+5280>
   0x7c5a62 <php_wddx_push_element+2119>:	mov    DWORD PTR [rbp-0x38],0x1
=> 0x7c5a69 <php_wddx_push_element+2126>:	mov    rax,QWORD PTR [rbp-0x70]
   0x7c5a6d <php_wddx_push_element+2130>:	mov    rax,QWORD PTR [rax+0x8]
   0x7c5a71 <php_wddx_push_element+2134>:	test   rax,rax
   0x7c5a74 <php_wddx_push_element+2137>:	je     0x7c5ad6 <php_wddx_push_element+2235>
   0x7c5a76 <php_wddx_push_element+2139>:	mov    rax,QWORD PTR [rbp-0x70]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa2e0 --> 0x0 
0008| 0x7fffffffa2e8 --> 0x0 
0016| 0x7fffffffa2f0 --> 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
0024| 0x7fffffffa2f8 --> 0x7fffffffa6d0 --> 0x4000000000 ('')
0032| 0x7fffffffa300 --> 0x0 
0040| 0x7fffffffa308 --> 0x7c00000077 ('w')
0048| 0x7fffffffa310 --> 0x7fffffffa460 --> 0xffff800000005b51 
0056| 0x7fffffffa318 --> 0x7ffff742efbe (add    rbp,QWORD PTR [rbx+0x8])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, php_wddx_push_element (user_data=0x7fffffffa6d0, name=0x120f5a0 "boolean", atts=0x0) at /home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c:816
816				SET_STACK_VARNAME;

gdb-peda$ p *&ent
$1 = {
  data = 0x0, 
  type = ST_BOOLEAN, 
  varname = 0x120ffb1 "boolean>"
}
gdb-peda$ n



 [----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
RCX: 0x7fffffffa6d0 --> 0x4000000000 ('')
RDX: 0x0 
RSI: 0xd36cd1 --> 0x6e61656c6f6f62 ('boolean')
RDI: 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
RBP: 0x7fffffffa3a0 --> 0x7fffffffa420 --> 0x120e9d0 --> 0x120ed90 --> 0x0 
RSP: 0x7fffffffa2e0 --> 0x0 
RIP: 0x7c5ade (<php_wddx_push_element+2243>:	lea    rax,[rbp-0x40])
R8 : 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
R9 : 0x120ffb1 ("boolean>")
R10: 0x7ffff72eeb88 --> 0x120f5b0 --> 0x0 
R11: 0x7ffff73c8820 (<xmlStrdup>:	push   rbp)
R12: 0x120ffb0 ("<boolean>")
R13: 0x120ffb8 --> 0x3e ('>')
R14: 0x1 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7c5acc <php_wddx_push_element+2225>:	mov    QWORD PTR [rax+0x8],0x0
   0x7c5ad4 <php_wddx_push_element+2233>:	jmp    0x7c5ade <php_wddx_push_element+2243>
   0x7c5ad6 <php_wddx_push_element+2235>:	mov    QWORD PTR [rbp-0x30],0x0
=> 0x7c5ade <php_wddx_push_element+2243>:	lea    rax,[rbp-0x40]
   0x7c5ae2 <php_wddx_push_element+2247>:	mov    QWORD PTR [rbp-0x68],rax
   0x7c5ae6 <php_wddx_push_element+2251>:	mov    rax,QWORD PTR [rbp-0x68]
   0x7c5aea <php_wddx_push_element+2255>:	mov    QWORD PTR [rax],0x0
   0x7c5af1 <php_wddx_push_element+2262>:	mov    rax,QWORD PTR [rbp-0x68]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa2e0 --> 0x0 
0008| 0x7fffffffa2e8 --> 0x0 
0016| 0x7fffffffa2f0 --> 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
0024| 0x7fffffffa2f8 --> 0x7fffffffa6d0 --> 0x4000000000 ('')
0032| 0x7fffffffa300 --> 0x0 
0040| 0x7fffffffa308 --> 0x7c00000077 ('w')
0048| 0x7fffffffa310 --> 0x7fffffffa460 --> 0xffff800000005b51 
0056| 0x7fffffffa318 --> 0x7ffff742efbe (add    rbp,QWORD PTR [rbx+0x8])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
817				ZVAL_FALSE(&ent.data);

gdb-peda$ p *&ent    (1) everything stills good here
$2 = {
  data = 0x0, 
  type = ST_BOOLEAN, 
  varname = 0x0
}
gdb-peda$ n





 [----------------------------------registers-----------------------------------]
RAX: 0x7fffffffa360 --> 0x0 
RBX: 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
RCX: 0x7fffffffa6d0 --> 0x4000000000 ('')
RDX: 0x0 
RSI: 0xd36cd1 --> 0x6e61656c6f6f62 ('boolean')
RDI: 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
RBP: 0x7fffffffa3a0 --> 0x7fffffffa420 --> 0x120e9d0 --> 0x120ed90 --> 0x0 
RSP: 0x7fffffffa2e0 --> 0x0 
RIP: 0x7c5af9 (<php_wddx_push_element+2270>:	lea    rcx,[rbp-0x40])
R8 : 0x120f187 --> 0x6e61656c6f6f62 ('boolean')
R9 : 0x120ffb1 ("boolean>")
R10: 0x7ffff72eeb88 --> 0x120f5b0 --> 0x0 
R11: 0x7ffff73c8820 (<xmlStrdup>:	push   rbp)
R12: 0x120ffb0 ("<boolean>")
R13: 0x120ffb8 --> 0x3e ('>')
R14: 0x1 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7c5aea <php_wddx_push_element+2255>:	mov    QWORD PTR [rax],0x0
   0x7c5af1 <php_wddx_push_element+2262>:	mov    rax,QWORD PTR [rbp-0x68]
   0x7c5af5 <php_wddx_push_element+2266>:	mov    BYTE PTR [rax+0x14],0x3
=> 0x7c5af9 <php_wddx_push_element+2270>:	lea    rcx,[rbp-0x40]
   0x7c5afd <php_wddx_push_element+2274>:	mov    rax,QWORD PTR [rbp-0x70]
   0x7c5b01 <php_wddx_push_element+2278>:	mov    edx,0x18
   0x7c5b06 <php_wddx_push_element+2283>:	mov    rsi,rcx
   0x7c5b09 <php_wddx_push_element+2286>:	mov    rdi,rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa2e0 --> 0x0 
0008| 0x7fffffffa2e8 --> 0x0 
0016| 0x7fffffffa2f0 --> 0x120f5a0 --> 0x6e61656c6f6f62 ('boolean')
0024| 0x7fffffffa2f8 --> 0x7fffffffa6d0 --> 0x4000000000 ('')
0032| 0x7fffffffa300 --> 0x0 
0040| 0x7fffffffa308 --> 0x7c00000077 ('w')
0048| 0x7fffffffa310 --> 0x7fffffffa460 --> 0xffff800000005b51 
0056| 0x7fffffffa318 --> 0x7ffff742efbe (add    rbp,QWORD PTR [rbx+0x8])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
818				wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));

gdb-peda$ p *&ent    (2) ZVAL_FALSE set varname to an invalid address 0x300000000 which is not in any valid memory region
$3 = {
  data = 0x0, 
  type = ST_BOOLEAN, 
  varname = 0x300000000 <error: Cannot access memory at address 0x300000000>
}
gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.


 [----------------------------------registers-----------------------------------]
RAX: 0x2ffffffb0 
RBX: 0x0 
RCX: 0xd369e8 ("/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c")
RDX: 0x1 
RSI: 0x300000000 
RDI: 0x108ef80 --> 0x1 
RBP: 0x7fffffffa5a0 --> 0x7fffffffa620 --> 0x7fffffffa650 --> 0x7fffffffa680 --> 0x7fffffffa700 --> 0x7fffffffa770 (--> ...)
RSP: 0x7fffffffa520 --> 0x0 
RIP: 0x83ccf6 (<zend_mm_check_ptr+339>:	mov    rdx,QWORD PTR [rax+0x8])
R8 : 0xee 
R9 : 0x0 
R10: 0x2a2 
R11: 0x7ffff70bf390 --> 0xfffda380fffda0af 
R12: 0x4207d0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe1a0 --> 0x2 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x83ccea <zend_mm_check_ptr+327>:	sub    rax,0x50
   0x83ccee <zend_mm_check_ptr+331>:	mov    QWORD PTR [rbp-0x30],rax
   0x83ccf2 <zend_mm_check_ptr+335>:	mov    rax,QWORD PTR [rbp-0x30]
=> 0x83ccf6 <zend_mm_check_ptr+339>:	mov    rdx,QWORD PTR [rax+0x8]
   0x83ccfa <zend_mm_check_ptr+343>:	mov    rax,QWORD PTR [rbp-0x30]
   0x83ccfe <zend_mm_check_ptr+347>:	mov    rax,QWORD PTR [rax+0x8]
   0x83cd02 <zend_mm_check_ptr+351>:	and    rax,0xfffffffffffffffc
   0x83cd06 <zend_mm_check_ptr+355>:	mov    rcx,rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa520 --> 0x0 
0008| 0x7fffffffa528 --> 0x0 
0016| 0x7fffffffa530 --> 0xd369e8 ("/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c")
0024| 0x7fffffffa538 --> 0x1000000ee 
0032| 0x7fffffffa540 --> 0x300000000 
0040| 0x7fffffffa548 --> 0x108ef80 --> 0x1 
0048| 0x7fffffffa550 --> 0xf7fd3f68 
0056| 0x7fffffffa558 --> 0x100000000 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x000000000083ccf6 in zend_mm_check_ptr (heap=0x108ef80, ptr=0x300000000, silent=0x1, __zend_filename=0xd369e8 "/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c", 
    __zend_lineno=0xee, __zend_orig_filename=0x0, __zend_orig_lineno=0x0) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_alloc.c:1384
1384		if (p->info._size != ZEND_MM_NEXT_BLOCK(p)->info._prev) {
gdb-peda$ bt
#0  0x000000000083ccf6 in zend_mm_check_ptr (heap=0x108ef80, ptr=0x300000000, silent=0x1, __zend_filename=0xd369e8 "/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c", 
    __zend_lineno=0xee, __zend_orig_filename=0x0, __zend_orig_lineno=0x0) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_alloc.c:1384
#1  0x000000000083e95d in _zend_mm_free_int (heap=0x108ef80, p=0x300000000, __zend_filename=0xd369e8 "/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c", __zend_lineno=0xee, 
    __zend_orig_filename=0x0, __zend_orig_lineno=0x0) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_alloc.c:2068
#2  0x000000000083ff6e in _efree (ptr=0x300000000, __zend_filename=0xd369e8 "/home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c", __zend_lineno=0xee, __zend_orig_filename=0x0, 
    __zend_orig_lineno=0x0) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_alloc.c:2440
#3  0x00000000007bfe21 in wddx_stack_destroy (stack=0x7fffffffa6d0) at /home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c:238
#4  0x00000000007c7960 in php_wddx_deserialize_ex (value=0x7ffff7ebc0a8 "<boolean>", vallen=0x9, return_value=0x7ffff7fd13c8)
    at /home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c:1206
#5  0x00000000007c8cc9 in zif_wddx_deserialize (ht=0x1, return_value=0x7ffff7fd13c8, return_value_ptr=0x7ffff7f9a220, this_ptr=0x0, return_value_used=0x1)
    at /home/user/Desktop/php-5.6.30_pure/ext/wddx/wddx.c:1405
#6  0x00000000008c38a7 in zend_do_fcall_common_helper_SPEC (execute_data=0x7ffff7f9a258) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_vm_execute.h:558
#7  0x00000000008c93d2 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x7ffff7f9a258) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_vm_execute.h:2602
#8  0x00000000008c2f0f in execute_ex (execute_data=0x7ffff7f9a258) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_vm_execute.h:363
#9  0x00000000008c2f96 in zend_execute (op_array=0x7ffff7fd22f8) at /home/user/Desktop/php-5.6.30_pure/Zend/zend_vm_execute.h:388
#10 0x000000000087b946 in zend_execute_scripts (type=0x8, retval=0x0, file_count=0x3) at /home/user/Desktop/php-5.6.30_pure/Zend/zend.c:1341
#11 0x00000000007dc96b in php_execute_script (primary_file=0x7fffffffcd70) at /home/user/Desktop/php-5.6.30_pure/main/main.c:2613
#12 0x00000000009386b6 in do_cli (argc=0x2, argv=0x108ecc0) at /home/user/Desktop/php-5.6.30_pure/sapi/cli/php_cli.c:998
#13 0x0000000000939a19 in main (argc=0x2, argv=0x108ecc0) at /home/user/Desktop/php-5.6.30_pure/sapi/cli/php_cli.c:1382
#14 0x00007ffff6f4b830 in __libc_start_main (main=0x9391fc <main>, argc=0x2, argv=0x7fffffffe1a8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, 
    stack_end=0x7fffffffe198) at ../csu/libc-start.c:291
#15 0x00000000004207f9 in _start ()


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-06-25 18:29 UTC] nikic@php.net
This affects PHP 5.6 only. The patch is probably https://gist.github.com/nikic/d629cba0738de96135428bced1a9476f, but I don't have a working build to test.
 [2017-06-27 16:59 UTC] nguyenluan dot vnn at gmail dot com
Hi Nikic,

I tested the patch you provided. It fixed this bug.
 [2017-07-02 21:28 UTC] stas@php.net
-Status: Open +Status: Duplicate
 [2017-07-02 21:28 UTC] stas@php.net
Same bug as bug #74145

The fix is in security repo as 36ac7722d93ee69d69e986b3102922fd529a3dfd and in https://gist.github.com/8e3f974e5a8913a66ae1a6f966ba351f
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 09:01:32 2024 UTC