Project: libjpeg-turbo (libjpeg-turbo/libjpeg-turbo), language: c++
Vulnerability instance: libjpeg-turbo.cve-2018-19664
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/libjpeg-turbo/libjpeg-turbo/issues/305
## Description:
Issue: libjpeg-turbo/libjpeg-turbo#305
Title: heap-buffer-overflow in function put_pixel_rows in wrbmp.c:145
State: closed
Created by: cool-tomato
Created at: 2018-11-27 07:35:12+00:00
Labels: bug, fixed

Issue Body:
```sh
fish@ubuntu:~/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1$ ./afl/afl/bin/djpeg -colors 256 -bmp heap-buffer-overflow-2
Corrupt JPEG data: 117 extraneous bytes before marker 0xdb
=================================================================
==79144==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6100000000f7 at pc 0x558ce02a8b5d bp 0x7fffde75e590 sp 0x7fffde75e580
READ of size 1 at 0x6100000000f7 thread T0
    #0 0x558ce02a8b5c in put_pixel_rows /home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/wrbmp.c:145
    #1 0x558ce028f197 in main /home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/djpeg.c:762
    #2 0x7f0a3d9fcb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #3 0x558ce0290659 in _start (/home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/afl/afl/bin/djpeg+0x8659)

0x6100000000f7 is located 0 bytes to the right of 183-byte region [0x610000000040,0x6100000000f7)
allocated by thread T0 here:
    #0 0x7f0a3e141b50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x7f0a3de0ef68 in alloc_large /home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/jmemmgr.c:391
    #2 0x7f0a3de0ef68 in alloc_sarray /home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/jmemmgr.c:475

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/fish/Desktop/2018-10-10/image/libjpeg-turbo-2.0.1/wrbmp.c:145 in put_pixel_rows
Shadow bytes around the buggy address:
  0x0c207fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c207fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c207fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c207fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c207fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c207fff8010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00[07]fa
  0x0c207fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c207fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c207fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c207fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c207fff8060: 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
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==79144==ABORTING
```


![heap-buffer-overflow-2](https://user-images.githubusercontent.com/31661016/49065782-f0ebfd00-f216-11e8-9e9b-a86f3d5ea58a.jpg)



Comments:
Comment by kirotawa on 2018-11-29 14:06:51+00:00:
This issue has CVE-2018-19664  number assigned. 

---

Comment by pgajdos on 2018-12-03 13:41:43+00:00:
Do not need to use ASAN, valgrind suffices.

```
$ valgrind -q djpeg -colors 256 -bmp testcase.jpg
Corrupt JPEG data: 117 extraneous bytes before marker 0xdb
==790== Invalid read of size 1
==790==    at 0x10DBB8: cmyk_to_rgb (cmyk.h:55)
==790==    by 0x10DBB8: put_pixel_rows (wrbmp.c:146)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4deed77 is 0 bytes after a block of size 183 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x10E62D: jinit_write_bmp (wrbmp.c:554)
==790==    by 0x10A845: main (djpeg.c:646)
==790== 
==790== Invalid read of size 1
==790==    at 0x10DB98: put_pixel_rows (wrbmp.c:145)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4deed78 is 1 bytes after a block of size 183 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x10E62D: jinit_write_bmp (wrbmp.c:554)
==790==    by 0x10A845: main (djpeg.c:646)
==790== 
==790== Invalid read of size 1
==790==    at 0x10DB9C: put_pixel_rows (wrbmp.c:145)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4deed79 is 2 bytes after a block of size 183 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x10E62D: jinit_write_bmp (wrbmp.c:554)
==790==    by 0x10A845: main (djpeg.c:646)
==790== 
==790== Invalid read of size 1
==790==    at 0x10DBA8: put_pixel_rows (wrbmp.c:145)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4deed7a is 3 bytes after a block of size 183 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x10E62D: jinit_write_bmp (wrbmp.c:554)
==790==    by 0x10A845: main (djpeg.c:646)
==790== 
==790== Invalid write of size 1
==790==    at 0x10DBEC: cmyk_to_rgb (cmyk.h:55)
==790==    by 0x10DBEC: put_pixel_rows (wrbmp.c:146)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4df5099 is 2 bytes after a block of size 12,855 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x4083BF7: realize_virt_arrays (jmemmgr.c:717)
==790==    by 0x407350C: master_selection (jdmaster.c:573)
==790==    by 0x407350C: jinit_master_decompress (jdmaster.c:736)
==790==    by 0x406316C: jpeg_start_decompress (jdapistd.c:47)
==790==    by 0x10A668: main (djpeg.c:679)
==790== 
==790== Invalid write of size 1
==790==    at 0x10DC00: cmyk_to_rgb (cmyk.h:57)
==790==    by 0x10DC00: put_pixel_rows (wrbmp.c:146)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4df5097 is 0 bytes after a block of size 12,855 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x4083BF7: realize_virt_arrays (jmemmgr.c:717)
==790==    by 0x407350C: master_selection (jdmaster.c:573)
==790==    by 0x407350C: jinit_master_decompress (jdmaster.c:736)
==790==    by 0x406316C: jpeg_start_decompress (jdapistd.c:47)
==790==    by 0x10A668: main (djpeg.c:679)
==790== 
==790== Invalid write of size 1
==790==    at 0x10DC0B: cmyk_to_rgb (cmyk.h:56)
==790==    by 0x10DC0B: put_pixel_rows (wrbmp.c:146)
==790==    by 0x10A941: main (djpeg.c:762)
==790==  Address 0x4df5098 is 1 bytes after a block of size 12,855 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4083916: alloc_large (jmemmgr.c:391)
==790==    by 0x4083916: alloc_sarray (jmemmgr.c:475)
==790==    by 0x4083BF7: realize_virt_arrays (jmemmgr.c:717)
==790==    by 0x407350C: master_selection (jdmaster.c:573)
==790==    by 0x407350C: jinit_master_decompress (jdmaster.c:736)
==790==    by 0x406316C: jpeg_start_decompress (jdapistd.c:47)
==790==    by 0x10A668: main (djpeg.c:679)
==790== 
==790== Syscall param write(buf) points to uninitialised byte(s)
==790==    at 0x4B13DD4: write (in /lib64/libc-2.27.so)
==790==    by 0x4AA462C: _IO_file_write@@GLIBC_2.2.5 (in /lib64/libc-2.27.so)
==790==    by 0x4AA39DE: new_do_write (in /lib64/libc-2.27.so)
==790==    by 0x4AA5778: _IO_do_write@@GLIBC_2.2.5 (in /lib64/libc-2.27.so)
==790==    by 0x4AA5BAE: _IO_file_overflow@@GLIBC_2.2.5 (in /lib64/libc-2.27.so)
==790==    by 0x10E43F: finish_output_bmp (wrbmp.c:465)
==790==    by 0x10A79A: main (djpeg.c:800)
==790==  Address 0x4df6aa1 is 129 bytes inside a block of size 4,096 alloc'd
==790==    at 0x403077F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==790==    by 0x4A98B5B: _IO_file_doallocate (in /lib64/libc-2.27.so)
==790==    by 0x4AA6FCF: _IO_doallocbuf (in /lib64/libc-2.27.so)
==790==    by 0x4AA5BC7: _IO_file_overflow@@GLIBC_2.2.5 (in /lib64/libc-2.27.so)
==790==    by 0x4AA4CFE: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.27.so)
==790==    by 0x4A99F27: fwrite (in /lib64/libc-2.27.so)
==790==    by 0x10E29A: write_bmp_header (wrbmp.c:286)
==790==    by 0x10E3BB: finish_output_bmp (wrbmp.c:451)
==790==    by 0x10A79A: main (djpeg.c:800)
==790== 
$
```

---

Comment by pgajdos on 2018-12-03 13:45:31+00:00:
I do not get it with 1.5.3.

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
cmake . -DCMAKE_INSTALL_PREFIX=$WORK -DENABLE_STATIC:bool=on
make "-j$(nproc)"
make install -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool nasm curl cmake
RUN git clone https://github.com/libjpeg-turbo/libjpeg-turbo libjpeg-turbo
RUN git -C libjpeg-turbo checkout bb3d325624526c91646bb9af9578d7198c082d51
WORKDIR $SRC/libjpeg-turbo
COPY build.sh $SRC/

Additional files:
filename: libjpeg_turbo_fuzzer.cc
content: /*
# Copyright 2016 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 <stdlib.h>

#include <memory>

#include <turbojpeg.h>


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    tjhandle jpegDecompressor = tjInitDecompress();

    int width, height, subsamp, colorspace;
    int res = tjDecompressHeader3(
        jpegDecompressor, data, size, &width, &height, &subsamp, &colorspace);

    // Bail out if decompressing the headers failed, the width or height is 0,
    // or the image is too large (avoids slowing down too much). Cast to size_t to
    // avoid overflows on the multiplication
    if (res != 0 || width == 0 || height == 0 || ((size_t)width * height > (1024 * 1024))) {
        tjDestroy(jpegDecompressor);
        return 0;
    }

    std::unique_ptr<unsigned char[]> buf(new unsigned char[width * height * 3]);
    tjDecompress2(
        jpegDecompressor, data, size, buf.get(), width, 0, height, TJPF_RGB, 0);

    tjDestroy(jpegDecompressor);

    return 0;
}