Bug 997208 (CVE-2016-7126)

Summary: VUL-0: CVE-2016-7126: php7: select_colors write out-of-bounds
Product: [Novell Products] SUSE Security Incidents Reporter: Victor Pereira <vpereira>
Component: IncidentsAssignee: Security Team bot <security-team>
Status: RESOLVED FIXED QA Contact: Security Team bot <security-team>
Severity: Normal    
Priority: P3 - Medium    
Version: unspecified   
Target Milestone: ---   
Hardware: Other   
OS: Other   
Whiteboard: CVSSv2:RedHat:CVE-2016-7126:4.3:(AV:N/AC:M/Au:N/C:N/I:N/A:P) maint:running:63038:important CVSSv2:SUSE:CVE-2016-7126:4.3:(AV:N/AC:M/Au:N/C:N/I:N/A:P)
Found By: --- Services Priority:
Business Priority: Blocker: ---
Marketing QA Status: --- IT Deployment: ---

Description Victor Pereira 2016-09-05 06:57:08 UTC
Description
============

Type mismatch parameters between ncolors and colorsWanted parameters at zif_imagetruecolortopalette and php_gd_gdImageTrueColorToPalette, ncolors is a 64 bit integer and colorsWanted is 32 bits, ncolors' value 0x1000000000000000 becomes 0 inside php_gd_gdImageTrueColorToPalette.

Later, select_colors will not allocate enough memory and writes out of bounds.

===============================================

PHP Source code: 

https://github.com/php/php-src/blob/PHP-7.0/ext/gd/libgd/gd_topal.c#L780

LOCAL (void)
#ifdef ORIGINAL_LIB_JPEG
select_colors (j_decompress_ptr cinfo, int desired_colors)
#else
select_colors (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int desired_colors)
#endif
/* Master routine for color selection */
{
  boxptr boxlist;
  int numboxes;
  int i;

  /* Allocate workspace for box list */
#ifdef ORIGINAL_LIB_JPEG
  boxlist = (boxptr) (*cinfo->mem->alloc_small)
    ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF (box));
#else
  boxlist = (boxptr) safe_emalloc(desired_colors, sizeof (box), 1);
  // desired_colors = 0 and reserved memory = 1
#endif
  /* Initialize one box containing whole space */
  numboxes = 1;
  boxlist[0].c0min = 0;                    // write out of bounds
  boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT;
  boxlist[0].c1min = 0;
  boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT;
  boxlist[0].c2min = 0;
  boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT;


GDB output
==========

USE_ZEND_ALLOC=0 gdb -q --args /home/operac/php-70-sinasan/sapi/cli/php -n poc.php

Reading symbols from /home/operac/php-70-sinasan/sapi/cli/php...done.
(gdb) b gd.c:1537
Breakpoint 1 at 0x549641: file /home/operac/php-70-sinasan/ext/gd/gd.c, line 1537.
(gdb) b php_gd_gdImageTrueColorToPalette
Breakpoint 2 at 0x569cce: file /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c, line 1768.
(gdb) r
Starting program: /home/operac/php-70-sinasan/sapi/cli/php -n poc.php

Breakpoint 1, zif_imagetruecolortopalette (execute_data=0x7ffff7ed70e0, return_value=0x7ffff7ed70d0) at /home/operac/php-70-sinasan/ext/gd/gd.c:1537
1537            gdImageTrueColorToPalette(im, dither, ncolors);
(gdb) p/x ncolors
$1 = 0x1000000000000000
(gdb) c
Continuing.

Breakpoint 2, php_gd_gdImageTrueColorToPalette (im=0x139de70, dither=0, colorsWanted=0) at /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c:1768
1768            gdImageTrueColorToPaletteBody(im, dither, colorsWanted, 0);
(gdb) p/x colorsWanted
$2 = 0x0     <---- parameter casted to 32 bits
(gdb)

gdb) b select_colors
Breakpoint 4 at 0x5686e5: file /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c, line 797.
(gdb) c
Continuing.

Breakpoint 4, select_colors (oim=0x139de70, nim=0x139de70, cquantize=0x13a66c0, desired_colors=0) at /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c:797
797       boxlist = (boxptr) safe_emalloc(desired_colors, sizeof (box), 1);
(gdb) b _safe_emalloc
Breakpoint 5 at 0x86d716: file /home/operac/php-70-sinasan/Zend/zend_alloc.c, line 2518.
(gdb) c
Continuing.

Breakpoint 5, _safe_emalloc (nmemb=0, size=40, offset=1, __zend_filename=0xd5ccd8 "/home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c", __zend_lineno=797, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at /home/operac/php-70-sinasan/Zend/zend_alloc.c:2518
2518            return emalloc_rel(safe_address(nmemb, size, offset));
(gdb) p nmemb
$3 = 0                <------ reserved memory =  (0 * 40) + 1 = 1 byte

(gdb) c
Program received signal SIGABRT, Aborted.

(gdb) bt
0x00007ffff5f7e418 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54      ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff5f7e418 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007ffff5f8001a in __GI_abort () at abort.c:89
#2  0x00007ffff5fc072a in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff60d96b0 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3  0x00007ffff5fc8f4a in malloc_printerr (ar_ptr=<optimized out>, ptr=<optimized out>, str=0x7ffff60d9728 "free(): invalid next size (fast)", action=3) at malloc.c:5007
#4  _int_free (av=<optimized out>, p=<optimized out>, have_lock=0) at malloc.c:3868
#5  0x00007ffff5fccabc in __GI___libc_free (mem=<optimized out>) at malloc.c:2969
#6  0x000000000086d45b in _efree (ptr=0x13a5ba0, __zend_filename=0xd5ccd8 "/home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c", __zend_lineno=841, __zend_orig_filename=0x0, __zend_orig_lineno=0)
    at /home/operac/php-70-sinasan/Zend/zend_alloc.c:2461
#7  0x00000000005688e8 in select_colors (oim=0x139de70, nim=0x139de70, cquantize=0x13a66c0, desired_colors=0) at /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c:841
#8  0x000000000056a0c2 in gdImageTrueColorToPaletteBody (oim=0x139de70, dither=0, colorsWanted=0, cimP=0x0) at /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c:1942
#9  0x0000000000569ce5 in php_gd_gdImageTrueColorToPalette (im=0x139de70, dither=0, colorsWanted=0) at /home/operac/php-70-sinasan/ext/gd/libgd/gd_topal.c:1768
#10 0x000000000054965c in zif_imagetruecolortopalette (execute_data=0x7ffff7ed70e0, return_value=0x7ffff7ed70d0) at /home/operac/php-70-sinasan/ext/gd/gd.c:1537
#11 0x0000000000902021 in ZEND_DO_ICALL_SPEC_HANDLER () at /home/operac/php-70-sinasan/Zend/zend_vm_execute.h:586
#12 0x0000000000901a4d in execute_ex (ex=0x7ffff7ed7040) at /home/operac/php-70-sinasan/Zend/zend_vm_execute.h:414
#13 0x0000000000901b5e in zend_execute (op_array=0x13a5f90, return_value=0x0) at /home/operac/php-70-sinasan/Zend/zend_vm_execute.h:458
#14 0x00000000008a2be4 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/operac/php-70-sinasan/Zend/zend.c:1427
#15 0x000000000080b2f9 in php_execute_script (primary_file=0x7fffffffd1f0) at /home/operac/php-70-sinasan/main/main.c:2494
#16 0x000000000096acf3 in do_cli (argc=3, argv=0x124e710) at /home/operac/php-70-sinasan/sapi/cli/php_cli.c:974
#17 0x000000000096bec1 in main (argc=3, argv=0x124e710) at /home/operac/php-70-sinasan/sapi/cli/php_cli.c:1344



======================


This issue doesn't affect upstream libgd, overflow2 function check will prevent it:

https://github.com/libgd/libgd/blob/master/src/gd_topal.c#L686

LOCAL (void)
select_colors (gdImagePtr oim, gdImagePtr nim, my_cquantize_ptr cquantize, int desired_colors)
/* Master routine for color selection */
{
        boxptr boxlist;
        int numboxes;
        int i;

        /* Allocate workspace for box list */
        /* This can't happen because we clamp desired_colors at gdMaxColors,
          but anyway */
        if (overflow2(desired_colors, sizeof (box))) {
                return;
        }


https://github.com/libgd/libgd/blob/master/src/gd_security.c#L21

int overflow2(int a, int b)
{
        if(a <= 0 || b <= 0) {
                gd_error_ex(GD_WARNING, "one parameter to a memory allocation multiplication is negative or zero, failing operation gracefully\n");
                return 1;
        }
        if(a > INT_MAX / b) {
                gd_error_ex(GD_WARNING, "product of memory allocation multiplication would exceed INT_MAX, failing operation gracefully\n");
                return 1;
        }
        return 0;
}



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

$img=imagecreatetruecolor(10, 10);
imagetruecolortopalette($img, false, PHP_INT_MAX / 8);


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

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

==25164==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000039790 at pc 0x000000a04930 bp 0x7fff2424a5e0 sp 0x7fff2424a5d0
WRITE of size 4 at 0x602000039790 thread T0
    #0 0xa0492f in select_colors /home/operac/php-70/ext/gd/libgd/gd_topal.c:801
    #1 0xa0492f in gdImageTrueColorToPaletteBody /home/operac/php-70/ext/gd/libgd/gd_topal.c:1942
    #2 0x967dac in zif_imagetruecolortopalette /home/operac/php-70/ext/gd/gd.c:1537
    #3 0x1da38da in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/php-70/Zend/zend_vm_execute.h:586
    #4 0x1b4c335 in execute_ex /home/operac/php-70/Zend/zend_vm_execute.h:414
    #5 0x1df9dc8 in zend_execute /home/operac/php-70/Zend/zend_vm_execute.h:458
    #6 0x194764a in zend_execute_scripts /home/operac/php-70/Zend/zend.c:1427
    #7 0x16b8347 in php_execute_script /home/operac/php-70/main/main.c:2494
    #8 0x1e02126 in do_cli /home/operac/php-70/sapi/cli/php_cli.c:974
    #9 0x467378 in main /home/operac/php-70/sapi/cli/php_cli.c:1344
    #10 0x7f67c285782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #11 0x467a48 in _start (/ramdisk/php-fuzz/phuzzer/php-70/sapi/cli/php+0x467a48)

0x602000039791 is located 0 bytes to the right of 1-byte region [0x602000039790,0x602000039791)
allocated by thread T0 here:
    #0 0x7f67c475d54a in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x9854a)
    #1 0xa00444 in select_colors /home/operac/php-70/ext/gd/libgd/gd_topal.c:797
    #2 0xa00444 in gdImageTrueColorToPaletteBody /home/operac/php-70/ext/gd/libgd/gd_topal.c:1942
    #3 0x967dac in zif_imagetruecolortopalette /home/operac/php-70/ext/gd/gd.c:1537
    #4 0x1da38da in ZEND_DO_ICALL_SPEC_HANDLER /home/operac/php-70/Zend/zend_vm_execute.h:586
    #5 0x1b4c335 in execute_ex /home/operac/php-70/Zend/zend_vm_execute.h:414
    #6 0x1df9dc8 in zend_execute /home/operac/php-70/Zend/zend_vm_execute.h:458
    #7 0x194764a in zend_execute_scripts /home/operac/php-70/Zend/zend.c:1427
    #8 0x16b8347 in php_execute_script /home/operac/php-70/main/main.c:2494
    #9 0x1e02126 in do_cli /home/operac/php-70/sapi/cli/php_cli.c:974
    #10 0x467378 in main /home/operac/php-70/sapi/cli/php_cli.c:1344
    #11 0x7f67c285782f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/operac/php-70/ext/gd/libgd/gd_topal.c:801 select_colors



References:
https://bugs.php.net/bug.php?id=72697
https://github.com/php/php-src/commit/b6f13a5ef9d6280cf984826a5de012a32c396cd4?w=1
Comment 1 Swamp Workflow Management 2016-09-05 22:00:43 UTC
bugbot adjusting priority
Comment 2 Petr Gajdos 2016-09-08 13:12:06 UTC
Packages submitted.
Comment 3 Bernhard Wiedemann 2016-09-08 14:00:51 UTC
This is an autogenerated message for OBS integration:
This bug (997208) was mentioned in
https://build.opensuse.org/request/show/425708 13.2 / php5
Comment 6 Swamp Workflow Management 2016-09-16 19:11:11 UTC
SUSE-SU-2016:2328-1: An update that fixes 18 vulnerabilities is now available.

Category: security (important)
Bug References: 987530,991426,991427,991428,991429,991430,991433,991437,997206,997207,997208,997210,997211,997220,997225,997230,997257
CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132
Sources used:
SUSE Linux Enterprise Server 11-SP2-LTSS (src):    php53-5.3.17-55.1
SUSE Linux Enterprise Debuginfo 11-SP2 (src):    php53-5.3.17-55.1
Comment 7 Swamp Workflow Management 2016-09-19 17:09:50 UTC
openSUSE-SU-2016:2337-1: An update that fixes 10 vulnerabilities is now available.

Category: security (important)
Bug References: 997206,997207,997208,997210,997211,997220,997225,997230,997248,997257
CVE References: CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134
Sources used:
openSUSE 13.2 (src):    php5-5.6.1-75.2
Comment 9 Swamp Workflow Management 2016-09-28 13:12:19 UTC
SUSE-SU-2016:2408-1: An update that fixes 24 vulnerabilities is now available.

Category: security (important)
Bug References: 987530,987580,988032,991422,991424,991426,991427,991428,991429,991430,991433,991434,991437,997206,997207,997208,997210,997211,997220,997225,997230,997248,997257
CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134
Sources used:
SUSE Linux Enterprise Software Development Kit 12-SP1 (src):    php5-5.5.14-73.1
SUSE Linux Enterprise Module for Web Scripting 12 (src):    php5-5.5.14-73.1
Comment 10 Swamp Workflow Management 2016-10-04 15:13:40 UTC
openSUSE-SU-2016:2451-1: An update that fixes 24 vulnerabilities is now available.

Category: security (important)
Bug References: 987530,987580,988032,991422,991424,991426,991427,991428,991429,991430,991433,991434,991437,997206,997207,997208,997210,997211,997220,997225,997230,997248,997257
CVE References: CVE-2014-3587,CVE-2016-3587,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6288,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7134
Sources used:
openSUSE Leap 42.1 (src):    php5-5.5.14-59.1
Comment 11 Swamp Workflow Management 2016-10-05 16:12:50 UTC
SUSE-SU-2016:2459-1: An update that fixes 16 vulnerabilities is now available.

Category: security (important)
Bug References: 997206,997207,997208,997210,997211,997220,997225,997230,997257,999679,999680,999682,999684,999685,999819,999820
CVE References: CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7411,CVE-2016-7412,CVE-2016-7413,CVE-2016-7414,CVE-2016-7416,CVE-2016-7417,CVE-2016-7418
Sources used:
SUSE OpenStack Cloud 5 (src):    php53-5.3.17-84.1
SUSE Manager Proxy 2.1 (src):    php53-5.3.17-84.1
SUSE Manager 2.1 (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Software Development Kit 11-SP4 (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Server 11-SP4 (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Server 11-SP3-LTSS (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Point of Sale 11-SP3 (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Debuginfo 11-SP4 (src):    php53-5.3.17-84.1
SUSE Linux Enterprise Debuginfo 11-SP3 (src):    php53-5.3.17-84.1
Comment 12 Swamp Workflow Management 2016-10-05 19:11:22 UTC
SUSE-SU-2016:2460-1: An update that solves 29 vulnerabilities and has two fixes is now available.

Category: security (important)
Bug References: 1001950,987580,988032,991422,991424,991426,991427,991428,991429,991430,991434,991437,995512,997206,997207,997208,997210,997211,997220,997225,997230,997247,997248,997257,999313,999679,999680,999684,999685,999819,999820
CVE References: CVE-2016-4473,CVE-2016-5399,CVE-2016-6128,CVE-2016-6161,CVE-2016-6207,CVE-2016-6289,CVE-2016-6290,CVE-2016-6291,CVE-2016-6292,CVE-2016-6295,CVE-2016-6296,CVE-2016-6297,CVE-2016-7124,CVE-2016-7125,CVE-2016-7126,CVE-2016-7127,CVE-2016-7128,CVE-2016-7129,CVE-2016-7130,CVE-2016-7131,CVE-2016-7132,CVE-2016-7133,CVE-2016-7134,CVE-2016-7412,CVE-2016-7413,CVE-2016-7414,CVE-2016-7416,CVE-2016-7417,CVE-2016-7418
Sources used:
SUSE Linux Enterprise Software Development Kit 12-SP1 (src):    php7-7.0.7-15.1
SUSE Linux Enterprise Module for Web Scripting 12 (src):    php7-7.0.7-15.1
Comment 13 Marcus Meissner 2016-10-31 08:27:15 UTC
released