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 (profile)
Status: Closed Package: Unknown/Other Function
PHP Version: 5.6.26 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:

 

 [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

Pull Requests

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-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 11:01:29 2024 UTC