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
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: nguyenluan dot vnn at gmail dot com
New email:
PHP Version: OS:

 

 [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: Tue Dec 03 17:01:29 2024 UTC