Commit d83cf43811ba597069b4608ab0fd831688ffd6b1
1 parent
3468ce36
In PL_DCT add option to limit the size of uncompressed corrupt data
Also, apply limit in dct_fuzzer
Showing
3 changed files
with
55 additions
and
36 deletions
fuzz/dct_fuzzer.cc
include/qpdf/Pl_DCT.hh
| ... | ... | @@ -34,6 +34,11 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline |
| 34 | 34 | QPDF_DLL |
| 35 | 35 | Pl_DCT(char const* identifier, Pipeline* next); |
| 36 | 36 | |
| 37 | + // Constructor for decompressing image data. If corrupt_data_limit is non-zero and the data is | |
| 38 | + // corrupt, only attempt to uncompress if the uncompressed size is less than corrupt_data_limit. | |
| 39 | + QPDF_DLL | |
| 40 | + Pl_DCT(char const* identifier, Pipeline* next, size_t corrupt_data_limit); | |
| 41 | + | |
| 37 | 42 | class QPDF_DLL_CLASS CompressConfig |
| 38 | 43 | { |
| 39 | 44 | public: |
| ... | ... | @@ -80,26 +85,30 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline |
| 80 | 85 | ~Members() = default; |
| 81 | 86 | |
| 82 | 87 | private: |
| 88 | + // For compression | |
| 83 | 89 | Members( |
| 84 | - action_e action, | |
| 85 | - char const* buf_description, | |
| 86 | - JDIMENSION image_width = 0, | |
| 87 | - JDIMENSION image_height = 0, | |
| 88 | - int components = 1, | |
| 89 | - J_COLOR_SPACE color_space = JCS_GRAYSCALE, | |
| 90 | - CompressConfig* config_callback = nullptr); | |
| 90 | + JDIMENSION image_width, | |
| 91 | + JDIMENSION image_height, | |
| 92 | + int components, | |
| 93 | + J_COLOR_SPACE color_space, | |
| 94 | + CompressConfig* config_callback); | |
| 95 | + // For decompression | |
| 96 | + Members(size_t corrupt_data_limit); | |
| 91 | 97 | Members(Members const&) = delete; |
| 92 | 98 | |
| 93 | 99 | action_e action; |
| 94 | 100 | Pl_Buffer buf; |
| 95 | 101 | |
| 102 | + // Used for decompression | |
| 103 | + size_t corrupt_data_limit{0}; | |
| 104 | + | |
| 96 | 105 | // Used for compression |
| 97 | - JDIMENSION image_width; | |
| 98 | - JDIMENSION image_height; | |
| 99 | - int components; | |
| 100 | - J_COLOR_SPACE color_space; | |
| 106 | + JDIMENSION image_width{0}; | |
| 107 | + JDIMENSION image_height{0}; | |
| 108 | + int components{1}; | |
| 109 | + J_COLOR_SPACE color_space{JCS_GRAYSCALE}; | |
| 101 | 110 | |
| 102 | - CompressConfig* config_callback; | |
| 111 | + CompressConfig* config_callback{nullptr}; | |
| 103 | 112 | }; |
| 104 | 113 | |
| 105 | 114 | std::shared_ptr<Members> m; | ... | ... |
libqpdf/Pl_DCT.cc
| 1 | 1 | #include <qpdf/Pl_DCT.hh> |
| 2 | 2 | |
| 3 | +#include "qpdf/QPDFLogger.hh" | |
| 3 | 4 | #include <qpdf/QIntC.hh> |
| 4 | 5 | #include <qpdf/QTC.hh> |
| 5 | 6 | |
| ... | ... | @@ -31,16 +32,21 @@ error_handler(j_common_ptr cinfo) |
| 31 | 32 | longjmp(jerr->jmpbuf, 1); |
| 32 | 33 | } |
| 33 | 34 | |
| 35 | +Pl_DCT::Members::Members(size_t corrupt_data_limit) : | |
| 36 | + action(a_decompress), | |
| 37 | + buf("DCT compressed image"), | |
| 38 | + corrupt_data_limit(corrupt_data_limit) | |
| 39 | +{ | |
| 40 | +} | |
| 41 | + | |
| 34 | 42 | Pl_DCT::Members::Members( |
| 35 | - action_e action, | |
| 36 | - char const* buf_description, | |
| 37 | 43 | JDIMENSION image_width, |
| 38 | 44 | JDIMENSION image_height, |
| 39 | 45 | int components, |
| 40 | 46 | J_COLOR_SPACE color_space, |
| 41 | 47 | CompressConfig* config_callback) : |
| 42 | - action(action), | |
| 43 | - buf(buf_description), | |
| 48 | + action(a_compress), | |
| 49 | + buf("DCT uncompressed image"), | |
| 44 | 50 | image_width(image_width), |
| 45 | 51 | image_height(image_height), |
| 46 | 52 | components(components), |
| ... | ... | @@ -50,8 +56,13 @@ Pl_DCT::Members::Members( |
| 50 | 56 | } |
| 51 | 57 | |
| 52 | 58 | Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next) : |
| 59 | + Pl_DCT(identifier, next, 0) | |
| 60 | +{ | |
| 61 | +} | |
| 62 | + | |
| 63 | +Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next, size_t corrupt_data_limit) : | |
| 53 | 64 | Pipeline(identifier, next), |
| 54 | - m(new Members(a_decompress, "DCT compressed image")) | |
| 65 | + m(new Members(corrupt_data_limit)) | |
| 55 | 66 | { |
| 56 | 67 | } |
| 57 | 68 | |
| ... | ... | @@ -64,14 +75,7 @@ Pl_DCT::Pl_DCT( |
| 64 | 75 | J_COLOR_SPACE color_space, |
| 65 | 76 | CompressConfig* config_callback) : |
| 66 | 77 | Pipeline(identifier, next), |
| 67 | - m(new Members( | |
| 68 | - a_compress, | |
| 69 | - "DCT uncompressed image", | |
| 70 | - image_width, | |
| 71 | - image_height, | |
| 72 | - components, | |
| 73 | - color_space, | |
| 74 | - config_callback)) | |
| 78 | + m(new Members(image_width, image_height, components, color_space, config_callback)) | |
| 75 | 79 | { |
| 76 | 80 | } |
| 77 | 81 | |
| ... | ... | @@ -311,16 +315,22 @@ Pl_DCT::decompress(void* cinfo_p, Buffer* b) |
| 311 | 315 | |
| 312 | 316 | (void)jpeg_read_header(cinfo, TRUE); |
| 313 | 317 | (void)jpeg_calc_output_dimensions(cinfo); |
| 314 | - | |
| 315 | 318 | unsigned int width = cinfo->output_width * QIntC::to_uint(cinfo->output_components); |
| 316 | - JSAMPARRAY buffer = | |
| 317 | - (*cinfo->mem->alloc_sarray)(reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, width, 1); | |
| 318 | - | |
| 319 | - (void)jpeg_start_decompress(cinfo); | |
| 320 | - while (cinfo->output_scanline < cinfo->output_height) { | |
| 321 | - (void)jpeg_read_scanlines(cinfo, buffer, 1); | |
| 322 | - this->getNext()->write(buffer[0], width * sizeof(buffer[0][0])); | |
| 319 | + if (cinfo->err->num_warnings == 0 || m->corrupt_data_limit == 0 || | |
| 320 | + (width * QIntC::to_uint(cinfo->output_height)) < m->corrupt_data_limit) { | |
| 321 | + // err->num_warnings is the number of corrupt data warnings emitted. | |
| 322 | + // err->msg_code could also be the code of an informational message. | |
| 323 | + JSAMPARRAY buffer = (*cinfo->mem->alloc_sarray)( | |
| 324 | + reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, width, 1); | |
| 325 | + | |
| 326 | + (void)jpeg_start_decompress(cinfo); | |
| 327 | + while (cinfo->output_scanline < cinfo->output_height) { | |
| 328 | + (void)jpeg_read_scanlines(cinfo, buffer, 1); | |
| 329 | + getNext()->write(buffer[0], width * sizeof(buffer[0][0])); | |
| 330 | + } | |
| 331 | + (void)jpeg_finish_decompress(cinfo); | |
| 332 | + } else { | |
| 333 | + *QPDFLogger::defaultLogger()->getError() << "corrupt JPEG data ignored" << "\n"; | |
| 323 | 334 | } |
| 324 | - (void)jpeg_finish_decompress(cinfo); | |
| 325 | - this->getNext()->finish(); | |
| 335 | + getNext()->finish(); | |
| 326 | 336 | } | ... | ... |