php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73645 version_compare illegal write access
Submitted: 2016-12-03 17:08 UTC Modified: 2016-12-06 06:17 UTC
From: fernando at null-life dot com Assigned:
Status: Closed Package: *General Issues
PHP Version: 7.0.13 OS: Linux
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-12-03 17:08 UTC] fernando at null-life dot com
Description:
------------
When version1 or version2 parameter length need more than 32 bits to be stored, php_canonicalize_version function calls to safe_emalloc with size parameter casted to 32 bits. Then, write of bounds happens when version parameter is copied to buffer.


--------------------------------------------------------------------------------------
Source code:
https://github.com/php/php-src/blob/PHP-7.0.14/ext/standard/versioning.c#L33

PHPAPI char *
php_canonicalize_version(const char *version)
{
    int len = strlen(version);
    char *buf = safe_emalloc(len, 2, 1), *q, lp, lq;
    const char *p;

    if (len == 0) {
        *buf = '\0';
        return buf;
    }

    p = version;
    q = buf;
    *q++ = lp = *p++;

    while (*p) {
/*  s/[-_+]/./g;
 *  s/([^\d\.])([^\D\.])/$1.$2/g;
 *  s/([^\D\.])([^\d\.])/$1.$2/g;
 */
#define isdig(x) (isdigit(x)&&(x)!='.')
#define isndig(x) (!isdigit(x)&&(x)!='.')
#define isspecialver(x) ((x)=='-'||(x)=='_'||(x)=='+')

		lq = *(q - 1);
		if (isspecialver(*p)) {
			if (lq != '.') {
				*q++ = '.';
			}
		} else if ((isndig(lp) && isdig(*p)) || (isdig(lp) && isndig(*p))) {
			if (lq != '.') {
				*q++ = '.';
			}
			*q++ = *p;
		} else if (!isalnum(*p)) {
			if (lq != '.') {
				*q++ = '.';
			}
		} else {
			*q++ = *p;   // write out of bounds
		}
		lp = *p++;
    }
    *q++ = '\0';
    return buf;
}



GDB output:

gdb -q --args /home/operac/build5/bin/php -n poc.php
Reading symbols from /home/operac/build5/bin/php...done.
(gdb) b php_canonicalize_version
Breakpoint 1 at 0xc39cb6: file /home/operac/build5/php-src/ext/standard/versioning.c, line 36.
(gdb) r
Starting program: /home/operac/build5/bin/php -n poc.php
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, php_canonicalize_version (version=0x7ffeeec00018 'x' <repeats 200 times>...) at /home/operac/build5/php-src/ext/standard/versioning.c:36
36          int len = strlen(version);
(gdb) b _safe_emalloc
Breakpoint 2 at 0xdc7637: file /home/operac/build5/php-src/Zend/zend_alloc.c, line 2516.
(gdb) b 37
Breakpoint 3 at 0xc39cc5: file /home/operac/build5/php-src/ext/standard/versioning.c, line 37.
(gdb) c
Continuing.

Breakpoint 3, php_canonicalize_version (version=0x7ffeeec00018 'x' <repeats 200 times>...) at /home/operac/build5/php-src/ext/standard/versioning.c:37
37          char *buf = safe_emalloc(len, 2, 1), *q, lp, lq;
(gdb) p len
$1 = 1                 // 0x100000001 casted to 1
(gdb) c
Continuing.

Breakpoint 2, _safe_emalloc (nmemb=1, size=2, offset=1, __zend_filename=0x16f1c60 "/home/operac/build5/php-src/ext/standard/versioning.c", __zend_lineno=37, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at /home/operac/build5/php-src/Zend/zend_alloc.c:2516
2516            return emalloc_rel(zend_safe_address_guarded(nmemb, size, offset));
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x0000000000c3a3bf in php_canonicalize_version (version=0x7ffeeec00018 'x' <repeats 200 times>...) at /home/operac/build5/php-src/ext/standard/versioning.c:73
73                              *q++ = *p;           // write out of bounds




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

ini_set('memory_limit', -1);

$v1 = str_repeat('x', 0x100000001);
version_compare($v1, PHP_VERSION);

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

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


ASAN:SIGSEGV
=================================================================
==15966==ERROR: AddressSanitizer: SEGV on unknown address 0x7f88d1200000 (pc 0x000000c3a3bf bp 0x7ffec149db70 sp 0x7ffec149db40 T0)
    #0 0xc3a3be in php_canonicalize_version /home/operac/build5/php-src/ext/standard/versioning.c:73
    #1 0xc3aa01 in php_version_compare /home/operac/build5/php-src/ext/standard/versioning.c:145
    #2 0xc3b44e in zif_version_compare /home/operac/build5/php-src/ext/standard/versioning.c:222
    #3 0xf277dc in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/build5/php-src/Zend/zend_vm_execute.h:586
    #4 0xf26872 in execute_ex /home/operac/build5/php-src/Zend/zend_vm_execute.h:414
    #5 0xf26aed in zend_execute /home/operac/build5/php-src/Zend/zend_vm_execute.h:458
    #6 0xe49c66 in zend_execute_scripts /home/operac/build5/php-src/Zend/zend.c:1427
    #7 0xd0b9d8 in php_execute_script /home/operac/build5/php-src/main/main.c:2494
    #8 0x1058a1c in do_cli /home/operac/build5/php-src/sapi/cli/php_cli.c:974
    #9 0x105a82c in main /home/operac/build5/php-src/sapi/cli/php_cli.c:1344
    #10 0x7f88d6b4b82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #11 0x430a88 in _start (/home/operac/build5/bin/php+0x430a88)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/operac/build5/php-src/ext/standard/versioning.c:73 php_canonicalize_version
==15966==ABORTING


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-12-06 06:17 UTC] stas@php.net
-Type: Security +Type: Bug
 [2016-12-06 06:17 UTC] stas@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=1d59ed75246c7d05688accc349fda70cbee90428
Log: Fix bug #73645 - int/size_t confusion
 [2016-12-06 06:17 UTC] stas@php.net
-Status: Open +Status: Closed
 [2016-12-06 16:12 UTC] ab@php.net
Automatic comment on behalf of stas
Revision: http://git.php.net/?p=php-src.git;a=commit;h=a92718868d6d491b40d994ec8703aa3ebe6f9383
Log: Fix bug #73645 - int/size_t confusion
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC