aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2019-06-21 23:37:33 +0200
committerJay Berkenbilt <ejb@ql.org>2019-06-21 23:56:24 +0200
commitb07ad6794eea175ee7f4b8c505995bdb4f397ce6 (patch)
tree43ca23b83e514e7f1f2c362cfc1b6ca21c21593e
parenta35d4ce9ccb3eb5903df3d221fdfd9a0d1fb5c37 (diff)
downloadqpdf-b07ad6794eea175ee7f4b8c505995bdb4f397ce6.tar.zst
Fix bugs found by fuzz tests
* Several assertions in linearization were not always true; change them to run time errors * Handle a few cases of uninitialized objects * Handle pages with no contents when doing form operations * Handle invalid page tree nodes when traversing pages
-rw-r--r--TODO5
-rw-r--r--libqpdf/QPDFFormFieldObjectHelper.cc7
-rw-r--r--libqpdf/QPDFObjectHandle.cc6
-rw-r--r--libqpdf/QPDFWriter.cc34
-rw-r--r--libqpdf/QPDF_linearization.cc28
-rw-r--r--libqpdf/QPDF_optimization.cc8
-rw-r--r--qpdf/qtest/qpdf/no-contents-coalesce-contents.pdf14
7 files changed, 67 insertions, 35 deletions
diff --git a/TODO b/TODO
index 7309ddc6..3c4997e8 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,3 @@
-OSS-Fuzz
-========
-
-* Fix open issues from https://oss-fuzz.com
-
Next ABI
========
diff --git a/libqpdf/QPDFFormFieldObjectHelper.cc b/libqpdf/QPDFFormFieldObjectHelper.cc
index b3c61ce5..97257c85 100644
--- a/libqpdf/QPDFFormFieldObjectHelper.cc
+++ b/libqpdf/QPDFFormFieldObjectHelper.cc
@@ -43,6 +43,10 @@ QPDFObjectHandle
QPDFFormFieldObjectHelper::getInheritableFieldValue(std::string const& name)
{
QPDFObjectHandle node = this->oh;
+ if (! node.isDictionary())
+ {
+ return QPDFObjectHandle::newNull();
+ }
QPDFObjectHandle result(node.getKey(name));
std::set<QPDFObjGen> seen;
while (result.isNull() && node.hasKey("/Parent"))
@@ -896,7 +900,8 @@ QPDFFormFieldObjectHelper::generateTextAppearance(
QPDFObjectHandle dr = getInheritableFieldValue("/DR");
font = getFontFromResource(dr, font_name);
}
- if (font.isDictionary() &&
+ if (font.isInitialized() &&
+ font.isDictionary() &&
font.getKey("/Encoding").isName())
{
std::string encoding = font.getKey("/Encoding").getName();
diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc
index 9ccfa37a..a3147940 100644
--- a/libqpdf/QPDFObjectHandle.cc
+++ b/libqpdf/QPDFObjectHandle.cc
@@ -1407,6 +1407,12 @@ QPDFObjectHandle::coalesceContentStreams()
QTC::TC("qpdf", "QPDFObjectHandle coalesce called on stream");
return;
}
+ else if (! contents.isArray())
+ {
+ // /Contents is optional for pages, and some very damaged
+ // files may have pages that are invalid in other ways.
+ return;
+ }
QPDF* qpdf = getOwningQPDF();
if (qpdf == 0)
{
diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc
index 23a67d7c..93a634f2 100644
--- a/libqpdf/QPDFWriter.cc
+++ b/libqpdf/QPDFWriter.cc
@@ -851,9 +851,11 @@ QPDFWriter::parseVersion(std::string const& version,
QUtil::int_to_string(minor);
if (tmp != version)
{
- throw std::logic_error(
- "INTERNAL ERROR: QPDFWriter::parseVersion"
- " called with invalid version number " + version);
+ // The version number in the input is probably invalid. This
+ // happens with some files that are designed to exercise bugs,
+ // such as files in the fuzzer corpus. Unfortunately
+ // QPDFWriter doesn't have a way to give a warning, so we just
+ // ignore this case.
}
}
@@ -3013,7 +3015,7 @@ QPDFWriter::discardGeneration(std::map<QPDFObjGen, int> const& in,
{
if (out.count((*iter).first.getObj()))
{
- throw std::logic_error(
+ throw std::runtime_error(
"QPDF cannot currently linearize files that contain"
" multiple objects with the same object ID and different"
" generations. If you see this error message, please file"
@@ -3130,15 +3132,33 @@ QPDFWriter::writeLinearized()
this->m->next_objid = part4_first_obj;
enqueuePart(part4);
- assert(this->m->next_objid == after_part4);
+ if (this->m->next_objid != after_part4)
+ {
+ // This can happen with very botched files as in the fuzzer
+ // test. There are likely some faulty assumptions in
+ // calculateLinearizationData
+ throw std::runtime_error(
+ "error encountered after"
+ " writing part 4 of linearized data");
+ }
this->m->next_objid = part6_first_obj;
enqueuePart(part6);
- assert(this->m->next_objid == after_part6);
+ if (this->m->next_objid != after_part6)
+ {
+ throw std::runtime_error(
+ "error encountered after"
+ " writing part 6 of linearized data");
+ }
this->m->next_objid = second_half_first_obj;
enqueuePart(part7);
enqueuePart(part8);
enqueuePart(part9);
- assert(this->m->next_objid == after_second_half);
+ if (this->m->next_objid != after_second_half)
+ {
+ throw std::runtime_error(
+ "error encountered after"
+ " writing part 9 of linearized data");
+ }
qpdf_offset_t hint_length = 0;
PointerHolder<Buffer> hint_buffer;
diff --git a/libqpdf/QPDF_linearization.cc b/libqpdf/QPDF_linearization.cc
index 04b646d3..5eb6277e 100644
--- a/libqpdf/QPDF_linearization.cc
+++ b/libqpdf/QPDF_linearization.cc
@@ -612,7 +612,7 @@ QPDF::checkLinearizationInternal()
if (this->m->part6.empty())
{
- throw std::logic_error("linearization part 6 unexpectedly empty");
+ stopOnError("linearization part 6 unexpectedly empty");
}
qpdf_offset_t min_E = -1;
qpdf_offset_t max_E = -1;
@@ -714,7 +714,7 @@ QPDF::getLinearizationOffset(QPDFObjGen const& og)
break;
default:
- throw std::logic_error(
+ stopOnError(
"getLinearizationOffset called for xref entry not of type 1 or 2");
break;
}
@@ -862,7 +862,7 @@ QPDF::checkHPageOffset(std::list<std::string>& errors,
int idx = he.shared_identifiers.at(i);
if (shared_idx_to_obj.count(idx) == 0)
{
- throw std::logic_error(
+ stopOnError(
"unable to get object for item in"
" shared objects hint table");
}
@@ -874,7 +874,7 @@ QPDF::checkHPageOffset(std::list<std::string>& errors,
int idx = ce.shared_identifiers.at(i);
if (idx >= this->m->c_shared_object_data.nshared_total)
{
- throw std::logic_error(
+ stopOnError(
"index out of bounds for shared object hint table");
}
int obj = this->m->c_shared_object_data.entries.at(toS(idx)).object;
@@ -1444,7 +1444,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
break;
case ObjUser::ou_bad:
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR: QPDF::calculateLinearizationData: "
"invalid user type");
break;
@@ -1558,10 +1558,14 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
// will do the same.
// First, place the actual first page object itself.
+ if (pages.empty())
+ {
+ stopOnError("no pages found while calculating linearization data");
+ }
QPDFObjGen first_page_og(pages.at(0).getObjGen());
if (! lc_first_page_private.count(first_page_og))
{
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR: QPDF::calculateLinearizationData: first page "
"object not in lc_first_page_private");
}
@@ -1611,7 +1615,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
QPDFObjGen page_og(pages.at(i).getObjGen());
if (! lc_other_page_private.count(page_og))
{
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR: "
"QPDF::calculateLinearizationData: page object for page " +
QUtil::uint_to_string(i) + " not in lc_other_page_private");
@@ -1646,7 +1650,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
// That should have covered all part7 objects.
if (! lc_other_page_private.empty())
{
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR:"
" QPDF::calculateLinearizationData: lc_other_page_private is "
"not empty after generation of part7");
@@ -1731,7 +1735,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
}
if (! lc_thumbnail_private.empty())
{
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR: "
"QPDF::calculateLinearizationData: lc_thumbnail_private "
"not empty after placing thumbnails");
@@ -1765,7 +1769,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
size_t num_wanted = this->m->object_to_obj_users.size();
if (num_placed != num_wanted)
{
- throw std::logic_error(
+ stopOnError(
"INTERNAL ERROR: QPDF::calculateLinearizationData: wrong "
"number of objects placed (num_placed = " +
QUtil::uint_to_string(num_placed) +
@@ -1820,7 +1824,7 @@ QPDF::calculateLinearizationData(std::map<int, int> const& object_stream_data)
if (static_cast<size_t>(this->m->c_shared_object_data.nshared_total) !=
this->m->c_shared_object_data.entries.size())
{
- throw std::logic_error(
+ stopOnError(
"shared object hint table has wrong number of entries");
}
@@ -2058,7 +2062,7 @@ QPDF::calculateHSharedObject(
}
if (soe.size() != static_cast<size_t>(cso.nshared_total))
{
- throw std::logic_error("soe has wrong size after initialization");
+ stopOnError("soe has wrong size after initialization");
}
so.nshared_total = cso.nshared_total;
diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc
index 3394836c..afa7ccbd 100644
--- a/libqpdf/QPDF_optimization.cc
+++ b/libqpdf/QPDF_optimization.cc
@@ -195,6 +195,14 @@ QPDF::pushInheritedAttributesToPageInternal(
}
visited.insert(this_og);
+ if (! cur_pages.isDictionary())
+ {
+ throw QPDFExc(qpdf_e_damaged_pdf, this->m->file->getName(),
+ this->m->last_object_description,
+ this->m->file->getLastOffset(),
+ "invalid object in page tree");
+ }
+
// Extract the underlying dictionary object
std::string type = cur_pages.getKey("/Type").getName();
diff --git a/qpdf/qtest/qpdf/no-contents-coalesce-contents.pdf b/qpdf/qtest/qpdf/no-contents-coalesce-contents.pdf
index c77ad5c9..28f6b3b1 100644
--- a/qpdf/qtest/qpdf/no-contents-coalesce-contents.pdf
+++ b/qpdf/qtest/qpdf/no-contents-coalesce-contents.pdf
@@ -7,21 +7,15 @@ endobj
<< /Count 1 /Kids [ 3 0 R ] /Type /Pages >>
endobj
3 0 obj
-<< /Contents 4 0 R /MediaBox [ 0 0 720 720 ] /Parent 2 0 R /Resources << >> /Type /Page >>
-endobj
-4 0 obj
-<< /Length 0 /Filter /FlateDecode >>
-stream
-endstream
+<< /MediaBox [ 0 0 720 720 ] /Parent 2 0 R /Resources << >> /Type /Page >>
endobj
xref
-0 5
+0 4
0000000000 65535 f
0000000015 00000 n
0000000064 00000 n
0000000123 00000 n
-0000000229 00000 n
-trailer << /Root 1 0 R /Size 5 /ID [<52bba3c78160d0c6e851b59110e5d076><31415926535897932384626433832795>] >>
+trailer << /Root 1 0 R /Size 4 /ID [<52bba3c78160d0c6e851b59110e5d076><31415926535897932384626433832795>] >>
startxref
-298
+213
%%EOF