php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73150 missing NULL check in dom_document_save_html
Submitted: 2016-09-23 15:05 UTC Modified: 2017-02-13 02:07 UTC
From: nguyenluan dot vnn at gmail dot com Assigned: stas
Status: Closed Package: Unknown/Other Function
PHP Version: 5.6.26 OS:
Private report: No CVE-ID:
View Add Comment Developer Edit
Anyone can comment on a bug. Have a simpler test case? Does it work for you on a different platform? Let us know!
Just going to say 'Me too!'? Don't clutter the database with that please !
Your email address:
MUST BE VALID
Solve the problem:
15 - 3 = ?
Subscribe to this entry?

 
 [2016-09-23 15:05 UTC] nguyenluan dot vnn at gmail dot com
Description:
------------
As you can see in the code below in dom_document_save_html function:
        } else {
#if LIBXML_VERSION >= 20623
		htmlDocDumpMemoryFormat(docp, &mem, &size, format);
#else
		htmlDocDumpMemory(docp, &mem, &size);
#endif
		if (!size) {
			RETVAL_FALSE;
		} else {
			RETVAL_STRINGL((const char*) mem, size, 1);  // missing NULL check for *mem when size != 0
		}
		if (mem)
			xmlFree(mem);
	}

If size != 0, a string will be retured. I tested with a HTML file > 2Gb and break at RETVAL_STRINGL((const char*) mem, size, 1);.

The *mem is NULL and size != 0. This lead to a memory access violation when RETVAL_STRINGL call memcpy.

If *mem is not NULL, a string with size > 2GB is returned. This is also considered as security issue in PHP.

Test script:
---------------
Python script to create HTML file:
#! /usr/bin/env python3

f = open('html.html', 'w');
f.write('<html>\n')

for _ in range(0x100):
    f.write('\t<p>')
    for _ in range(0x100):
        f.write('a'*(0x8000))
    f.write('</p>\n')

f.write('</html>')
f.close()

PHP script:
<?php
    ini_set('memory_limit', -1);
    
    $doc = new DOMDocument();
    $doc->loadHTMLFile('/home/user/Desktop/test/string/html.html');
    
    $str = $doc->saveHTML();
    
    var_dump(strlen($str));
?>


Expected result:
----------------
No crash, no string return since HTML string input is larger than 2Gb.

Actual result:
--------------
gdb-peda$ r ../test/string/test_domhtml.php 
Starting program: /home/user/Desktop/php-5.6.26/sapi/cli/php ../test/string/test_domhtml.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
[----------------------------------registers-----------------------------------]
RAX: 0x80000985 
RBX: 0xa66943 (<execute_ex>:	push   rbp)
RCX: 0x0 
RDX: 0x7ffff4f1d778 --> 0x163a8b0 --> 0x20 (' ')
RSI: 0xffffffff 
RDI: 0x0 
RBP: 0x7fffffffa610 --> 0x7fffffffa680 --> 0x7fffffffa6a0 --> 0x7fffffffa6d0 --> 0x7fffffffa700 --> 0x7fffffffa860 --> 0x7fffffffcad0 --> 0x7fffffffde10 --> 0x7fffffffdf70 --> 0x0 
RSP: 0x7fffffffa530 --> 0x14a2650 --> 0x5d00000001 
RIP: 0x60c425 (<zif_dom_document_save_html+1061>:	mov    rax,QWORD PTR [rbp-0x80])
R8 : 0x7fffff00 
R9 : 0x163a8c0 --> 0x0 
R10: 0x7ffff4f1d7b8 --> 0x1656fc0 ("aaaaaaaaAPy\001")
R11: 0x7ffff4f1d7b0 --> 0x0 
R12: 0x4397d0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe050 --> 0x2 
R14: 0x0 
R15: 0x0
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x60c41b <zif_dom_document_save_html+1051>:	mov    rax,QWORD PTR [rbp-0x18]
   0x60c41f <zif_dom_document_save_html+1055>:	mov    BYTE PTR [rax+0x14],0x3
   0x60c423 <zif_dom_document_save_html+1059>:	
    jmp    0x60c48d <zif_dom_document_save_html+1165>
=> 0x60c425 <zif_dom_document_save_html+1061>:	mov    rax,QWORD PTR [rbp-0x80]
   0x60c429 <zif_dom_document_save_html+1065>:	mov    QWORD PTR [rbp-0x10],rax
   0x60c42d <zif_dom_document_save_html+1069>:	mov    eax,DWORD PTR [rbp-0xa4]
   0x60c433 <zif_dom_document_save_html+1075>:	mov    DWORD PTR [rbp-0x94],eax
   0x60c439 <zif_dom_document_save_html+1081>:	mov    rax,QWORD PTR [rbp-0xc0]
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa530 --> 0x14a2650 --> 0x5d00000001 
0008| 0x7fffffffa538 --> 0x103e510 ("/home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h")
0016| 0x7fffffffa540 --> 0x7ffff7faff50 --> 0x5a5a5a5a00000001 
0024| 0x7fffffffa548 --> 0x7ffff7f77250 --> 0x7ffff7fafed8 ('Z' <repeats 16 times>, "\001")
0032| 0x7fffffffa550 --> 0x7ffff7fafed8 ('Z' <repeats 16 times>, "\001")
0040| 0x7fffffffa558 --> 0x1 
0048| 0x7fffffffa560 --> 0x7ffff7fafe88 --> 0x7fff6def5880 ('a' <repeats 200 times>...)
0056| 0x7fffffffa568 --> 0x8000098500000078 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, zif_dom_document_save_html (ht=0x0, return_value=0x7ffff7fafed8, 
    return_value_ptr=0x7ffff7f77250, this_ptr=0x7ffff7faff50, 
    return_value_used=0x1)
    at /home/user/Desktop/php-5.6.26/ext/dom/document.c:2333
2333				RETVAL_STRINGL((const char*) mem, size, 1);


gdb-peda$ p size
$1 = 0x80000985     <- size is larger than 2Gb


gdb-peda$ b zend_alloc.c:2664
Breakpoint 2 at 0x9e7f82: file /home/user/Desktop/php-5.6.26/Zend/zend_alloc.c, line 2664.
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
RAX: 0x0 
RBX: 0xa66943 (<execute_ex>:	push   rbp)
RCX: 0x7ffff4c5387a (<mmap64+10>:	cmp    rax,0xfffffffffffff001)
RDX: 0x8003eb38 
RSI: 0x7fff3261aa00 --> 0x7fffa8740c08 ('a' <repeats 200 times>...)
RDI: 0x13c0670 --> 0x1 
RBP: 0x7fffffffa520 --> 0x7fffffffa610 --> 0x7fffffffa680 --> 0x7fffffffa6a0 --> 0x7fffffffa6d0 --> 0x7fffffffa700 --> 0x7fffffffa860 --> 0x7fffffffcad0 --> 0x7fffffffde10 --> 0x7fffffffdf70 --> 0x0 
RSP: 0x7fffffffa4e0 --> 0x0 
RIP: 0x9e7f82 (<_estrndup+156>:	mov    edx,DWORD PTR [rbp-0x1c])
R8 : 0xffffffff 
R9 : 0x0 
R10: 0x22 ('"')
R11: 0xf4f1d701 
R12: 0x4397d0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe050 --> 0x2 
R14: 0x0 
R15: 0x0
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x9e7f7a <_estrndup+148>:	call   rax
   0x9e7f7c <_estrndup+150>:	mov    rax,QWORD PTR [rbp-0x8]
   0x9e7f80 <_estrndup+154>:	jmp    0x9e7fc4 <_estrndup+222>
=> 0x9e7f82 <_estrndup+156>:	mov    edx,DWORD PTR [rbp-0x1c]
   0x9e7f85 <_estrndup+159>:	mov    rcx,QWORD PTR [rbp-0x18]
   0x9e7f89 <_estrndup+163>:	mov    rax,QWORD PTR [rbp-0x8]
   0x9e7f8d <_estrndup+167>:	mov    rsi,rcx
   0x9e7f90 <_estrndup+170>:	mov    rdi,rax
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa4e0 --> 0x0 
0008| 0x7fffffffa4e8 --> 0x0 
0016| 0x7fffffffa4f0 --> 0x0 
0024| 0x7fffffffa4f8 --> 0xbdf3a8 ("/home/user/Desktop/php-5.6.26/ext/dom/document.c")
0032| 0x7fffffffa500 --> 0x800009850000091d 
0040| 0x7fffffffa508 --> 0x0 
0048| 0x7fffffffa510 --> 0xa66943 (<execute_ex>:	push   rbp)
0056| 0x7fffffffa518 --> 0x7ffeb261a070 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, _estrndup (s=0x0, length=0x80000985, 
    __zend_filename=0xbdf3a8 "/home/user/Desktop/php-5.6.26/ext/dom/document.c", __zend_lineno=0x91d, __zend_orig_filename=0x0, __zend_orig_lineno=0x0)
    at /home/user/Desktop/php-5.6.26/Zend/zend_alloc.c:2664
2664		memcpy(p, s, length);


gdb-peda$ p length
$2 = 0x80000985 <- alloc length is larger than 2Gb


gdb-peda$ p p
$3 = 0x7ffeb261a070 ""


gdb-peda$ p s
$4 = 0x0        <- source string is NULL


gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
RAX: 0xffff8000cd9e560b 
RBX: 0xa66943 (<execute_ex>:	push   rbp)
RCX: 0x10000130a 
RDX: 0x80000985 
RSI: 0x0 
RDI: 0x7ffeb261a070 --> 0x0 
RBP: 0x7fffffffa520 --> 0x7fffffffa610 --> 0x7fffffffa680 --> 0x7fffffffa6a0 --> 0x7fffffffa6d0 --> 0x7fffffffa700 --> 0x7fffffffa860 --> 0x7fffffffcad0 --> 0x7fffffffde10 --> 0x7fffffffdf70 --> 0x0 
RSP: 0x7fffffffa4d8 --> 0x9e7f98 (<_estrndup+178>:	mov    edx,DWORD PTR [rbp-0x1c])
RIP: 0x7ffff4bf6ee0 (<__memcpy_sse2_unaligned+32>:	movdqu xmm8,XMMWORD PTR [rsi])
R8 : 0xffffffff 
R9 : 0x0 
R10: 0x22 ('"')
R11: 0xf4f1d701 
R12: 0x4397d0 (<_start>:	xor    ebp,ebp)
R13: 0x7fffffffe050 --> 0x2 
R14: 0x0 
R15: 0x0
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x7ffff4bf6ed0 <__memcpy_sse2_unaligned+16>:	
    jb     0x7ffff4bf6fdd <__memcpy_sse2_unaligned+285>
   0x7ffff4bf6ed6 <__memcpy_sse2_unaligned+22>:	cmp    rdx,0x10
   0x7ffff4bf6eda <__memcpy_sse2_unaligned+26>:	
    jbe    0x7ffff4bf706b <__memcpy_sse2_unaligned+427>
=> 0x7ffff4bf6ee0 <__memcpy_sse2_unaligned+32>:	movdqu xmm8,XMMWORD PTR [rsi]
   0x7ffff4bf6ee5 <__memcpy_sse2_unaligned+37>:	cmp    rdx,0x20
   0x7ffff4bf6ee9 <__memcpy_sse2_unaligned+41>:	movdqu XMMWORD PTR [rdi],xmm8
   0x7ffff4bf6eee <__memcpy_sse2_unaligned+46>:	
    movdqu xmm8,XMMWORD PTR [rsi+rdx*1-0x10]
   0x7ffff4bf6ef5 <__memcpy_sse2_unaligned+53>:	
    movdqu XMMWORD PTR [rdi+rdx*1-0x10],xmm8
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffa4d8 --> 0x9e7f98 (<_estrndup+178>:	mov    edx,DWORD PTR [rbp-0x1c])
0008| 0x7fffffffa4e0 --> 0x0 
0016| 0x7fffffffa4e8 --> 0x0 
0024| 0x7fffffffa4f0 --> 0x0 
0032| 0x7fffffffa4f8 --> 0xbdf3a8 ("/home/user/Desktop/php-5.6.26/ext/dom/document.c")
0040| 0x7fffffffa500 --> 0x800009850000091d 
0048| 0x7fffffffa508 --> 0x0 
0056| 0x7fffffffa510 --> 0xa66943 (<execute_ex>:	push   rbp)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
__memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
33	../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S: No such file or directory.
gdb-peda$ bt
#0  __memcpy_sse2_unaligned ()
    at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:33
#1  0x00000000009e7f98 in _estrndup (s=0x0, length=0x80000985, 
    __zend_filename=0xbdf3a8 "/home/user/Desktop/php-5.6.26/ext/dom/document.c", __zend_lineno=0x91d, __zend_orig_filename=0x0, __zend_orig_lineno=0x0)
    at /home/user/Desktop/php-5.6.26/Zend/zend_alloc.c:2664
#2  0x000000000060c47b in zif_dom_document_save_html (ht=0x0, 
    return_value=0x7ffff7fafed8, return_value_ptr=0x7ffff7f77250, 
    this_ptr=0x7ffff7faff50, return_value_used=0x1)
    at /home/user/Desktop/php-5.6.26/ext/dom/document.c:2333
#3  0x0000000000a67334 in zend_do_fcall_common_helper_SPEC (
    execute_data=0x7ffff7f77368)
    at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:558
#4  0x0000000000a67b05 in ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER (
    execute_data=0x7ffff7f77368)
    at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:693
#5  0x0000000000a669a3 in execute_ex (execute_data=0x7ffff7f77368)
    at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:363
#6  0x0000000000a66a2c in zend_execute (op_array=0x7ffff7fb0da0)
    at /home/user/Desktop/php-5.6.26/Zend/zend_vm_execute.h:388
#7  0x0000000000a21a50 in zend_execute_scripts (type=0x8, retval=0x0, 
    file_count=0x3) at /home/user/Desktop/php-5.6.26/Zend/zend.c:1341
#8  0x0000000000986fbe in php_execute_script (primary_file=0x7fffffffcc10)
    at /home/user/Desktop/php-5.6.26/main/main.c:2613
#9  0x0000000000ad6225 in do_cli (argc=0x2, argv=0x13c03a0)
    at /home/user/Desktop/php-5.6.26/sapi/cli/php_cli.c:994
#10 0x0000000000ad757e in main (argc=0x2, argv=0x13c03a0)
    at /home/user/Desktop/php-5.6.26/sapi/cli/php_cli.c:1378
#11 0x00007ffff4b80f45 in __libc_start_main (main=0xad6d66 <main>, argc=0x2, 
    argv=0x7fffffffe058, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffe048) at libc-start.c:287
#12 0x00000000004397f9 in _start ()


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-09-26 04:26 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-09-26 04:26 UTC] stas@php.net
The fix is in security repo as 1c0e9126fbfb7fde3173347b7464237f56c38bfa and in https://gist.github.com/2aeeeff7776f2edb701c7a350e66d181

please verify
 [2016-09-26 06:28 UTC] nguyenluan dot vnn at gmail dot com
The check for "mem" is good, but could you add a check for size >= INTMAX or use RETVAL_STRINGL_CHECK to prevent this function returns string larger than 2Gb because no string in PHP 5 could larger than this value?
 [2016-10-11 23:45 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=1c0e9126fbfb7fde3173347b7464237f56c38bfa
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-11 23:45 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-10-12 14:26 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d1e878f2726e65502fdd992c5b57feeada57893f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-12 23:26 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=512d3c679d6300765e2c293feb614bae2195d216
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-12 23:35 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=1c0e9126fbfb7fde3173347b7464237f56c38bfa
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-14 01:02 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=336322ce63f4d6bd2abd2b187c9de387f387593f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-14 02:23 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d1e878f2726e65502fdd992c5b57feeada57893f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-14 02:23 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=1c0e9126fbfb7fde3173347b7464237f56c38bfa
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-14 02:23 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=336322ce63f4d6bd2abd2b187c9de387f387593f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-17 10:06 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=336322ce63f4d6bd2abd2b187c9de387f387593f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-17 10:07 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=d1e878f2726e65502fdd992c5b57feeada57893f
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2016-10-17 10:07 UTC] bwoebi@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=1c0e9126fbfb7fde3173347b7464237f56c38bfa
Log: Fix bug #73150: missing NULL check in dom_document_save_html
 [2017-02-13 02:07 UTC] stas@php.net
-Type: Security +Type: Bug
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Tue Aug 29 15:01:52 2017 UTC