php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Sec Bug #72697 select_colors write out-of-bounds
Submitted: 2016-07-28 06:38 UTC Modified: 2016-09-05 15:28 UTC
From: fernando at null-life dot com Assigned: stas (profile)
Status: Closed Package: GD related
PHP Version: 5.6.24 OS: *
Private report: No CVE-ID: 2016-7126
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-28 06:38 UTC] fernando at null-life dot com
Description:
------------
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


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-08-10 07:01 UTC] stas@php.net
-PHP Version: 7.0.9 +PHP Version: 5.6.24 -Assigned To: +Assigned To: stas
 [2016-08-10 07:01 UTC] stas@php.net
Fix in https://gist.github.com/9fa708887b20a02bac3a0820e8237be9
and in security repo as b6f13a5ef9d6280cf984826a5de012a32c396cd4

please verify
 [2016-08-15 06:03 UTC] stas@php.net
-CVE-ID: +CVE-ID: needed
 [2016-08-17 06:41 UTC] stas@php.net
-Status: Assigned +Status: Closed
 [2016-08-17 06:41 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.


 [2016-09-05 15:28 UTC] remi@php.net
-CVE-ID: needed +CVE-ID: 2016-7126
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Tue Dec 03 17:01:29 2024 UTC