Coverage Report

Created: 2026-01-25 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libjpeg-turbo.dev/fuzz/decompress_libjpeg.cc
Line
Count
Source
1
/*
2
 * Copyright (C)2021-2024, 2026 D. R. Commander.  All Rights Reserved.
3
 * Copyright (C)2025 Leslie P. Polzer.  All Rights Reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions are met:
7
 *
8
 * - Redistributions of source code must retain the above copyright notice,
9
 *   this list of conditions and the following disclaimer.
10
 * - Redistributions in binary form must reproduce the above copyright notice,
11
 *   this list of conditions and the following disclaimer in the documentation
12
 *   and/or other materials provided with the distribution.
13
 * - Neither the name of the libjpeg-turbo Project nor the names of its
14
 *   contributors may be used to endorse or promote products derived from this
15
 *   software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
 * POSSIBILITY OF SUCH DAMAGE.
28
 */
29
30
/* This fuzzer uses the libjpeg API to exercise code paths that are not covered
31
 * by the other fuzzers (or by the TurboJPEG API in general):
32
 *
33
 * - JCS_UNKNOWN (NULL color conversion with a component count other than 3 or
34
 *   4)
35
 * - Floating point IDCT
36
 * - Buffered-image mode
37
 * - Interstitial line skipping
38
 * - jpeg_save_markers() with a length limit
39
 * - Custom marker processor
40
 * - JCS_RGB565
41
 * - Color quantization
42
 */
43
44
#include <stdint.h>
45
#include <stdio.h>
46
#include <stdlib.h>
47
#include <string.h>
48
#include <setjmp.h>
49
50
extern "C" {
51
#include "../src/jpeglib.h"
52
#include "../src/jerror.h"
53
}
54
55
56
struct fuzzer_error_mgr {
57
  struct jpeg_error_mgr pub;
58
  jmp_buf setjmp_buffer;
59
};
60
61
62
static void fuzzer_error_exit(j_common_ptr cinfo)
63
6.40k
{
64
6.40k
  struct fuzzer_error_mgr *fuzz_err = (struct fuzzer_error_mgr *)cinfo->err;
65
66
6.40k
  longjmp(fuzz_err->setjmp_buffer, 1);
67
6.40k
}
68
69
70
static void fuzzer_emit_message(j_common_ptr cinfo, int msg_level)
71
83.2M
{
72
83.2M
}
73
74
75
static int64_t marker_sum = 0;
76
77
static boolean custom_marker_processor(j_decompress_ptr cinfo)
78
4.42k
{
79
4.42k
  struct jpeg_source_mgr *src = cinfo->src;
80
4.42k
  INT32 length;
81
82
  /* Read and consume the 2-byte length field. */
83
4.42k
  if (src->bytes_in_buffer < 2)
84
61
    return FALSE;
85
86
4.36k
  length = ((INT32)src->next_input_byte[0] << 8) +
87
4.36k
            (INT32)src->next_input_byte[1];
88
4.36k
  src->next_input_byte += 2;
89
4.36k
  src->bytes_in_buffer -= 2;
90
4.36k
  length -= 2;
91
92
4.36k
  if (length < 0)
93
1.34k
    return FALSE;
94
95
  /* Consume and touch all marker data in order to catch uninitialized reads
96
     when using MemorySanitizer. */
97
5.90M
  while (length > 0) {
98
5.90M
    if (src->bytes_in_buffer == 0) {
99
5.90M
      if (!(*src->fill_input_buffer) (cinfo))
100
0
        return FALSE;
101
5.90M
    }
102
103
5.90M
    size_t available = (size_t)length < src->bytes_in_buffer ?
104
5.90M
                       (size_t)length : src->bytes_in_buffer;
105
106
18.6M
    for (size_t i = 0; i < available; i++)
107
12.7M
      marker_sum += src->next_input_byte[i];
108
109
5.90M
    src->next_input_byte += available;
110
5.90M
    src->bytes_in_buffer -= available;
111
5.90M
    length -= (INT32)available;
112
5.90M
  }
113
114
3.01k
  return TRUE;
115
3.01k
}
116
117
118
#define NUMTESTS  7
119
120
121
struct test {
122
  J_COLOR_SPACE out_color_space;
123
  boolean quantize_colors;
124
  boolean two_pass_quantize;
125
  J_DITHER_MODE dither_mode;
126
};
127
128
129
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
130
9.48k
{
131
9.48k
  struct jpeg_decompress_struct cinfo;
132
9.48k
  struct fuzzer_error_mgr jerr;
133
9.48k
  JSAMPARRAY buffer = NULL;
134
9.48k
  int row_stride;
135
9.48k
  int numTests = 1;
136
9.48k
  struct test tests[NUMTESTS] = {
137
    /*
138
      Output       Quantize 2-Pass Dither
139
      Colorspace   Colors   Quant  Mode
140
    */
141
9.48k
    { JCS_RGB565,  FALSE,   FALSE, JDITHER_NONE    },
142
9.48k
    { JCS_RGB565,  FALSE,   FALSE, JDITHER_ORDERED },
143
9.48k
    { JCS_UNKNOWN, TRUE,    FALSE, JDITHER_NONE    },
144
9.48k
    { JCS_UNKNOWN, TRUE,    FALSE, JDITHER_ORDERED },
145
9.48k
    { JCS_UNKNOWN, TRUE,    FALSE, JDITHER_FS      },
146
9.48k
    { JCS_UNKNOWN, TRUE,    TRUE,  JDITHER_NONE    },
147
9.48k
    { JCS_UNKNOWN, TRUE,    TRUE,  JDITHER_FS      }
148
9.48k
  };
149
150
  /* Reject too-small input. */
151
9.48k
  if (size < 2)
152
2
    return 0;
153
154
9.48k
  cinfo.err = jpeg_std_error(&jerr.pub);
155
9.48k
  jerr.pub.error_exit = fuzzer_error_exit;
156
9.48k
  jerr.pub.emit_message = fuzzer_emit_message;
157
158
9.48k
  jpeg_create_decompress(&cinfo);
159
160
37.0k
  for (int ti = 0; ti < numTests; ti++) {
161
27.6k
    int64_t sum = 0;
162
163
27.6k
    marker_sum = 0;
164
165
27.6k
    if (setjmp(jerr.setjmp_buffer)) {
166
6.40k
      jpeg_abort_decompress(&cinfo);
167
6.40k
      continue;
168
6.40k
    }
169
170
21.2k
    jpeg_mem_src(&cinfo, data, (unsigned long)size);
171
172
464k
    for (int m = JPEG_APP0; m <= JPEG_APP0 + 15; m++) {
173
442k
      if (m != JPEG_APP0 + 3)
174
415k
        jpeg_save_markers(&cinfo, m, 256);
175
442k
    }
176
21.2k
    jpeg_set_marker_processor(&cinfo, JPEG_APP0 + 3, custom_marker_processor);
177
178
21.2k
    jpeg_read_header(&cinfo, TRUE);
179
180
    /* Sanity check dimensions to avoid memory exhaustion.  Casting width to
181
       (uint64_t) prevents integer overflow if width * height > INT_MAX. */
182
25.6k
    if (cinfo.image_width < 1 || cinfo.image_height < 1 ||
183
25.6k
        (uint64_t)cinfo.image_width * cinfo.image_height > 1048576)
184
81
      goto bailout;
185
186
21.2k
    cinfo.dct_method = JDCT_FLOAT;
187
21.2k
    cinfo.buffered_image = jpeg_has_multiple_scans(&cinfo);
188
21.2k
    if (((cinfo.jpeg_color_space == JCS_YCbCr ||
189
19.8k
          cinfo.jpeg_color_space == JCS_RGB) && cinfo.num_components == 3) ||
190
5.70k
        (cinfo.jpeg_color_space == JCS_GRAYSCALE &&
191
23.8k
         cinfo.num_components == 1)) {
192
23.8k
      cinfo.out_color_space = tests[ti].out_color_space;
193
23.8k
      if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
194
4.06k
        numTests = 5;
195
4.06k
        if (cinfo.out_color_space == JCS_UNKNOWN)
196
2.05k
          cinfo.out_color_space = JCS_GRAYSCALE;
197
19.8k
      } else {
198
19.8k
        numTests = 7;
199
19.8k
        if (cinfo.out_color_space == JCS_UNKNOWN)
200
12.7k
          cinfo.out_color_space = ti % 2 ? JCS_RGB : JCS_EXT_BGR;
201
19.8k
      }
202
23.8k
      cinfo.quantize_colors = tests[ti].quantize_colors;
203
23.8k
      cinfo.two_pass_quantize = tests[ti].two_pass_quantize;
204
23.8k
      cinfo.dither_mode = tests[ti].dither_mode;
205
23.8k
    }
206
207
21.2k
    if (!jpeg_start_decompress(&cinfo)) {
208
0
      jpeg_abort_decompress(&cinfo);
209
0
      continue;
210
0
    }
211
212
21.2k
    row_stride = cinfo.output_width * cinfo.output_components;
213
21.2k
    buffer = (*cinfo.mem->alloc_sarray)
214
21.2k
      ((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
215
216
21.2k
    if (cinfo.buffered_image) {
217
      /* Process all scans. */
218
126k
      while (!jpeg_input_complete(&cinfo) &&
219
115k
             cinfo.input_scan_number != cinfo.output_scan_number) {
220
115k
        int retval;
221
222
115k
        if (cinfo.input_scan_number > 500) {
223
7
          jpeg_abort_decompress(&cinfo);
224
7
          goto bailout;
225
7
        }
226
227
        /* Consume input data until we have a complete scan or reach the end
228
           of input. */
229
10.4M
        do {
230
10.4M
          retval = jpeg_consume_input(&cinfo);
231
10.4M
        } while (retval != JPEG_SUSPENDED && retval != JPEG_REACHED_SOS &&
232
10.2M
                 retval != JPEG_REACHED_EOI);
233
234
115k
        if (retval == JPEG_REACHED_EOI || retval == JPEG_SUSPENDED)
235
8.05k
          break;
236
237
        /* Start outputting the current scan. */
238
107k
        if (!jpeg_start_output(&cinfo, cinfo.input_scan_number))
239
20
          break;
240
241
100M
        while (cinfo.output_scanline < cinfo.output_height) {
242
100M
          if (!cinfo.two_pass_quantize &&
243
75.6M
              (cinfo.output_scanline == 0 || cinfo.output_scanline == 16))
244
150k
            jpeg_skip_scanlines(&cinfo, 8);
245
100M
          else {
246
100M
            jpeg_read_scanlines(&cinfo, buffer, 1);
247
            /* Touch all of the output pixels in order to catch uninitialized
248
               reads when using MemorySanitizer. */
249
7.04G
            for (int i = 0; i < row_stride; i++)
250
6.94G
              sum += buffer[0][i];
251
100M
          }
252
100M
        }
253
254
        /* Finish this output pass. */
255
107k
        if (!jpeg_finish_output(&cinfo))
256
7
          break;
257
107k
      }
258
259
19.5k
    } else {
260
261
11.3M
      while (cinfo.output_scanline < cinfo.output_height) {
262
11.3M
        if (!cinfo.two_pass_quantize &&
263
9.87M
            (cinfo.output_scanline == 0 || cinfo.output_scanline == 16))
264
7.80k
          jpeg_skip_scanlines(&cinfo, 8);
265
11.3M
        else {
266
11.3M
          jpeg_read_scanlines(&cinfo, buffer, 1);
267
770M
          for (int i = 0; i < row_stride; i++)
268
759M
            sum += buffer[0][i];
269
11.3M
        }
270
11.3M
      }
271
272
1.66k
    }
273
274
21.1k
    jpeg_finish_decompress(&cinfo);
275
276
    /* Prevent the sums above from being optimized out.  This test should never
277
       be true, but the compiler doesn't know that. */
278
21.1k
    if (sum > (int64_t)255 * 1048576 * 4 ||
279
21.1k
        marker_sum > (int64_t)255 * 1048576)
280
8
      goto bailout;
281
21.1k
  }
282
283
9.48k
bailout:
284
9.48k
  jpeg_destroy_decompress(&cinfo);
285
9.48k
  return 0;
286
9.48k
}