diff --git a/fuzz/qpdf_extra/15442.fuzz b/fuzz/qpdf_extra/15442.fuzz new file mode 100644 index 00000000..4c3b9b20 --- /dev/null +++ b/fuzz/qpdf_extra/15442.fuzz @@ -0,0 +1 @@ +trailer<>stream \ No newline at end of file diff --git a/include/qpdf/BufferInputSource.hh b/include/qpdf/BufferInputSource.hh index 90263335..b11189eb 100644 --- a/include/qpdf/BufferInputSource.hh +++ b/include/qpdf/BufferInputSource.hh @@ -54,7 +54,7 @@ class BufferInputSource: public InputSource virtual void unreadCh(char ch); private: - qpdf_offset_t const bufSizeAsOffset() const; + static void range_check(qpdf_offset_t cur, qpdf_offset_t delta); class Members { @@ -72,6 +72,7 @@ class BufferInputSource: public InputSource std::string description; Buffer* buf; qpdf_offset_t cur_offset; + qpdf_offset_t max_offset; }; PointerHolder m; diff --git a/libqpdf/BufferInputSource.cc b/libqpdf/BufferInputSource.cc index 23bad5f0..cbb2e7f0 100644 --- a/libqpdf/BufferInputSource.cc +++ b/libqpdf/BufferInputSource.cc @@ -3,6 +3,8 @@ #include #include #include +#include +#include BufferInputSource::Members::Members(bool own_memory, std::string const& description, @@ -10,7 +12,8 @@ BufferInputSource::Members::Members(bool own_memory, own_memory(own_memory), description(description), buf(buf), - cur_offset(0) + cur_offset(0), + max_offset(buf ? QIntC::to_offset(buf->getSize()) : 0) { } @@ -29,6 +32,7 @@ BufferInputSource::BufferInputSource(std::string const& description, m(new Members(true, description, 0)) { this->m->buf = new Buffer(contents.length()); + this->m->max_offset = QIntC::to_offset(this->m->buf->getSize()); unsigned char* bp = this->m->buf->getBuffer(); memcpy(bp, contents.c_str(), contents.length()); } @@ -41,12 +45,6 @@ BufferInputSource::~BufferInputSource() } } -qpdf_offset_t const -BufferInputSource::bufSizeAsOffset() const -{ - return QIntC::to_offset(this->m->buf->getSize()); -} - qpdf_offset_t BufferInputSource::findAndSkipNextEOL() { @@ -54,7 +52,7 @@ BufferInputSource::findAndSkipNextEOL() { throw std::logic_error("INTERNAL ERROR: BufferInputSource offset < 0"); } - qpdf_offset_t end_pos = bufSizeAsOffset(); + qpdf_offset_t end_pos = this->m->max_offset; if (this->m->cur_offset >= end_pos) { this->last_offset = end_pos; @@ -102,6 +100,20 @@ BufferInputSource::tell() return this->m->cur_offset; } +void +BufferInputSource::range_check(qpdf_offset_t cur, qpdf_offset_t delta) +{ + if ((delta > 0) && + ((std::numeric_limits::max() - cur) < delta)) + { + std::ostringstream msg; + msg << "seeking forward from " << cur + << " by " << delta + << " would cause an overflow of the offset type"; + throw std::range_error(msg.str()); + } +} + void BufferInputSource::seek(qpdf_offset_t offset, int whence) { @@ -112,10 +124,12 @@ BufferInputSource::seek(qpdf_offset_t offset, int whence) break; case SEEK_END: - this->m->cur_offset = bufSizeAsOffset() + offset; + range_check(this->m->max_offset, offset); + this->m->cur_offset = this->m->max_offset + offset; break; case SEEK_CUR: + range_check(this->m->cur_offset, offset); this->m->cur_offset += offset; break; @@ -145,7 +159,7 @@ BufferInputSource::read(char* buffer, size_t length) { throw std::logic_error("INTERNAL ERROR: BufferInputSource offset < 0"); } - qpdf_offset_t end_pos = bufSizeAsOffset(); + qpdf_offset_t end_pos = this->m->max_offset; if (this->m->cur_offset >= end_pos) { this->last_offset = end_pos;