From 6fe7b704c7dfb517e4de20fb25536fab1de83d56 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Wed, 6 Jan 2021 09:49:10 -0500 Subject: [PATCH] Warn rather than segv on access after closing input source (fixes #495) --- ChangeLog | 5 ++++ libqpdf/QPDF.cc | 47 +++++++++++++++++++++++++++++++++++++- qpdf/qtest/qpdf.test | 8 ++++++- qpdf/qtest/qpdf/test73.out | 2 ++ qpdf/test_driver.cc | 5 ++++ 5 files changed, 65 insertions(+), 2 deletions(-) create mode 100644 qpdf/qtest/qpdf/test73.out diff --git a/ChangeLog b/ChangeLog index 77dc7b73..cd0a0e02 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2021-01-06 Jay Berkenbilt + + * Give warnings instead of segfaulting if a QPDF operation is + attempted after calling closeInputSource(). Fixes #495. + 2021-01-05 Jay Berkenbilt * 10.1.0: release diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 29508a40..34f15706 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -45,6 +45,51 @@ static char const* EMPTY_PDF = "110\n" "%%EOF\n"; +class InvalidInputSource: public InputSource +{ + public: + virtual ~InvalidInputSource() = default; + virtual qpdf_offset_t findAndSkipNextEOL() override + { + throwException(); + return 0; + } + virtual std::string const& getName() const override + { + static std::string name("closed input source"); + return name; + } + virtual qpdf_offset_t tell() override + { + throwException(); + return 0; + } + virtual void seek(qpdf_offset_t offset, int whence) override + { + throwException(); + } + virtual void rewind() override + { + throwException(); + } + virtual size_t read(char* buffer, size_t length) override + { + throwException(); + return 0; + } + virtual void unreadCh(char ch) override + { + throwException(); + } + + private: + void throwException() + { + throw std::runtime_error( + "QPDF operation attempted after closing input source"); + } +}; + QPDF::ForeignStreamData::ForeignStreamData( PointerHolder encp, PointerHolder file, @@ -254,7 +299,7 @@ QPDF::processInputSource(PointerHolder source, void QPDF::closeInputSource() { - this->m->file = 0; + this->m->file = new InvalidInputSource(); } void diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 83cbacd3..2843adab 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -1215,7 +1215,13 @@ $td->runtest("check output", show_ntests(); # ---------- $td->notify("--- Invalid objects ---"); -$n_tests += 2; +$n_tests += 3; + +$td->runtest("closed input source", + {$td->COMMAND => "test_driver 73 minimal.pdf"}, + {$td->FILE => "test73.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); $td->runtest("empty object", {$td->COMMAND => "qpdf -show-object=7,0 empty-object.pdf"}, diff --git a/qpdf/qtest/qpdf/test73.out b/qpdf/qtest/qpdf/test73.out new file mode 100644 index 00000000..1b7ad0e6 --- /dev/null +++ b/qpdf/qtest/qpdf/test73.out @@ -0,0 +1,2 @@ +WARNING: closed input source: object 2/0: error reading object: QPDF operation attempted after closing input source +test 73 done diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index aef46d87..1be7fbaf 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -2300,6 +2300,11 @@ void runtest(int n, char const* filename1, char const* arg2) buf->getSize()); assert(s.find("/bye") != std::string::npos); } + else if (n == 73) + { + pdf.closeInputSource(); + pdf.getRoot().getKey("/Pages").unparseResolved(); + } else { throw std::runtime_error(std::string("invalid test ") +