Project: libgd (libgd/libgd), language: c++
Vulnerability instance: libgd.cve-2016-6132
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/libgd/libgd/issues/247
## Description:
Issue: libgd/libgd#247
Title: [CVE-2016-6132]: A read out-of-bands was found in the parsing of TGA files
State: closed
Created by: gaa-cifasis
Created at: 2016-06-30 13:33:42+00:00
Labels: bug

Issue Body:
Hi,

A read out-of-bands was found in the parsing of TGA files using the last revision of libgd (a6a0e7feabb2a9738086a5dc96348f233c87fa79). Find [attached a small sample (it is a tga, not a really a txt)](https://github.com/libgd/libgd/files/341530/test.txt) to reproduce it. The ASAN report is here:

```
==25148==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62500000e91c at pc 0x7ffff6c8b446 bp 0x7fffffffdf40 sp 0x7fffffffdf38
READ of size 4 at 0x62500000e91c thread T0
    #0 0x7ffff6c8b445 in gdImageCreateFromTgaCtx /tmp/libgd/src/gd_tga.c:103
    #1 0x7ffff6c8ad26 in gdImageCreateFromTga /tmp/libgd/src/gd_tga.c:25
    #2 0x401581 in main tga/bug00084.c:10
    #3 0x7ffff686aec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)
    #4 0x401458 (/tmp/libgd/tests/tga/.libs/lt-bug00084+0x401458)

0x62500000e91c is located 0 bytes to the right of 8220-byte region [0x62500000c900,0x62500000e91c)
allocated by thread T0 here:
    #0 0x7ffff6f567df in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.1+0x547df)
    #1 0x7ffff6ca49e1 in gdMalloc /tmp/libgd/src/gdhelpers.c:75
    #2 0x7ffff6c8c254 in read_image_tga /tmp/libgd/src/gd_tga.c:226
    #3 0x7ffff6c8af91 in gdImageCreateFromTgaCtx /tmp/libgd/src/gd_tga.c:74
    #4 0x7ffff6c8ad26 in gdImageCreateFromTga /tmp/libgd/src/gd_tga.c:25
    #5 0x401581 in main tga/bug00084.c:10
    #6 0x7ffff686aec4 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21ec4)

SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/libgd/src/gd_tga.c:103 gdImageCreateFromTgaCtx
```

(it is not related with bug 00084, i just re-used the test case to read an arbitrary TGA file)

This issue was found using QuickFuzz.

Regards,
Gustavo.



Comments:
Comment by cmb69 on 2016-07-03 19:08:54+00:00:
I can confirm the issue using valgrind.

The reason of the bug is that the TARGA file is corrupt; its header reports a size of 1x2055 pixels, so 2055 bytes are to be read, but it only contains 56 bytes of image data. It seems to me that checking that the return value of the respective [gdGetBuf](https://github.com/libgd/libgd/blob/gd-2.2.2/src/gd_tga.c#L240) is equal to image_block_size, and gracefully error'ing out otherwise is the best solution. The [same check](https://github.com/libgd/libgd/blob/gd-2.2.2/src/gd_tga.c#L264) would have to be done for RLE TARGA files, and maybe for other file types as well.


---

Comment by vapier on 2016-07-04 03:03:55+00:00:
agreed on both.  want to put together the PR ? :)


---

Comment by cmb69 on 2016-07-04 11:26:47+00:00:
I'll give it try, but it may take some days (rather busy otherwise, currently).


---

Comment by oerdnj on 2016-07-12 09:49:28+00:00:
@cmb69 @pierrejoye @vapier Folks could you review the changes in PR #250 ?


---

Comment by gaa-cifasis on 2016-07-12 15:27:28+00:00:
It looks like QuickFuzz can still find a read out-of-bound. The new test case is [here](https://gist.github.com/gaa-cifasis/3109030cb01c14500fcf52cfe3fc9c27). We recently added a new feature to our tool, to dump the internal structure of the corrupted file causing a crash in the .val file (hopefully it can help!). 

```
==20805== ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60060000f00c at pc 0x41a8f0 bp 0x7fffffffe3e0 sp 0x7fffffffe3d8
READ of size 4 at 0x60060000f00c thread T0
    #0 0x41a8ef (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41a8ef)
    #1 0x41a219 (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41a219)
    #2 0x4011ca (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x4011ca)
    #3 0x7ffff47a1ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
    #4 0x4010a8 (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x4010a8)
0x60060000f00c is located 12 bytes to the right of 32-byte region [0x60060000efe0,0x60060000f000)
allocated by thread T0 here:
    #0 0x7ffff4e6041a (/usr/lib/x86_64-linux-gnu/libasan.so.0.0.0+0x1541a)
    #1 0x41d353 (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41d353)
    #2 0x41b999 (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41b999)
    #3 0x41a43e (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41a43e)
    #4 0x41a219 (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x41a219)
    #5 0x4011ca (/home/vagrant/repos/libgd-asan/tests/gif/bug00066+0x4011ca)
    #6 0x7ffff47a1ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
Shadow bytes around the buggy address:
  0x0c013fff9db0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9dc0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9dd0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9de0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9df0: fa fa fa fa fa fa fa fa fa fa fa fa 00 00 00 00
=>0x0c013fff9e00: fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9e10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9e20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9e30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9e40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c013fff9e50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:     fa
  Heap righ redzone:     fb
  Freed Heap region:     fd
  Stack left redzone:    f1
  Stack mid redzone:     f2
  Stack right redzone:   f3
  Stack partial redzone: f4
  Stack after return:    f5
  Stack use after scope: f8
  Global redzone:        f9
  Global init order:     f6
  Poisoned by user:      f7
  ASan internal:         fe
==20805== ABORTING

Program received signal SIGABRT, Aborted.
0x00007ffff47b6cc9 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
56 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0  0x00007ffff47b6cc9 in __GI_raise (sig=sig@entry=6) at ../nptl/sysdeps/unix/sysv/linux/raise.c:56
#1  0x00007ffff47ba0d8 in __GI_abort () at abort.c:89
#2  0x00007ffff4e66829 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#3  0x00007ffff4e5d3ec in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#4  0x00007ffff4e64012 in ?? () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#5  0x00007ffff4e63121 in __asan_report_error () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#6  0x00007ffff4e5d704 in __asan_report_load4 () from /usr/lib/x86_64-linux-gnu/libasan.so.0
#7  0x000000000041a8f0 in gdImageCreateFromTgaCtx (ctx=0x600e0000dfb0) at gd_tga.c:103
#8  0x000000000041a21a in gdImageCreateFromTga (fp=0x60360000fd80) at gd_tga.c:25
#9  0x00000000004011cb in main (argc=2, argv=0x7fffffffe5f8) at gif/bug00066.c:16
```

Maybe this patch wasn't complete?


---

Comment by oerdnj on 2016-07-12 15:30:08+00:00:
@gaa-cifasis Have you tested QuickFuzz with #251 ?


---

Comment by cmb69 on 2016-07-12 15:54:23+00:00:
This TGA is not (recognized as being) RLE'd, so #251 doesn't apply.

Anyway, I can reproduce the issue with valgrind, so re-opening.


---

Comment by cmb69 on 2016-07-12 16:29:05+00:00:
The problem is https://github.com/libgd/libgd/blob/gd-2.2.2/src/gd_tga.c#L102. In this case `tga->bits == TGA_BPP_8 && tga->alphabits == 1`, but the code in the if body assumes `tga->bits == TGA_BPP_32`. The comment above the respective code block already hints, that this combination is not supported. The condition is supposed to be:

```
} else if (tga->bits == TGA_BPP_32 && tga->alphabits) {
```

Also we probably need an else block to gracefully fail for unsupported tga->bits and tga->alphabits.


---

Comment by cmb69 on 2016-07-12 17:34:11+00:00:
I have a fix, but that breaks tests/tga/bug00084, because bug00084.tga has 8bpp, what is not supposed to be supported anyway. I'll see to it.


---

Comment by cmb69 on 2016-07-12 18:13:53+00:00:
> […],  because bug00084.tga has 8bpp, […]

Well, not really. I simply screwed up the patch.


---

Comment by cmb69 on 2016-07-12 18:48:27+00:00:
Fixed with commit 10ef1dc; closing.


---

Comment by oerdnj on 2016-07-12 19:52:28+00:00:
@cmb69 did you merged/cherry-picked it to GD-2.2? 


---

Comment by cmb69 on 2016-07-12 20:10:39+00:00:
The build fails, so I've reverted the commit for now. I'll have to take a closer look.


---

Comment by oerdnj on 2016-07-12 20:44:24+00:00:
@cmb69 Does it fail everywhere or just Mac OS X? (The failure on Macs is orthogonal to this issue...)


---

Comment by cmb69 on 2016-07-12 23:36:48+00:00:
No, it did fail everywhere due to a typo in Makemodule.am. I've fixed that with commit cb1a0b7. Now the Linux checks work fine, but the OS X checks seem to stall (https://travis-ci.org/libgd/libgd/builds/144312731). :-/


---

Comment by cmb69 on 2016-07-13 00:06:08+00:00:
Ah, apparently Travis was just busy! Eventually, [all checks succeeded](https://travis-ci.org/libgd/libgd/builds/144312731), and so I [cherry-picked into GD-2.2](https://github.com/libgd/libgd/commit/341aa68).

@gaa-cifasis Thanks for the report(s)! If you find further bugs, please file new issues. Actually, [the first](https://github.com/libgd/libgd/issues/247#issue-163166532) and [the second](https://github.com/libgd/libgd/issues/247#issuecomment-232084241) where not really related, and it's easier for us to separate concerns.


---

Comment by gaa-cifasis on 2016-07-13 06:59:38+00:00:
@cmb69 You are welcome!, I was wondering if the second test case was the same as #248 (so a different CVE can be requested).


---

Comment by cmb69 on 2016-07-13 08:57:36+00:00:
#248 is an issue with RunLengthEncoded TGA files, the two issues of #247 are different issues with uncompressed TGAs.


---

Comment by oerdnj on 2016-07-13 09:09:02+00:00:
JFTR CVE for #248 has already been requested by Debian Security Team.


---

Comment by oerdnj on 2016-07-14 08:32:11+00:00:
Second issue is CVE-2016-6214


Attachments:
https://github.com/libgd/libgd/files/341530/test.txt

Commit References:
a6a0e7feabb2a9738086a5dc96348f233c87fa79

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
./bootstrap.sh
./configure --prefix="$WORK" --disable-shared
make -j$(nproc) install

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool pkg-config libz-dev
RUN apt-get install -y make autoconf automake libtool pkg-config libz-dev
RUN git clone https://github.com/libgd/libgd libgd
RUN git -C libgd checkout ec6b61f591b949a1431280899f21c098ffe8ec14
WORKDIR $SRC/libgd
COPY build.sh $SRC/

Additional files:
filename: parser_target.cc
content: // Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/////////////////////////////////////////////////////////////////////////////

#include <stdint.h>
#include "gd.h"

#define PASTE(x) gdImageCreateFrom ## x ## Ptr
#define CREATE_IMAGE(FORMAT) PASTE(FORMAT)

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    gdImagePtr im = CREATE_IMAGE(FUZZ_GD_FORMAT)(Size, (void*) Data);
    if (im) gdImageDestroy(im);
    return 0;
}