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

================= Bug Report (1/1) ==================
## Source: Red Hat Bugzilla
## URL: https://bugzilla.redhat.com/show_bug.cgi?id=1943797
## Description:
Bugzilla ID: 1943797
Title: libhpeg-turbo version 2.0.90 is vulnerable to a heap-buffer-overflow vulnerability in decompress_smooth_data in jdcoefct.c.

Description
Jie_0Y 2021-03-27 13:51:18 UTC
Created attachment 1766892 [details]
poc

Description of problem:
libhpeg-turbo version 2.0.90 is vulnerable to a heap-buffer-overflow vulnerability in decompress_smooth_data in jdcoefct.c.

Version-Release number of selected component (if applicable):
2.0.90(2.1 beta1)

How reproducible:
OS version: ubuntu 18.04.1
Linux 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

GCC version
gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)

Steps to Reproduce:
Compile with Address Sanitizer (ASan) :
./djpeg poc.jpg

Actual results:
./djpeg poc.jpg 
Corrupt JPEG data: 13 extraneous bytes before marker 0xdb
P6
4 544
255
=================================================================
==42286==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x621000004ea0 at pc 0x7fe1b5a4f17c bp 0x7ffc88ac4870 sp 0x7ffc88ac4860
READ of size 2 at 0x621000004ea0 thread T0
    #0 0x7fe1b5a4f17b in decompress_smooth_data /home/ostrich/build/libjpeg-turbo-2.0.90/jdcoefct.c:595
    #1 0x7fe1b5a81b49 in process_data_simple_main /home/ostrich/build/libjpeg-turbo-2.0.90/jdmainct.c:300
    #2 0x7fe1b5a3423c in jpeg_read_scanlines /home/ostrich/build/libjpeg-turbo-2.0.90/jdapistd.c:287
    #3 0x55f4b1cb2b84 in main /home/ostrich/build/libjpeg-turbo-2.0.90/djpeg.c:810
    #4 0x7fe1b552abf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #5 0x55f4b1cb4179 in _start (/home/ostrich/testbases/libjpeg/djpeg+0x9179)

0x621000004ea0 is located 105 bytes to the right of 4407-byte region [0x621000003d00,0x621000004e37)
allocated by thread T0 here:
    #0 0x7fe1b5e8cb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
    #1 0x7fe1b5b30e47 in alloc_large /home/ostrich/build/libjpeg-turbo-2.0.90/jmemmgr.c:391
    #2 0x7fe1b5b30e47 in alloc_barray /home/ostrich/build/libjpeg-turbo-2.0.90/jmemmgr.c:527
    #3 0x7fe1b5b30e47 in realize_virt_arrays /home/ostrich/build/libjpeg-turbo-2.0.90/jmemmgr.c:741

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ostrich/build/libjpeg-turbo-2.0.90/jdcoefct.c:595 in decompress_smooth_data
Shadow bytes around the buggy address:
  0x0c427fff8980: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff8990: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff89a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff89b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c427fff89c0: 00 00 00 00 00 00 07 fa fa fa fa fa fa fa fa fa
=>0x0c427fff89d0: fa fa fa fa[fa]fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff89e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff89f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff8a00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff8a10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c427fff8a20: 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
==42286==ABORTING

Expected results:


Additional info:


Comments:
Comment 1
Sandipan Roy 2023-08-30 13:18:08 UTC
Making the report Public as CVE-2021-29390 is already public.

---

Comment 2
František Zatloukal 2024-02-06 23:04:22 UTC
This was fixed in the 2.1.0 release long time ago, closing.

Attachments:
Attachment 1766892: poc [Link: https://bugzilla.redhat.com/attachment.cgi?id=1766892]

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 4e52b66f342a803d3b8099b79607e3158d3a241c
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;
    }

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

    // For memory sanitizer, test each output byte
    const unsigned char* raw_buf = buf.get();
    int count = 0;
    for( int i = 0; i < buffer_size; i++ )
    {
        if (raw_buf[i])
        {
            count ++;
        }
    }
    if (count == buffer_size)
    {
        // Do something with side effect, so that all the above tests don't
        // get removed by the optimizer.
        free(malloc(1));
    }

    tjDestroy(jpegDecompressor);

    return 0;
}