Commit 2bb9e06d1eb779edfe17f8ce37871ee3b1959211

Authored by m-holger
1 parent bc68003c

In qpdf_fuzzer add a memory limit for Pl_Flate

fuzz/qpdf_fuzzer.cc
@@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
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_Flate.hh>
5 #include <qpdf/Pl_PNGFilter.hh> 6 #include <qpdf/Pl_PNGFilter.hh>
6 #include <qpdf/Pl_TIFFPredictor.hh> 7 #include <qpdf/Pl_TIFFPredictor.hh>
7 #include <qpdf/QPDF.hh> 8 #include <qpdf/QPDF.hh>
@@ -183,6 +184,7 @@ FuzzHelper::doChecks() @@ -183,6 +184,7 @@ FuzzHelper::doChecks()
183 184
184 Pl_PNGFilter::setMemoryLimit(1'000'000); 185 Pl_PNGFilter::setMemoryLimit(1'000'000);
185 Pl_TIFFPredictor::setMemoryLimit(1'000'000); 186 Pl_TIFFPredictor::setMemoryLimit(1'000'000);
  187 + Pl_Flate::setMemoryLimit(10'000'000);
186 188
187 // Do not decompress corrupt data. This may cause extended runtime within jpeglib without 189 // Do not decompress corrupt data. This may cause extended runtime within jpeglib without
188 // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts. 190 // exercising additional code paths in qpdf, and potentially causing counterproductive timeouts.
include/qpdf/Pl_Flate.hh
@@ -42,6 +42,11 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline @@ -42,6 +42,11 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline
42 QPDF_DLL 42 QPDF_DLL
43 ~Pl_Flate() override; 43 ~Pl_Flate() override;
44 44
  45 + // Limit the memory used.
  46 + // NB This is a static option affecting all Pl_PNGFilter instances.
  47 + QPDF_DLL
  48 + static void setMemoryLimit(unsigned long long limit);
  49 +
45 QPDF_DLL 50 QPDF_DLL
46 void write(unsigned char const* data, size_t len) override; 51 void write(unsigned char const* data, size_t len) override;
47 QPDF_DLL 52 QPDF_DLL
@@ -87,6 +92,7 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline @@ -87,6 +92,7 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline
87 action_e action; 92 action_e action;
88 bool initialized; 93 bool initialized;
89 void* zdata; 94 void* zdata;
  95 + unsigned long long written{0};
90 std::function<void(char const*, int)> callback; 96 std::function<void(char const*, int)> callback;
91 }; 97 };
92 98
libqpdf/Pl_Flate.cc
@@ -7,6 +7,11 @@ @@ -7,6 +7,11 @@
7 #include <qpdf/QIntC.hh> 7 #include <qpdf/QIntC.hh>
8 #include <qpdf/QUtil.hh> 8 #include <qpdf/QUtil.hh>
9 9
  10 +namespace
  11 +{
  12 + unsigned long long memory_limit{0};
  13 +} // namespace
  14 +
10 int Pl_Flate::compression_level = Z_DEFAULT_COMPRESSION; 15 int Pl_Flate::compression_level = Z_DEFAULT_COMPRESSION;
11 16
12 Pl_Flate::Members::Members(size_t out_bufsize, action_e action) : 17 Pl_Flate::Members::Members(size_t out_bufsize, action_e action) :
@@ -64,6 +69,12 @@ Pl_Flate::~Pl_Flate() // NOLINT (modernize-use-equals-default) @@ -64,6 +69,12 @@ Pl_Flate::~Pl_Flate() // NOLINT (modernize-use-equals-default)
64 } 69 }
65 70
66 void 71 void
  72 +Pl_Flate::setMemoryLimit(unsigned long long limit)
  73 +{
  74 + memory_limit = limit;
  75 +}
  76 +
  77 +void
67 Pl_Flate::setWarnCallback(std::function<void(char const*, int)> callback) 78 Pl_Flate::setWarnCallback(std::function<void(char const*, int)> callback)
68 { 79 {
69 m->callback = callback; 80 m->callback = callback;
@@ -170,6 +181,12 @@ Pl_Flate::handleData(unsigned char const* data, size_t len, int flush) @@ -170,6 +181,12 @@ Pl_Flate::handleData(unsigned char const* data, size_t len, int flush)
170 } 181 }
171 uLong ready = QIntC::to_ulong(m->out_bufsize - zstream.avail_out); 182 uLong ready = QIntC::to_ulong(m->out_bufsize - zstream.avail_out);
172 if (ready > 0) { 183 if (ready > 0) {
  184 + if (memory_limit) {
  185 + m->written += ready;
  186 + if (m->written > memory_limit) {
  187 + throw std::runtime_error("PL_Flate memory limit exceeded");
  188 + }
  189 + }
173 this->getNext()->write(m->outbuf.get(), ready); 190 this->getNext()->write(m->outbuf.get(), ready);
174 zstream.next_out = m->outbuf.get(); 191 zstream.next_out = m->outbuf.get();
175 zstream.avail_out = QIntC::to_uint(m->out_bufsize); 192 zstream.avail_out = QIntC::to_uint(m->out_bufsize);