stb_image: Early-out in progressive JPEG decode If we ran into an unexpected marker mid-scan and there is no more data to be read regularly, there's no point going into the regular MCU decode procedure. This stream is malformed; skip decoding coeff-by-coeff, just leave coeffs as they are (progressive decode inits coeffs to 0 at startup, so they're not uninitialized). Fixes very slow case found via fuzzing.
diff --git a/stb_image.h b/stb_image.h index 51ed3ba..9770b04 100644 --- a/stb_image.h +++ b/stb_image.h
@@ -3099,13 +3099,17 @@ for (j=0; j < h; ++j) { for (i=0; i < w; ++i) { short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; + // if we're out of data, just skip decoding this MCU + // progressive coeffs got initialized at setup so this is fine + if (!z->nomore) { + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } } // every data block is an MCU, so countdown the restart interval if (--z->todo <= 0) { @@ -3505,7 +3509,7 @@ if (!stbi__process_scan_header(j)) return 0; if (!stbi__parse_entropy_coded_data(j)) return 0; if (j->marker == STBI__MARKER_none ) { - j->marker = stbi__skip_jpeg_junk_at_end(j); + j->marker = stbi__skip_jpeg_junk_at_end(j); // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 } m = stbi__get_marker(j);
diff --git a/tests/local_fuzz/image_seeds/slow.jpg b/tests/local_fuzz/image_seeds/slow.jpg new file mode 100644 index 0000000..55c21ae --- /dev/null +++ b/tests/local_fuzz/image_seeds/slow.jpg Binary files differ