Commit 8ed10d71eaac74ea9afb430f1b548e6b915ec46d
1 parent
ef492916
In qpdf_fuzzer and dct_fuzzer add a scan limit for Pl_DCT
Showing
4 changed files
with
30 additions
and
0 deletions
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 | ... | ... |