Skip to content

Commit f48f73d

Browse files
committed
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 19f9d8f, 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.
1 parent 3832867 commit f48f73d

3 files changed

Lines changed: 11 additions & 10 deletions

File tree

doc/html/group___turbo_j_p_e_g.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2664,7 +2664,7 @@ <h2 class="memtitle"><span class="permalink"><a href="#ga9cb8abf4cc91881e04a0329
26642664
<tr><td class="paramname">dstBufs</td><td>pointer to an array of n byte buffers. <code>dstBufs[i]</code> will receive a JPEG image that has been transformed using the parameters in <code>transforms[i]</code>. TurboJPEG has the ability to reallocate the JPEG destination buffer to accommodate the size of the transformed JPEG image. Thus, you can choose to:<ol type="1">
26652665
<li>pre-allocate the JPEG destination buffer with an arbitrary size using <a class="el" href="group___turbo_j_p_e_g.html#gaec627dd4c5f30b7a775a7aea3bec5d83" title="Allocate a byte buffer for use with TurboJPEG.">tjAlloc()</a> and let TurboJPEG grow the buffer as needed,</li>
26662666
<li>set <code>dstBufs[i]</code> to NULL to tell TurboJPEG to allocate the buffer for you, or</li>
2667-
<li>pre-allocate the buffer to a "worst case" size determined by calling <a class="el" href="group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90" title="The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters.">tjBufSize()</a> with the transformed or cropped width and height. Under normal circumstances, this should ensure that the buffer never has to be re-allocated. (Setting <a class="el" href="group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963" title="Disable JPEG buffer (re)allocation.">TJFLAG_NOREALLOC</a> 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 <a class="el" href="group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963" title="Disable JPEG buffer (re)allocation.">TJFLAG_NOREALLOC</a> cannot be used in those cases.</li>
2667+
<li>pre-allocate the buffer to a "worst case" size determined by calling <a class="el" href="group___turbo_j_p_e_g.html#ga67ac12fee79073242cb216e07c9f1f90" title="The maximum size of the buffer (in bytes) required to hold a JPEG image with the given parameters.">tjBufSize()</a> 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 <a class="el" href="group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963" title="Disable JPEG buffer (re)allocation.">TJFLAG_NOREALLOC</a> 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 <a class="el" href="group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963" title="Disable JPEG buffer (re)allocation.">TJFLAG_NOREALLOC</a> cannot be used in those cases.</li>
26682668
</ol>
26692669
If you choose option 1, then <code>dstSizes[i]</code> should be set to the size of your pre-allocated buffer. In any case, unless you have set <a class="el" href="group___turbo_j_p_e_g.html#ga8808d403c68b62aaa58a4c1e58e98963" title="Disable JPEG buffer (re)allocation.">TJFLAG_NOREALLOC</a>, you should always check <code>dstBufs[i]</code> upon return from this function, as it may have changed.</td></tr>
26702670
<tr><td class="paramname">dstSizes</td><td>pointer to an array of n unsigned long variables that will receive the actual sizes (in bytes) of each transformed JPEG image. If <code>dstBufs[i]</code> points to a pre-allocated buffer, then <code>dstSizes[i]</code> should be set to the size of the buffer. Upon return, <code>dstSizes[i]</code> will contain the size of the transformed JPEG image (in bytes.)</td></tr>

fuzz/transform.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
9898
transforms[0].options = TJXOPT_GRAY | TJXOPT_CROP | TJXOPT_COPYNONE;
9999
dstBufs[0] =
100100
(unsigned char *)malloc(tjBufSize((height + 1) / 2, (width + 1) / 2,
101-
TJSAMP_GRAY));
101+
jpegSubsamp));
102102
if (!dstBufs[0])
103103
goto bailout;
104104

105-
maxBufSize = tjBufSize((height + 1) / 2, (width + 1) / 2, TJSAMP_GRAY);
105+
maxBufSize = tjBufSize((height + 1) / 2, (width + 1) / 2, jpegSubsamp);
106106

107107
if (tjTransform(handle, data, size, 1, dstBufs, dstSizes, transforms,
108108
TJFLAG_LIMITSCANS | TJFLAG_NOREALLOC) == 0) {

turbojpeg.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,13 +1517,14 @@ DLLEXPORT tjhandle tjInitTransform(void);
15171517
* -# set `dstBufs[i]` to NULL to tell TurboJPEG to allocate the buffer for
15181518
* you, or
15191519
* -# pre-allocate the buffer to a "worst case" size determined by calling
1520-
* #tjBufSize() with the transformed or cropped width and height. Under normal
1521-
* circumstances, this should ensure that the buffer never has to be
1522-
* re-allocated. (Setting #TJFLAG_NOREALLOC guarantees that it won't be.)
1523-
* Note, however, that there are some rare cases (such as transforming images
1524-
* with a large amount of embedded EXIF or ICC profile data) in which the
1525-
* transformed JPEG image will be larger than the worst-case size, and
1526-
* #TJFLAG_NOREALLOC cannot be used in those cases.
1520+
* #tjBufSize() with the transformed or cropped width and height and the level
1521+
* of subsampling used in the source image. Under normal circumstances, this
1522+
* should ensure that the buffer never has to be re-allocated. (Setting
1523+
* #TJFLAG_NOREALLOC guarantees that it won't be.) Note, however, that there
1524+
* are some rare cases (such as transforming images with a large amount of
1525+
* embedded EXIF or ICC profile data) in which the transformed JPEG image will
1526+
* be larger than the worst-case size, and #TJFLAG_NOREALLOC cannot be used in
1527+
* those cases.
15271528
* .
15281529
* If you choose option 1, then `dstSizes[i]` should be set to the size of your
15291530
* pre-allocated buffer. In any case, unless you have set #TJFLAG_NOREALLOC,

0 commit comments

Comments
 (0)