From d0e99f195a987c483bbb6c5449cf39bee34e08a1 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Fri, 16 Feb 2018 17:25:27 -0500 Subject: More robust handling of type errors Give objects descriptions and context so it is possible to issue warnings instead of fatal errors for attempts to access objects of the wrong type. --- libqpdf/QPDFObjectHandle.cc | 484 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 404 insertions(+), 80 deletions(-) (limited to 'libqpdf/QPDFObjectHandle.cc') diff --git a/libqpdf/QPDFObjectHandle.cc b/libqpdf/QPDFObjectHandle.cc index d48461bf..2e9cc996 100644 --- a/libqpdf/QPDFObjectHandle.cc +++ b/libqpdf/QPDFObjectHandle.cc @@ -190,6 +190,18 @@ QPDFObjectHandle::releaseResolved() } } +void +QPDFObjectHandle::setObjectDescriptionFromInput( + QPDFObjectHandle object, QPDF* context, + std::string const& description, PointerHolder input, + qpdf_offset_t offset) +{ + object.setObjectDescription( + context, + input->getName() + ", " + description + + " at offset " + QUtil::int_to_string(offset)); +} + bool QPDFObjectHandle::isInitialized() const { @@ -282,7 +294,8 @@ QPDFObjectHandle::getNumericValue() } else { - throw std::logic_error("getNumericValue called for non-numeric object"); + typeWarning("number", "returning 0"); + QTC::TC("qpdf", "QPDFObjectHandle numeric non-numeric"); } return result; } @@ -363,8 +376,16 @@ QPDFObjectHandle::isScalar() bool QPDFObjectHandle::getBoolValue() { - assertBool(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isBool()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("boolean", "returning false"); + QTC::TC("qpdf", "QPDFObjectHandle boolean returning false"); + return false; + } } // Integer accessors @@ -372,8 +393,16 @@ QPDFObjectHandle::getBoolValue() long long QPDFObjectHandle::getIntValue() { - assertInteger(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isInteger()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("integer", "returning 0"); + QTC::TC("qpdf", "QPDFObjectHandle integer returning 0"); + return 0; + } } // Real accessors @@ -381,8 +410,16 @@ QPDFObjectHandle::getIntValue() std::string QPDFObjectHandle::getRealValue() { - assertReal(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isReal()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("real", "returning 0.0"); + QTC::TC("qpdf", "QPDFObjectHandle real returning 0.0"); + return "0.0"; + } } // Name accessors @@ -390,8 +427,16 @@ QPDFObjectHandle::getRealValue() std::string QPDFObjectHandle::getName() { - assertName(); - return dynamic_cast(m->obj.getPointer())->getName(); + if (isName()) + { + return dynamic_cast(m->obj.getPointer())->getName(); + } + else + { + typeWarning("name", "returning dummy name"); + QTC::TC("qpdf", "QPDFObjectHandle name returning dummy name"); + return "/QPDFFakeName"; + } } // String accessors @@ -399,15 +444,31 @@ QPDFObjectHandle::getName() std::string QPDFObjectHandle::getStringValue() { - assertString(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isString()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("string", "returning empty string"); + QTC::TC("qpdf", "QPDFObjectHandle string returning empty string"); + return ""; + } } std::string QPDFObjectHandle::getUTF8Value() { - assertString(); - return dynamic_cast(m->obj.getPointer())->getUTF8Val(); + if (isString()) + { + return dynamic_cast(m->obj.getPointer())->getUTF8Val(); + } + else + { + typeWarning("string", "returning empty string"); + QTC::TC("qpdf", "QPDFObjectHandle string returning empty utf8"); + return ""; + } } // Operator and Inline Image accessors @@ -415,15 +476,31 @@ QPDFObjectHandle::getUTF8Value() std::string QPDFObjectHandle::getOperatorValue() { - assertOperator(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isOperator()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("operator", "returning fake value"); + QTC::TC("qpdf", "QPDFObjectHandle operator returning fake value"); + return "QPDFFAKE"; + } } std::string QPDFObjectHandle::getInlineImageValue() { - assertInlineImage(); - return dynamic_cast(m->obj.getPointer())->getVal(); + if (isInlineImage()) + { + return dynamic_cast(m->obj.getPointer())->getVal(); + } + else + { + typeWarning("inlineimage", "returning empty data"); + QTC::TC("qpdf", "QPDFObjectHandle inlineimage returning empty data"); + return ""; + } } // Array accessors @@ -431,22 +508,66 @@ QPDFObjectHandle::getInlineImageValue() int QPDFObjectHandle::getArrayNItems() { - assertArray(); - return dynamic_cast(m->obj.getPointer())->getNItems(); + if (isArray()) + { + return dynamic_cast(m->obj.getPointer())->getNItems(); + } + else + { + typeWarning("array", "treating as empty"); + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty"); + return 0; + } } QPDFObjectHandle QPDFObjectHandle::getArrayItem(int n) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->getItem(n); + QPDFObjectHandle result; + if (isArray() && (n < getArrayNItems()) && (n >= 0)) + { + result = dynamic_cast(m->obj.getPointer())->getItem(n); + } + else + { + result = newNull(); + if (isArray()) + { + objectWarning("returning null for out of bounds array access"); + QTC::TC("qpdf", "QPDFObjectHandle array bounds"); + } + else + { + typeWarning("array", "returning null"); + QTC::TC("qpdf", "QPDFObjectHandle array null for non-array"); + } + QPDF* context = 0; + std::string description; + if (this->m->obj->getDescription(context, description)) + { + result.setObjectDescription( + context, + description + + " -> null returned from invalid array access"); + } + } + return result; } std::vector QPDFObjectHandle::getArrayAsVector() { - assertArray(); - return dynamic_cast(m->obj.getPointer())->getAsVector(); + std::vector result; + if (isArray()) + { + result = dynamic_cast(m->obj.getPointer())->getAsVector(); + } + else + { + typeWarning("array", "treating as empty"); + QTC::TC("qpdf", "QPDFObjectHandle array treating as empty vector"); + } + return result; } // Array mutators @@ -454,36 +575,79 @@ QPDFObjectHandle::getArrayAsVector() void QPDFObjectHandle::setArrayItem(int n, QPDFObjectHandle const& item) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->setItem(n, item); + if (isArray()) + { + dynamic_cast(m->obj.getPointer())->setItem(n, item); + } + else + { + typeWarning("array", "ignoring attempt to set item"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring set item"); + } } void QPDFObjectHandle::setArrayFromVector(std::vector const& items) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->setFromVector(items); + if (isArray()) + { + dynamic_cast(m->obj.getPointer())->setFromVector(items); + } + else + { + typeWarning("array", "ignoring attempt to replace items"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring replace items"); + } } void QPDFObjectHandle::insertItem(int at, QPDFObjectHandle const& item) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->insertItem(at, item); + if (isArray()) + { + dynamic_cast(m->obj.getPointer())->insertItem(at, item); + } + else + { + typeWarning("array", "ignoring attempt to insert item"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring insert item"); + } } void QPDFObjectHandle::appendItem(QPDFObjectHandle const& item) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->appendItem(item); + if (isArray()) + { + dynamic_cast(m->obj.getPointer())->appendItem(item); + } + else + { + typeWarning("array", "ignoring attempt to append item"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring append item"); + } } void QPDFObjectHandle::eraseItem(int at) { - assertArray(); - return dynamic_cast(m->obj.getPointer())->eraseItem(at); + if (isArray() && (at < getArrayNItems()) && (at >= 0)) + { + dynamic_cast(m->obj.getPointer())->eraseItem(at); + } + else + { + if (isArray()) + { + objectWarning("ignoring attempt to erase out of bounds array item"); + QTC::TC("qpdf", "QPDFObjectHandle erase array bounds"); + } + else + { + typeWarning("array", "ignoring attempt to erase item"); + QTC::TC("qpdf", "QPDFObjectHandle array ignoring erase item"); + } + } } // Dictionary accessors @@ -491,29 +655,79 @@ QPDFObjectHandle::eraseItem(int at) bool QPDFObjectHandle::hasKey(std::string const& key) { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->hasKey(key); + if (isDictionary()) + { + return dynamic_cast(m->obj.getPointer())->hasKey(key); + } + else + { + typeWarning("dictionary", + "returning false for a key containment request"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary false for hasKey"); + return false; + } } QPDFObjectHandle QPDFObjectHandle::getKey(std::string const& key) { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->getKey(key); + QPDFObjectHandle result; + if (isDictionary()) + { + result = dynamic_cast( + m->obj.getPointer())->getKey(key); + } + else + { + typeWarning( + "dictionary", "returning null for attempted key retrieval"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary null for getKey"); + result = newNull(); + QPDF* qpdf = 0; + std::string description; + if (this->m->obj->getDescription(qpdf, description)) + { + result.setObjectDescription( + qpdf, + description + + " -> null returned from getting key " + + key + " from non-Dictionary"); + } + } + return result; } std::set QPDFObjectHandle::getKeys() { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->getKeys(); + std::set result; + if (isDictionary()) + { + result = dynamic_cast(m->obj.getPointer())->getKeys(); + } + else + { + typeWarning("dictionary", "treating as empty"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty set for getKeys"); + } + return result; } std::map QPDFObjectHandle::getDictAsMap() { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->getAsMap(); + std::map result; + if (isDictionary()) + { + result = dynamic_cast( + m->obj.getPointer())->getAsMap(); + } + else + { + typeWarning("dictionary", "treating as empty"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary empty map for asMap"); + } + return result; } // Array and Name accessors @@ -551,27 +765,48 @@ QPDFObjectHandle::getOwningQPDF() void QPDFObjectHandle::replaceKey(std::string const& key, - QPDFObjectHandle const& value) + QPDFObjectHandle value) { - assertDictionary(); - return dynamic_cast( - m->obj.getPointer())->replaceKey(key, value); + if (isDictionary()) + { + dynamic_cast( + m->obj.getPointer())->replaceKey(key, value); + } + else + { + typeWarning("dictionary", "ignoring key replacement request"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring replaceKey"); + } } void QPDFObjectHandle::removeKey(std::string const& key) { - assertDictionary(); - return dynamic_cast(m->obj.getPointer())->removeKey(key); + if (isDictionary()) + { + dynamic_cast(m->obj.getPointer())->removeKey(key); + } + else + { + typeWarning("dictionary", "ignoring key removal request"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removeKey"); + } } void QPDFObjectHandle::replaceOrRemoveKey(std::string const& key, QPDFObjectHandle value) { - assertDictionary(); - return dynamic_cast( - m->obj.getPointer())->replaceOrRemoveKey(key, value); + if (isDictionary()) + { + dynamic_cast( + m->obj.getPointer())->replaceOrRemoveKey(key, value); + } + else + { + typeWarning("dictionary", "ignoring key removal/replacement request"); + QTC::TC("qpdf", "QPDFObjectHandle dictionary ignoring removereplace"); + } } // Stream accessors @@ -1173,35 +1408,45 @@ QPDFObjectHandle::parseInternal(PointerHolder input, std::vector state_stack; state_stack.push_back(st_top); std::vector offset_stack; - offset_stack.push_back(input->tell()); + qpdf_offset_t offset = input->tell(); + offset_stack.push_back(offset); bool done = false; while (! done) { std::vector& olist = olist_stack.back(); parser_state_e state = state_stack.back(); - qpdf_offset_t offset = offset_stack.back(); + offset = offset_stack.back(); object = QPDFObjectHandle(); QPDFTokenizer::Token token = - tokenizer.readToken(input, object_description); + tokenizer.readToken(input, object_description, true); switch (token.getType()) { case QPDFTokenizer::tt_eof: - if (content_stream) + if (! content_stream) { - state = st_eof; - } - else - { - // When not in content stream mode, EOF is tt_bad and - // throws an exception before we get here. - throw std::logic_error( - "EOF received while not in content stream mode"); + QTC::TC("qpdf", "QPDFObjectHandle eof in parseInternal"); + warn(context, + QPDFExc(qpdf_e_damaged_pdf, input->getName(), + object_description, + input->getLastOffset(), + "unexpected EOF")); } + state = st_eof; break; + case QPDFTokenizer::tt_bad: + QTC::TC("qpdf", "QPDFObjectHandle bad token in parse"); + warn(context, + QPDFExc(qpdf_e_damaged_pdf, input->getName(), + object_description, + input->getLastOffset(), + token.getErrorMessage())); + object = newNull(); + break; + case QPDFTokenizer::tt_brace_open: case QPDFTokenizer::tt_brace_close: QTC::TC("qpdf", "QPDFObjectHandle bad brace"); @@ -1375,11 +1620,19 @@ QPDFObjectHandle::parseInternal(PointerHolder input, "parse error while reading object")); } done = true; - // Leave object uninitialized to indicate EOF + // In content stream mode, leave object uninitialized to + // indicate EOF + if (! content_stream) + { + object = newNull(); + } break; case st_dictionary: case st_array: + setObjectDescriptionFromInput( + object, context, object_description, input, + input->getLastOffset()); olist.push_back(object); break; @@ -1402,6 +1655,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, if (old_state == st_array) { object = newArray(olist); + setObjectDescriptionFromInput( + object, context, object_description, input, offset); } else if (old_state == st_dictionary) { @@ -1458,6 +1713,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, "dictionary ended prematurely; " "using null as value for last key")); val = newNull(); + setObjectDescriptionFromInput( + val, context, object_description, input, offset); } else { @@ -1466,6 +1723,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, dict[key_obj.getName()] = val; } object = newDictionary(dict); + setObjectDescriptionFromInput( + object, context, object_description, input, offset); } olist_stack.pop_back(); offset_stack.pop_back(); @@ -1480,6 +1739,8 @@ QPDFObjectHandle::parseInternal(PointerHolder input, } } + setObjectDescriptionFromInput( + object, context, object_description, input, offset); return object; } @@ -1635,6 +1896,26 @@ QPDFObjectHandle::newReserved(QPDF* qpdf) return result; } +void +QPDFObjectHandle::setObjectDescription(QPDF* owning_qpdf, + std::string const& object_description) +{ + if (isInitialized() && this->m->obj.getPointer()) + { + this->m->obj->setDescription(owning_qpdf, object_description); + } +} + +bool +QPDFObjectHandle::hasObjectDescription() +{ + if (isInitialized() && this->m->obj.getPointer()) + { + return this->m->obj->hasDescription(); + } + return false; +} + QPDFObjectHandle QPDFObjectHandle::shallowCopy() { @@ -1793,85 +2074,127 @@ QPDFObjectHandle::assertInitialized() const } void -QPDFObjectHandle::assertType(char const* type_name, bool istype) const +QPDFObjectHandle::typeWarning(char const* expected_type, + std::string const& warning) +{ + QPDF* context = 0; + std::string description; + if (this->m->obj->getDescription(context, description)) + { + warn(context, + QPDFExc( + qpdf_e_damaged_pdf, + "", description, 0, + std::string("operation for ") + expected_type + + " attempted on object of type " + + getTypeName() + ": " + warning)); + } + else + { + assertType(expected_type, false); + } +} + +void +QPDFObjectHandle::objectWarning(std::string const& warning) +{ + QPDF* context = 0; + std::string description; + if (this->m->obj->getDescription(context, description)) + { + warn(context, + QPDFExc( + qpdf_e_damaged_pdf, + "", description, 0, + warning)); + } + else + { + throw std::logic_error(warning); + } +} + +void +QPDFObjectHandle::assertType(char const* type_name, bool istype) { if (! istype) { throw std::logic_error(std::string("operation for ") + type_name + - " object attempted on object of wrong type"); + " attempted on object of type " + + getTypeName()); } } void QPDFObjectHandle::assertNull() { - assertType("Null", isNull()); + assertType("null", isNull()); } void QPDFObjectHandle::assertBool() { - assertType("Boolean", isBool()); + assertType("boolean", isBool()); } void QPDFObjectHandle::assertInteger() { - assertType("Integer", isInteger()); + assertType("integer", isInteger()); } void QPDFObjectHandle::assertReal() { - assertType("Real", isReal()); + assertType("real", isReal()); } void QPDFObjectHandle::assertName() { - assertType("Name", isName()); + assertType("name", isName()); } void QPDFObjectHandle::assertString() { - assertType("String", isString()); + assertType("string", isString()); } void QPDFObjectHandle::assertOperator() { - assertType("Operator", isOperator()); + assertType("operator", isOperator()); } void QPDFObjectHandle::assertInlineImage() { - assertType("InlineImage", isInlineImage()); + assertType("inlineimage", isInlineImage()); } void QPDFObjectHandle::assertArray() { - assertType("Array", isArray()); + assertType("array", isArray()); } void QPDFObjectHandle::assertDictionary() { - assertType("Dictionary", isDictionary()); + assertType("dictionary", isDictionary()); } void QPDFObjectHandle::assertStream() { - assertType("Stream", isStream()); + assertType("stream", isStream()); } void QPDFObjectHandle::assertReserved() { - assertType("Reserved", isReserved()); + assertType("reserved", isReserved()); } void @@ -1887,13 +2210,13 @@ QPDFObjectHandle::assertIndirect() void QPDFObjectHandle::assertScalar() { - assertType("Scalar", isScalar()); + assertType("scalar", isScalar()); } void QPDFObjectHandle::assertNumber() { - assertType("Number", isNumber()); + assertType("number", isNumber()); } bool @@ -1928,7 +2251,8 @@ QPDFObjectHandle::dereference() this->m->qpdf, this->m->objid, this->m->generation); if (obj.getPointer() == 0) { - QTC::TC("qpdf", "QPDFObjectHandle indirect to unknown"); + // QPDF::resolve never returns an uninitialized object, but + // check just in case. this->m->obj = new QPDF_Null(); } else if (dynamic_cast(obj.getPointer())) -- cgit v1.2.3-54-g00ecf