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
| @@ -27,7 +27,7 @@ void | @@ -27,7 +27,7 @@ void | ||
| 27 | FuzzHelper::doChecks() | 27 | FuzzHelper::doChecks() |
| 28 | { | 28 | { |
| 29 | Pl_Discard discard; | 29 | Pl_Discard discard; |
| 30 | - Pl_DCT p("decode", &discard); | 30 | + Pl_DCT p("decode", &discard, 20'000'000); |
| 31 | p.write(const_cast<unsigned char*>(data), size); | 31 | p.write(const_cast<unsigned char*>(data), size); |
| 32 | p.finish(); | 32 | p.finish(); |
| 33 | } | 33 | } |
include/qpdf/Pl_DCT.hh
| @@ -34,6 +34,11 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline | @@ -34,6 +34,11 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline | ||
| 34 | QPDF_DLL | 34 | QPDF_DLL |
| 35 | Pl_DCT(char const* identifier, Pipeline* next); | 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 | class QPDF_DLL_CLASS CompressConfig | 42 | class QPDF_DLL_CLASS CompressConfig |
| 38 | { | 43 | { |
| 39 | public: | 44 | public: |
| @@ -80,26 +85,30 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline | @@ -80,26 +85,30 @@ class QPDF_DLL_CLASS Pl_DCT: public Pipeline | ||
| 80 | ~Members() = default; | 85 | ~Members() = default; |
| 81 | 86 | ||
| 82 | private: | 87 | private: |
| 88 | + // For compression | ||
| 83 | Members( | 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 | Members(Members const&) = delete; | 97 | Members(Members const&) = delete; |
| 92 | 98 | ||
| 93 | action_e action; | 99 | action_e action; |
| 94 | Pl_Buffer buf; | 100 | Pl_Buffer buf; |
| 95 | 101 | ||
| 102 | + // Used for decompression | ||
| 103 | + size_t corrupt_data_limit{0}; | ||
| 104 | + | ||
| 96 | // Used for compression | 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 | std::shared_ptr<Members> m; | 114 | std::shared_ptr<Members> m; |
libqpdf/Pl_DCT.cc
| 1 | #include <qpdf/Pl_DCT.hh> | 1 | #include <qpdf/Pl_DCT.hh> |
| 2 | 2 | ||
| 3 | +#include "qpdf/QPDFLogger.hh" | ||
| 3 | #include <qpdf/QIntC.hh> | 4 | #include <qpdf/QIntC.hh> |
| 4 | #include <qpdf/QTC.hh> | 5 | #include <qpdf/QTC.hh> |
| 5 | 6 | ||
| @@ -31,16 +32,21 @@ error_handler(j_common_ptr cinfo) | @@ -31,16 +32,21 @@ error_handler(j_common_ptr cinfo) | ||
| 31 | longjmp(jerr->jmpbuf, 1); | 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 | Pl_DCT::Members::Members( | 42 | Pl_DCT::Members::Members( |
| 35 | - action_e action, | ||
| 36 | - char const* buf_description, | ||
| 37 | JDIMENSION image_width, | 43 | JDIMENSION image_width, |
| 38 | JDIMENSION image_height, | 44 | JDIMENSION image_height, |
| 39 | int components, | 45 | int components, |
| 40 | J_COLOR_SPACE color_space, | 46 | J_COLOR_SPACE color_space, |
| 41 | CompressConfig* config_callback) : | 47 | CompressConfig* config_callback) : |
| 42 | - action(action), | ||
| 43 | - buf(buf_description), | 48 | + action(a_compress), |
| 49 | + buf("DCT uncompressed image"), | ||
| 44 | image_width(image_width), | 50 | image_width(image_width), |
| 45 | image_height(image_height), | 51 | image_height(image_height), |
| 46 | components(components), | 52 | components(components), |
| @@ -50,8 +56,13 @@ Pl_DCT::Members::Members( | @@ -50,8 +56,13 @@ Pl_DCT::Members::Members( | ||
| 50 | } | 56 | } |
| 51 | 57 | ||
| 52 | Pl_DCT::Pl_DCT(char const* identifier, Pipeline* next) : | 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 | Pipeline(identifier, next), | 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,14 +75,7 @@ Pl_DCT::Pl_DCT( | ||
| 64 | J_COLOR_SPACE color_space, | 75 | J_COLOR_SPACE color_space, |
| 65 | CompressConfig* config_callback) : | 76 | CompressConfig* config_callback) : |
| 66 | Pipeline(identifier, next), | 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,16 +315,22 @@ Pl_DCT::decompress(void* cinfo_p, Buffer* b) | ||
| 311 | 315 | ||
| 312 | (void)jpeg_read_header(cinfo, TRUE); | 316 | (void)jpeg_read_header(cinfo, TRUE); |
| 313 | (void)jpeg_calc_output_dimensions(cinfo); | 317 | (void)jpeg_calc_output_dimensions(cinfo); |
| 314 | - | ||
| 315 | unsigned int width = cinfo->output_width * QIntC::to_uint(cinfo->output_components); | 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 | } |