Commit d83cf43811ba597069b4608ab0fd831688ffd6b1

Authored by m-holger
1 parent 3468ce36

In PL_DCT add option to limit the size of uncompressed corrupt data

Also, apply limit in dct_fuzzer
fuzz/dct_fuzzer.cc
... ... @@ -27,7 +27,7 @@ void
27 27 FuzzHelper::doChecks()
28 28 {
29 29 Pl_Discard discard;
30   - Pl_DCT p("decode", &discard);
  30 + Pl_DCT p("decode", &discard, 20'000'000);
31 31 p.write(const_cast<unsigned char*>(data), size);
32 32 p.finish();
33 33 }
... ...
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 }
... ...