From f48f73d4ec0eff7043de16f35c6ceabc301623c2 Mon Sep 17 00:00:00 2001 From: DRC Date: Wed, 5 Jul 2023 15:35:21 -0400 Subject: [PATCH] xform fuzz: Use src subsamp to calc dst buf size Referring to https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=60379 there are some specially-crafted malformed JPEG images that, when transformed to grayscale, will exceed the worst-case transformed grayscale JPEG image size. This is similar in nature to the issue fixed by 19f9d8f0fd9d753a7af3d5b4d58e20f9d752f5c0, except that in this case, the issue occurs regardless of the amount of metadata in the source image. Also, the tjTransform() function, the Java_org_libjpegturbo_turbojpeg_TJTransformer_transform() JNI function, and TJBench were behaving correctly in this case, because the TurboJPEG API documentation specifies that the source image's subsampling type should be used when computing the worst-case transformed JPEG image size. (However, only the Java API documentation specified that. Oops. The C API documentation now does as well.) The documented usage mitigates the issue, and only the transform fuzzer did not adhere to that. Thus, this was an issue with the fuzzer itself rather than an issue with the library. --- doc/html/group___turbo_j_p_e_g.html | 2 +- fuzz/transform.cc | 4 ++-- turbojpeg.h | 15 ++++++++------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/html/group___turbo_j_p_e_g.html b/doc/html/group___turbo_j_p_e_g.html index 39fe4d79..dd0f9aec 100644 --- a/doc/html/group___turbo_j_p_e_g.html +++ b/doc/html/group___turbo_j_p_e_g.html @@ -2664,7 +2664,7 @@ If you choose option 1, then *jpegSize should be set to the size of dstBufspointer to an array of n byte buffers. dstBufs[i] will receive a JPEG image that has been transformed using the parameters in transforms[i]. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:
  1. pre-allocate the JPEG destination buffer with an arbitrary size using tjAlloc() and let TurboJPEG grow the buffer as needed,
  2. set dstBufs[i] to NULL to tell TurboJPEG to allocate the buffer for you, or
  3. -
  4. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
  5. +
  6. pre-allocate the buffer to a "worst case" size determined by calling tjBufSize() with the transformed or cropped width and height and the level of subsampling used in the source image. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there are some rare cases (such as transforming images with a large amount of embedded EXIF or ICC profile data) in which the transformed JPEG image will be larger than the worst-case size, and TJFLAG_NOREALLOC cannot be used in those cases.
If you choose option 1, then dstSizes[i] should be set to the size of your pre-allocated buffer. In any case, unless you have set TJFLAG_NOREALLOC, you should always check dstBufs[i] upon return from this function, as it may have changed. dstSizespointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If dstBufs[i] points to a pre-allocated buffer, then dstSizes[i] should be set to the size of the buffer. Upon return, dstSizes[i] will contain the size of the transformed JPEG image (in bytes.) diff --git a/fuzz/transform.cc b/fuzz/transform.cc index 2eefd670..99d1584c 100644 --- a/fuzz/transform.cc +++ b/fuzz/transform.cc @@ -98,11 +98,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE; dstBufs[0] = (unsigned char *)malloc(tjBufSize((height + 1) / 2, (width + 1) / 2, - TJSAMP_GRAY)); + jpegSubsamp)); if (!dstBufs[0]) goto bailout; - maxBufSize = tjBufSize((height + 1) / 2, (width + 1) / 2, TJSAMP_GRAY); + maxBufSize = tjBufSize((height + 1) / 2, (width + 1) / 2, jpegSubsamp); if (tjTransform(handle, data, size, 1, dstBufs, dstSizes, transforms, TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC) == 0) { diff --git a/turbojpeg.h b/turbojpeg.h index c089f289..8664ab6f 100644 --- a/turbojpeg.h +++ b/turbojpeg.h @@ -1517,13 +1517,14 @@ DLLEXPORT tjhandle tjInitTransform(void); * -# set `dstBufs[i]` to NULL to tell TurboJPEG to allocate the buffer for * you, or * -# pre-allocate the buffer to a "worst case" size determined by calling - * #tjBufSize() with the transformed or cropped width and height. Under normal - * circumstances, this should ensure that the buffer never has to be - * re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.) - * Note, however, that there are some rare cases (such as transforming images - * with a large amount of embedded EXIF or ICC profile data) in which the - * transformed JPEG image will be larger than the worst-case size, and - * #TJFLAG_NOREALLOC cannot be used in those cases. + * #tjBufSize() with the transformed or cropped width and height and the level + * of subsampling used in the source image. Under normal circumstances, this + * should ensure that the buffer never has to be re-allocated. (Setting + * #TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there + * are some rare cases (such as transforming images with a large amount of + * embedded EXIF or ICC profile data) in which the transformed JPEG image will + * be larger than the worst-case size, and #TJFLAG_NOREALLOC cannot be used in + * those cases. * . * If you choose option 1, then `dstSizes[i]` should be set to the size of your * pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC,