mirror of
https://github.com/qpdf/qpdf.git
synced 2025-02-14 09:40:22 +00:00
Limit memory usage of Pl_Runlength during fuzzing
Fixes oss-fuzz case 394129398. Issue arose from chaining multiple runlength filters and inflating a compressed stream of ~100 bytes to several gigabytes. There is no obvious fix without imposing an arbitrary implementation limit and therefore potentially excluding valid PDF files.
This commit is contained in:
parent
ad3ecadf05
commit
671b6e2ecf
@ -147,10 +147,12 @@ set(CORPUS_OTHER
|
||||
369662293.fuzz
|
||||
369662293a.fuzz
|
||||
376305073.fuzz
|
||||
376305073a.fuzz
|
||||
377977949.fuzz
|
||||
389339260.fuzz
|
||||
389974979.fuzz
|
||||
391974927.fuzz
|
||||
394129398.fuzz
|
||||
)
|
||||
|
||||
set(CORPUS_DIR ${CMAKE_CURRENT_BINARY_DIR}/qpdf_corpus)
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFPageObjectHelper.hh>
|
||||
@ -108,6 +109,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFPageObjectHelper.hh>
|
||||
@ -108,6 +109,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
BIN
fuzz/qpdf_extra/376305073a.fuzz
Normal file
BIN
fuzz/qpdf_extra/376305073a.fuzz
Normal file
Binary file not shown.
BIN
fuzz/qpdf_extra/394129398.fuzz
Normal file
BIN
fuzz/qpdf_extra/394129398.fuzz
Normal file
Binary file not shown.
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFPageObjectHelper.hh>
|
||||
@ -106,6 +107,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFPageObjectHelper.hh>
|
||||
@ -107,6 +108,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFOutlineDocumentHelper.hh>
|
||||
@ -84,6 +85,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <qpdf/Pl_Discard.hh>
|
||||
#include <qpdf/Pl_Flate.hh>
|
||||
#include <qpdf/Pl_PNGFilter.hh>
|
||||
#include <qpdf/Pl_RunLength.hh>
|
||||
#include <qpdf/Pl_TIFFPredictor.hh>
|
||||
#include <qpdf/QPDF.hh>
|
||||
#include <qpdf/QPDFAcroFormDocumentHelper.hh>
|
||||
@ -105,6 +106,7 @@ FuzzHelper::doChecks()
|
||||
Pl_DCT::setScanLimit(50);
|
||||
|
||||
Pl_PNGFilter::setMemoryLimit(1'000'000);
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_TIFFPredictor::setMemoryLimit(1'000'000);
|
||||
Pl_Flate::setMemoryLimit(200'000);
|
||||
|
||||
|
@ -11,7 +11,7 @@ my $td = new TestDriver('fuzz');
|
||||
|
||||
my $qpdf_corpus = $ENV{'QPDF_FUZZ_CORPUS'} || die "must set QPDF_FUZZ_CORPUS";
|
||||
|
||||
my $n_qpdf_files = 88; # increment when adding new files
|
||||
my $n_qpdf_files = 90; # increment when adding new files
|
||||
|
||||
my @fuzzers = (
|
||||
['ascii85' => 1],
|
||||
|
@ -25,6 +25,7 @@ FuzzHelper::FuzzHelper(unsigned char const* data, size_t size) :
|
||||
void
|
||||
FuzzHelper::doChecks()
|
||||
{
|
||||
Pl_RunLength::setMemoryLimit(1'000'000);
|
||||
Pl_Discard discard;
|
||||
Pl_RunLength p("decode", &discard, Pl_RunLength::a_decode);
|
||||
p.write(const_cast<unsigned char*>(data), size);
|
||||
|
@ -46,7 +46,7 @@ class QPDF_DLL_CLASS Pl_Flate: public Pipeline
|
||||
~Pl_Flate() override;
|
||||
|
||||
// Limit the memory used.
|
||||
// NB This is a static option affecting all Pl_PNGFilter instances.
|
||||
// NB This is a static option affecting all Pl_Flate instances.
|
||||
QPDF_DLL
|
||||
static void setMemoryLimit(unsigned long long limit);
|
||||
|
||||
|
@ -32,6 +32,11 @@ class QPDF_DLL_CLASS Pl_RunLength: public Pipeline
|
||||
QPDF_DLL
|
||||
~Pl_RunLength() override;
|
||||
|
||||
// Limit the memory used.
|
||||
// NB This is a static option affecting all Pl_RunLength instances.
|
||||
QPDF_DLL
|
||||
static void setMemoryLimit(unsigned long long limit);
|
||||
|
||||
QPDF_DLL
|
||||
void write(unsigned char const* data, size_t len) override;
|
||||
QPDF_DLL
|
||||
|
@ -3,6 +3,11 @@
|
||||
#include <qpdf/QTC.hh>
|
||||
#include <qpdf/QUtil.hh>
|
||||
|
||||
namespace
|
||||
{
|
||||
unsigned long long memory_limit{0};
|
||||
} // namespace
|
||||
|
||||
Pl_RunLength::Members::Members(action_e action) :
|
||||
action(action)
|
||||
{
|
||||
@ -17,6 +22,12 @@ Pl_RunLength::Pl_RunLength(char const* identifier, Pipeline* next, action_e acti
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Pl_RunLength::setMemoryLimit(unsigned long long limit)
|
||||
{
|
||||
memory_limit = limit;
|
||||
}
|
||||
|
||||
Pl_RunLength::~Pl_RunLength() // NOLINT (modernize-use-equals-default)
|
||||
{
|
||||
// Must be explicit and not inline -- see QPDF_DLL_CLASS in README-maintainer
|
||||
@ -67,6 +78,9 @@ Pl_RunLength::encode(unsigned char const* data, size_t len)
|
||||
void
|
||||
Pl_RunLength::decode(unsigned char const* data, size_t len)
|
||||
{
|
||||
if (memory_limit && (len + m->out.size()) > memory_limit) {
|
||||
throw std::runtime_error("Pl_RunLength memory limit exceeded");
|
||||
}
|
||||
m->out.reserve(len);
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
unsigned char const& ch = data[i];
|
||||
|
@ -1877,8 +1877,8 @@ QPDFWriter::generateID()
|
||||
if (m->deterministic_id_data.empty()) {
|
||||
QTC::TC("qpdf", "QPDFWriter deterministic with no data");
|
||||
throw std::runtime_error("INTERNAL ERROR: QPDFWriter::generateID has no data for "
|
||||
"deterministic ID. This may happen if deterministic ID and "
|
||||
"file encryption are requested together.");
|
||||
"deterministic ID. This may happen if deterministic ID "
|
||||
"and file encryption are requested together.");
|
||||
}
|
||||
seed += m->deterministic_id_data;
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user