Commit 8ed10d71eaac74ea9afb430f1b548e6b915ec46d

Authored by m-holger
1 parent ef492916

In qpdf_fuzzer and dct_fuzzer add a scan limit for Pl_DCT

fuzz/dct_fuzzer.cc
... ... @@ -31,6 +31,7 @@ FuzzHelper::doChecks()
31 31 // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
32 32 // occur legitimately and therefore must be allowed during normal operations.
33 33 Pl_DCT::setMemoryLimit(200'000'000);
  34 + Pl_DCT::setScanLimit(50);
34 35  
35 36 // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
36 37 // exercising additional code paths in qpdf.
... ...
fuzz/qpdf_fuzzer.cc
... ... @@ -181,6 +181,7 @@ FuzzHelper::doChecks()
181 181 // jpeg_start_decompress is called. During normal use of qpdf very large JPEGs can occasionally
182 182 // occur legitimately and therefore must be allowed during normal operations.
183 183 Pl_DCT::setMemoryLimit(100'000'000);
  184 + Pl_DCT::setScanLimit(50);
184 185  
185 186 Pl_PNGFilter::setMemoryLimit(1'000'000);
186 187 Pl_TIFFPredictor::setMemoryLimit(1'000'000);
... ...
include/qpdf/Pl_DCT.hh
... ... @@ -39,6 +39,11 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline
39 39 QPDF_DLL
40 40 static void setMemoryLimit(long limit);
41 41  
  42 + // Limit the number of scans used by jpeglib when decompressing progressive jpegs.
  43 + // NB This is a static option affecting all Pl_DCT instances.
  44 + QPDF_DLL
  45 + static void setScanLimit(int limit);
  46 +
42 47 // Treat corrupt data as a runtime error rather than attempting to decompress regardless. This
43 48 // is the qpdf default behaviour. To attempt to decompress corrupt data set 'treat_as_error' to
44 49 // false.
... ...
libqpdf/Pl_DCT.cc
... ... @@ -22,6 +22,7 @@ namespace
22 22 };
23 23  
24 24 long memory_limit{0};
  25 + int scan_limit{0};
25 26 bool throw_on_corrupt_data{true};
26 27 } // namespace
27 28  
... ... @@ -45,6 +46,17 @@ emit_message(j_common_ptr cinfo, int msg_level)
45 46 }
46 47 }
47 48  
  49 +static void
  50 +progress_monitor(j_common_ptr cinfo)
  51 +{
  52 + if (cinfo->is_decompressor &&
  53 + reinterpret_cast<jpeg_decompress_struct*>(cinfo)->input_scan_number > scan_limit) {
  54 + auto* jerr = reinterpret_cast<qpdf_jpeg_error_mgr*>(cinfo->err);
  55 + jerr->msg = "Pl_DCT::decompress: JPEG data has too many scans";
  56 + longjmp(jerr->jmpbuf, 1);
  57 + }
  58 +}
  59 +
48 60 Pl_DCT::Members::Members() :
49 61 action(a_decompress),
50 62 buf("DCT compressed image")
... ... @@ -75,6 +87,12 @@ Pl_DCT::setMemoryLimit(long limit)
75 87 }
76 88  
77 89 void
  90 +Pl_DCT::setScanLimit(int limit)
  91 +{
  92 + scan_limit = limit;
  93 +}
  94 +
  95 +void
78 96 Pl_DCT::setThrowOnCorruptData(bool treat_as_error)
79 97 {
80 98 throw_on_corrupt_data = treat_as_error;
... ... @@ -341,6 +359,11 @@ Pl_DCT::decompress(void* cinfo_p, Buffer* b)
341 359 // first warning is encountered causing a timeout in oss-fuzz.
342 360 throw std::runtime_error("Pl_DCT::decompress: JPEG data large - may be too slow");
343 361 }
  362 + jpeg_progress_mgr progress_mgr;
  363 + if (scan_limit > 0) {
  364 + progress_mgr.progress_monitor = &progress_monitor;
  365 + cinfo->progress = &progress_mgr;
  366 + }
344 367 JSAMPARRAY buffer =
345 368 (*cinfo->mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, width, 1);
346 369  
... ...