php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #72595 php_output_handler_append illegal write access
Submitted: 2016-07-14 03:08 UTC Modified: 2021-07-14 15:56 UTC
From: fernando at null-life dot com Assigned: cmb (profile)
Status: Closed Package: Output Control
PHP Version: 7.0.8 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: fernando at null-life dot com
New email:
PHP Version: OS:

 

 [2016-07-14 03:08 UTC] fernando at null-life dot com
Description:
------------
Integer overflow at php_output_handler_append function causes illegal write access

Calling to ob_start function with a big chunk_size value (i.e. PHP_INT_MAX), an integer overflow happens while trying to realloc the memory buffer. This causes a call to realloc with a new size of 0, the behaviour will depend on the libc implementation, but glibc returns a valid pointer here, and later an illegal write access trough the memcpy function. This was tested on a 32 bits system.

------------------------------------

Source code https://github.com/php/php-src/blob/master/main/output.c#L880

static inline int php_output_handler_append(php_output_handler *handler, const php_output_buffer *buf)
{
	if (buf->used) {
		OG(flags) |= PHP_OUTPUT_WRITTEN;
		/* store it away */
		if ((handler->buffer.size - handler->buffer.used) <= buf->used) {
			size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size);
			size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used));
			size_t grow_max = MAX(grow_int, grow_buf);

			handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);
                        ## integer overflow : handler->buffer.size + grow_max  = 0 ^
			handler->buffer.size += grow_max;
		}
		memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
                ## illegal write ^
		handler->buffer.used += buf->used;

		/* chunked buffering */
		if (handler->size && (handler->buffer.used >= handler->size)) {
			/* store away errors and/or any intermediate output */
			return OG(running) ? 1 : 0;
		}
	}
	return 1;
}


GDB output:


gdb -q --args /matatetete/matatetete/php-70/sapi/cli/php -n poc2.php

gdb-peda$ b output.c:890
Breakpoint 2 at 0x938ec71: output.c:890. (4 locations)
gdb-peda$ b output.c:891
Breakpoint 3 at 0x938ed53: output.c:891. (4 locations)
gdb-peda$ r
...
Breakpoint 2, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:890
890                             handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);
gdb-peda$ p *handler
$1 = {
  name = 0xf32013b8,
  flags = 0x71,
  level = 0x0,
  size = 0x7fffffff,
  buffer = {
    data = 0x70e00000 "",                      ## Before realloc
    size = 0x80000000,
    used = 0x7fffe000,
    free = 0x0,
    _reserved = 0x0
  },
  opaq = 0x0,
  dtor = 0x0,
  func = {
    user = 0xf327a0e0,
    internal = 0xf327a0e0
  }
}
gdb-peda$ c
...
Breakpoint 3, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:891
891                             handler->buffer.size += grow_max;
gdb-peda$ p *handler
$2 = {
  name = 0xf32013b8,
  flags = 0x71,
  level = 0x0,
  size = 0x7fffffff,
  buffer = {
    data = 0xf32622a8 "\320\"", <incomplete sequence \363>,         ## After realloc(0)
    size = 0x80000000,
    used = 0x7fffe000,
    free = 0x0,
    _reserved = 0x0
  },
  opaq = 0x0,
  dtor = 0x0,
  func = {
    user = 0xf327a0e0,
    internal = 0xf327a0e0
  }
}
gdb-peda$ l
886                             size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(handler->size);
887                             size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(buf->used - (handler->buffer.size - handler->buffer.used));
888                             size_t grow_max = MAX(grow_int, grow_buf);
889
890                             handler->buffer.data = erealloc(handler->buffer.data, handler->buffer.size + grow_max);      ## erealloc(0)
891                             handler->buffer.size += grow_max;
892                     }
893                     memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
894                     handler->buffer.used += buf->used;
895
gdb-peda$ p handler->buffer.size + grow_max        ## Integer overflow
$1 = 0x0
gdb-peda$ p handler->buffer.size
$2 = 0x80000000
gdb-peda$ p grow_max
$3 = 0x80000000
gdb-peda$ b output.c:893
Breakpoint 4 at 0x938e00c: output.c:893. (4 locations)
gdb-peda$ c
...
Breakpoint 4, php_output_handler_append (buf=0xffff7814, buf=0xffff7814, handler=0xf3202300) at /matatetete/php-src/main/output.c:893
893                     memcpy(handler->buffer.data + handler->buffer.used, buf->data, buf->used);
gdb-peda$ p handler->buffer.data + handler->buffer.used
$3 = 0x732602a8 <error: Cannot access memory at address 0x732602a8>      ## Illegal dst address
gdb-peda$



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

ini_set('memory_limit', -1);
function callback_function($buffer) { return 1; }
$fp = fopen("/dev/zero", "r");
ob_start("callback_function", PHP_INT_MAX);
fpassthru($fp);

Expected result:
----------------
No crash / OOM error?

Actual result:
--------------
ASan output:

/matatetete/matatetete/php-70/sapi/cli/php -n poc.php
ASAN:SIGSEGV
=================================================================
==14329==ERROR: AddressSanitizer: SEGV on unknown address 0x72863280 (pc 0xf6d60e0c bp 0xfff72108 sp 0xfff71c98 T0)
    #0 0xf6d60e0b  (/lib/i386-linux-gnu/libc.so.6+0x126e0b)
    #1 0xf72456dd in __asan_memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8a6dd)
    #2 0xf7245c2f in memcpy (/usr/lib/i386-linux-gnu/libasan.so.2+0x8ac2f)
    #3 0x938e0e3 in memcpy /usr/include/i386-linux-gnu/bits/string3.h:53
    #4 0x938e0e3 in php_output_handler_append /matatetete/php-src/main/output.c:893
    #5 0x938e0e3 in php_output_handler_op /matatetete/php-src/main/output.c:941
    #6 0x938e0e3 in php_output_op /matatetete/php-src/main/output.c:1057
    #7 0x938e0e3 in php_output_write /matatetete/php-src/main/output.c:257
    #8 0x93b5abb in _php_stream_passthru /matatetete/php-src/main/streams/streams.c:1411
    #9 0x901ebc5 in zif_fpassthru /matatetete/php-src/ext/standard/file.c:1464
    #10 0x9ac461a in ZEND_DO_ICALL_SPEC_HANDLER /matatetete/php-src/Zend/zend_vm_execute.h:586
    #11 0x980eaaf in execute_ex /matatetete/php-src/Zend/zend_vm_execute.h:414
    #12 0x9b2a835 in zend_execute /matatetete/php-src/Zend/zend_vm_execute.h:458
    #13 0x95e778c in zend_execute_scripts /matatetete/php-src/Zend/zend.c:1427
    #14 0x932ea6b in php_execute_script /matatetete/php-src/main/main.c:2494
    #15 0x9b33374 in do_cli /matatetete/php-src/sapi/cli/php_cli.c:974
    #16 0x80a64d3 in main /matatetete/php-src/sapi/cli/php_cli.c:1344
    #17 0xf6c52636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #18 0x80a6aea  (/matatetete/matatetete/php-70/sapi/cli/php+0x80a6aea)


Patches

Pull Requests

Pull requests:

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-07-17 20:32 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-07-17 20:32 UTC] stas@php.net
Doesn't look like security issue - requires specially crafted code to trigger.
 [2021-07-14 15:56 UTC] cmb@php.net
-Package: *General Issues +Package: Output Control -Assigned To: +Assigned To: cmb
 [2021-07-14 15:56 UTC] cmb@php.net
The following pull request has been associated:

Patch Name: Fix #72595: php_output_handler_append illegal write access
On GitHub:  https://github.com/php/php-src/pull/7241
Patch:      https://github.com/php/php-src/pull/7241.patch
 [2021-07-15 13:32 UTC] git@php.net
Automatic comment on behalf of cmb69
Revision: https://github.com/php/php-src/commit/a942cf5b028728cf9e8d1fb4bd72b6b94f6843a3
Log: Fix #72595: php_output_handler_append illegal write access
 [2021-07-15 13:32 UTC] git@php.net
-Status: Assigned +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 11:01:29 2024 UTC