Commit 34729e37e001a92e5c7da250bd3118e052c79e64

Authored by m-holger
1 parent fe1fffe8

Limit memory used by Pl_PNGFilter and Pl_TIFFPredictor during fuzzing

fuzz/qpdf_fuzzer.cc
@@ -2,6 +2,8 @@ @@ -2,6 +2,8 @@
2 #include <qpdf/BufferInputSource.hh> 2 #include <qpdf/BufferInputSource.hh>
3 #include <qpdf/Pl_DCT.hh> 3 #include <qpdf/Pl_DCT.hh>
4 #include <qpdf/Pl_Discard.hh> 4 #include <qpdf/Pl_Discard.hh>
  5 +#include <qpdf/Pl_PNGFilter.hh>
  6 +#include <qpdf/Pl_TIFFPredictor.hh>
5 #include <qpdf/QPDF.hh> 7 #include <qpdf/QPDF.hh>
6 #include <qpdf/QPDFAcroFormDocumentHelper.hh> 8 #include <qpdf/QPDFAcroFormDocumentHelper.hh>
7 #include <qpdf/QPDFOutlineDocumentHelper.hh> 9 #include <qpdf/QPDFOutlineDocumentHelper.hh>
@@ -179,6 +181,9 @@ FuzzHelper::doChecks() @@ -179,6 +181,9 @@ FuzzHelper::doChecks()
179 // occur legitimately and therefore must be allowed during normal operations. 181 // occur legitimately and therefore must be allowed during normal operations.
180 Pl_DCT::setMemoryLimit(1'000'000'000); 182 Pl_DCT::setMemoryLimit(1'000'000'000);
181 183
  184 + Pl_PNGFilter::setMemoryLimit(1'000'000'000);
  185 + Pl_TIFFPredictor::setMemoryLimit(1'000'000'000);
  186 +
182 // Do not decompress corrupt data. This may cause extended runtime within jpeglib without 187 // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
183 // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts. 188 // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
184 Pl_DCT::setThrowOnCorruptData(true); 189 Pl_DCT::setThrowOnCorruptData(true);
libqpdf/Pl_PNGFilter.cc
@@ -7,6 +7,11 @@ @@ -7,6 +7,11 @@
7 #include <cstring> 7 #include <cstring>
8 #include <stdexcept> 8 #include <stdexcept>
9 9
  10 +namespace
  11 +{
  12 + unsigned long long memory_limit{0};
  13 +} // namespace
  14 +
10 static int 15 static int
11 abs_diff(int a, int b) 16 abs_diff(int a, int b)
12 { 17 {
@@ -41,6 +46,9 @@ Pl_PNGFilter::Pl_PNGFilter( @@ -41,6 +46,9 @@ Pl_PNGFilter::Pl_PNGFilter(
41 if ((bpr == 0) || (bpr > (UINT_MAX - 1))) { 46 if ((bpr == 0) || (bpr > (UINT_MAX - 1))) {
42 throw std::runtime_error("PNGFilter created with invalid columns value"); 47 throw std::runtime_error("PNGFilter created with invalid columns value");
43 } 48 }
  49 + if (memory_limit > 0 && bpr > (memory_limit / 2U)) {
  50 + throw std::runtime_error("PNGFilter memory limit exceeded");
  51 + }
44 this->bytes_per_row = bpr & UINT_MAX; 52 this->bytes_per_row = bpr & UINT_MAX;
45 this->buf1 = QUtil::make_shared_array<unsigned char>(this->bytes_per_row + 1); 53 this->buf1 = QUtil::make_shared_array<unsigned char>(this->bytes_per_row + 1);
46 this->buf2 = QUtil::make_shared_array<unsigned char>(this->bytes_per_row + 1); 54 this->buf2 = QUtil::make_shared_array<unsigned char>(this->bytes_per_row + 1);
@@ -54,6 +62,12 @@ Pl_PNGFilter::Pl_PNGFilter( @@ -54,6 +62,12 @@ Pl_PNGFilter::Pl_PNGFilter(
54 } 62 }
55 63
56 void 64 void
  65 +Pl_PNGFilter::setMemoryLimit(unsigned long long limit)
  66 +{
  67 + memory_limit = limit;
  68 +}
  69 +
  70 +void
57 Pl_PNGFilter::write(unsigned char const* data, size_t len) 71 Pl_PNGFilter::write(unsigned char const* data, size_t len)
58 { 72 {
59 size_t left = this->incoming - this->pos; 73 size_t left = this->incoming - this->pos;
libqpdf/Pl_TIFFPredictor.cc
@@ -7,6 +7,11 @@ @@ -7,6 +7,11 @@
7 #include <climits> 7 #include <climits>
8 #include <stdexcept> 8 #include <stdexcept>
9 9
  10 +namespace
  11 +{
  12 + unsigned long long memory_limit{0};
  13 +} // namespace
  14 +
10 Pl_TIFFPredictor::Pl_TIFFPredictor( 15 Pl_TIFFPredictor::Pl_TIFFPredictor(
11 char const* identifier, 16 char const* identifier,
12 Pipeline* next, 17 Pipeline* next,
@@ -31,10 +36,19 @@ Pl_TIFFPredictor::Pl_TIFFPredictor( @@ -31,10 +36,19 @@ Pl_TIFFPredictor::Pl_TIFFPredictor(
31 if ((bpr == 0) || (bpr > (UINT_MAX - 1))) { 36 if ((bpr == 0) || (bpr > (UINT_MAX - 1))) {
32 throw std::runtime_error("TIFFPredictor created with invalid columns value"); 37 throw std::runtime_error("TIFFPredictor created with invalid columns value");
33 } 38 }
  39 + if (memory_limit > 0 && bpr > (memory_limit / 2U)) {
  40 + throw std::runtime_error("TIFFPredictor memory limit exceeded");
  41 + }
34 this->bytes_per_row = bpr & UINT_MAX; 42 this->bytes_per_row = bpr & UINT_MAX;
35 } 43 }
36 44
37 void 45 void
  46 +Pl_TIFFPredictor::setMemoryLimit(unsigned long long limit)
  47 +{
  48 + memory_limit = limit;
  49 +}
  50 +
  51 +void
38 Pl_TIFFPredictor::write(unsigned char const* data, size_t len) 52 Pl_TIFFPredictor::write(unsigned char const* data, size_t len)
39 { 53 {
40 auto end = data + len; 54 auto end = data + len;
libqpdf/qpdf/Pl_PNGFilter.hh
@@ -24,6 +24,10 @@ class Pl_PNGFilter: public Pipeline @@ -24,6 +24,10 @@ class Pl_PNGFilter: public Pipeline
24 unsigned int bits_per_sample = 8); 24 unsigned int bits_per_sample = 8);
25 ~Pl_PNGFilter() override = default; 25 ~Pl_PNGFilter() override = default;
26 26
  27 + // Limit the memory used.
  28 + // NB This is a static option affecting all Pl_PNGFilter instances.
  29 + static void setMemoryLimit(unsigned long long limit);
  30 +
27 void write(unsigned char const* data, size_t len) override; 31 void write(unsigned char const* data, size_t len) override;
28 void finish() override; 32 void finish() override;
29 33
libqpdf/qpdf/Pl_TIFFPredictor.hh
@@ -22,6 +22,10 @@ class Pl_TIFFPredictor: public Pipeline @@ -22,6 +22,10 @@ class Pl_TIFFPredictor: public Pipeline
22 unsigned int bits_per_sample = 8); 22 unsigned int bits_per_sample = 8);
23 ~Pl_TIFFPredictor() override = default; 23 ~Pl_TIFFPredictor() override = default;
24 24
  25 + // Limit the memory used.
  26 + // NB This is a static option affecting all Pl_TIFFPredictor instances.
  27 + static void setMemoryLimit(unsigned long long limit);
  28 +
25 void write(unsigned char const* data, size_t len) override; 29 void write(unsigned char const* data, size_t len) override;
26 void finish() override; 30 void finish() override;
27 31