diff options
-rw-r--r-- | TODO-pages.md | 4 | ||||
-rw-r--r-- | fuzz/qtest/fuzz.test | 2 | ||||
-rw-r--r-- | fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029 | bin | 0 -> 1048576 bytes | |||
-rw-r--r-- | libqpdf/JSON.cc | 95 | ||||
-rw-r--r-- | libqpdf/QPDF.cc | 4 | ||||
-rw-r--r-- | libtests/json.cc | 47 | ||||
-rw-r--r-- | qpdf/qpdf.testcov | 1 | ||||
-rw-r--r-- | qpdf/qtest/qpdf/obj0-check.out | 1 |
8 files changed, 104 insertions, 50 deletions
diff --git a/TODO-pages.md b/TODO-pages.md index b99c6f84..83de0fff 100644 --- a/TODO-pages.md +++ b/TODO-pages.md @@ -447,7 +447,7 @@ Most of chapter 12 applies. See Document-level navigation (12.3). # Feature to Issue Mapping -Last checked: 2024-01-11 +Last checked: 2024-01-18 ``` gh search issues label:pages --repo qpdf/qpdf --limit 200 --state=open @@ -546,6 +546,8 @@ gh search issues label:pages --repo qpdf/qpdf --limit 200 --state=open * There is some helpful discussion in #343 including * Preserving open/closed status * Preserving javascript actions +* Split pages: write pages to memory + * Issues: #1130 # Other use cases diff --git a/fuzz/qtest/fuzz.test b/fuzz/qtest/fuzz.test index 7ca371fd..a15e6281 100644 --- a/fuzz/qtest/fuzz.test +++ b/fuzz/qtest/fuzz.test @@ -19,7 +19,7 @@ my @fuzzers = ( ['lzw' => 2], ['pngpredictor' => 1], ['runlength' => 6], - ['tiffpredictor' => 1], + ['tiffpredictor' => 2], ['qpdf' => 56], # increment when adding new files ); diff --git a/fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029 b/fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029 Binary files differnew file mode 100644 index 00000000..6b1ffd1e --- /dev/null +++ b/fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029 diff --git a/libqpdf/JSON.cc b/libqpdf/JSON.cc index 35b8fd76..c9816809 100644 --- a/libqpdf/JSON.cc +++ b/libqpdf/JSON.cc @@ -191,7 +191,7 @@ JSON::JSON_blob::write(Pipeline* p, size_t) const void JSON::write(Pipeline* p, size_t depth) const { - if (nullptr == m->value) { + if (!m) { *p << "null"; } else { m->value->write(p, depth); @@ -201,6 +201,9 @@ JSON::write(Pipeline* p, size_t depth) const std::string JSON::unparse() const { + if (!m) { + return "null"; + } std::string s; Pl_String p("unparse", nullptr, s); write(&p, 0); @@ -275,8 +278,8 @@ JSON::makeDictionary() JSON JSON::addDictionaryMember(std::string const& key, JSON const& val) { - if (auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get())) { - return obj->members[encode_string(key)] = val.m->value ? val : makeNull(); + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { + return obj->members[encode_string(key)] = val.m ? val : makeNull(); } else { throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary"); } @@ -285,15 +288,11 @@ JSON::addDictionaryMember(std::string const& key, JSON const& val) bool JSON::checkDictionaryKeySeen(std::string const& key) { - auto* obj = dynamic_cast<JSON_dictionary*>(m->value.get()); - if (nullptr == obj) { - throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary"); - } - if (obj->parsed_keys.count(key)) { - return true; + if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) { + return !obj->parsed_keys.insert(key).second; } - obj->parsed_keys.insert(key); - return false; + throw std::logic_error("JSON::checkDictionaryKey called on non-dictionary"); + return false; // unreachable } JSON @@ -305,16 +304,16 @@ JSON::makeArray() JSON JSON::addArrayElement(JSON const& val) { - auto* arr = dynamic_cast<JSON_array*>(m->value.get()); - if (nullptr == arr) { - throw std::runtime_error("JSON::addArrayElement called on non-array"); - } - if (val.m->value.get()) { - arr->elements.push_back(val); - } else { - arr->elements.push_back(makeNull()); + if (auto* arr = m ? dynamic_cast<JSON_array*>(m->value.get()) : nullptr) { + if (val.m) { + arr->elements.push_back(val); + } else { + arr->elements.push_back(makeNull()); + } + return arr->elements.back(); } - return arr->elements.back(); + throw std::runtime_error("JSON::addArrayElement called on non-array"); + return {}; // unreachable } JSON @@ -362,19 +361,19 @@ JSON::makeBlob(std::function<void(Pipeline*)> fn) bool JSON::isArray() const { - return m->value->type_code == vt_array; + return m ? m->value->type_code == vt_array : false; } bool JSON::isDictionary() const { - return m->value->type_code == vt_dictionary; + return m && m->value->type_code == vt_dictionary; } bool JSON::getString(std::string& utf8) const { - if (m->value->type_code == vt_string) { + if (m && m->value->type_code == vt_string) { auto v = dynamic_cast<JSON_string const*>(m->value.get()); utf8 = v->utf8; return true; @@ -385,7 +384,7 @@ JSON::getString(std::string& utf8) const bool JSON::getNumber(std::string& value) const { - if (m->value->type_code == vt_number) { + if (m && m->value->type_code == vt_number) { auto v = dynamic_cast<JSON_number const*>(m->value.get()); value = v->encoded; return true; @@ -396,7 +395,7 @@ JSON::getNumber(std::string& value) const bool JSON::getBool(bool& value) const { - if (m->value->type_code == vt_bool) { + if (m && m->value->type_code == vt_bool) { auto v = dynamic_cast<JSON_bool const*>(m->value.get()); value = v->value; return true; @@ -407,13 +406,13 @@ JSON::getBool(bool& value) const bool JSON::isNull() const { - return m->value->type_code == vt_null; + return m && m->value->type_code == vt_null; } JSON JSON::getDictItem(std::string const& key) const { - if (auto v = dynamic_cast<JSON_dictionary const*>(m->value.get())) { + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) { if (auto it = v->members.find(key); it != v->members.end()) { return it->second; } @@ -424,39 +423,37 @@ JSON::getDictItem(std::string const& key) const bool JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const { - auto v = dynamic_cast<JSON_dictionary const*>(m->value.get()); - if (v == nullptr) { - return false; - } - for (auto const& k: v->members) { - fn(k.first, JSON(k.second)); + if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) { + for (auto const& [key, value]: v->members) { + fn(key, value); + } + return true; } - return true; + return false; } bool JSON::forEachArrayItem(std::function<void(JSON value)> fn) const { - auto v = dynamic_cast<JSON_array const*>(m->value.get()); - if (v == nullptr) { - return false; - } - for (auto const& i: v->elements) { - fn(JSON(i)); + if (auto v = m ? dynamic_cast<JSON_array const*>(m->value.get()) : nullptr) { + for (auto const& i: v->elements) { + fn(JSON(i)); + } + return true; } - return true; + return false; } bool JSON::checkSchema(JSON schema, std::list<std::string>& errors) { - return checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, ""); + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, ""); } bool JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors) { - return checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, ""); + return m && checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, ""); } bool @@ -1374,23 +1371,27 @@ JSON::parse(std::string const& s) void JSON::setStart(qpdf_offset_t start) { - m->start = start; + if (m) { + m->start = start; + } } void JSON::setEnd(qpdf_offset_t end) { - m->end = end; + if (m) { + m->end = end; + } } qpdf_offset_t JSON::getStart() const { - return m->start; + return m ? m->start : 0; } qpdf_offset_t JSON::getEnd() const { - return m->end; + return m ? m->end : 0; } diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index 8cff3dfd..01158ce2 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -1195,6 +1195,10 @@ QPDF::insertFreeXrefEntry(QPDFObjGen og) void QPDF::insertReconstructedXrefEntry(int obj, qpdf_offset_t f1, int f2) { + if (!(obj > 0 && 0 <= f2 && f2 < 65535)) { + QTC::TC("qpdf", "QPDF xref overwrite invalid objgen"); + return; + } QPDFObjGen og(obj, f2); if (!m->deleted_objects.count(obj)) { // deleted_objects stores the uncompressed objects removed from the xref table at the start diff --git a/libtests/json.cc b/libtests/json.cc index f265f6f6..67c8534f 100644 --- a/libtests/json.cc +++ b/libtests/json.cc @@ -2,8 +2,10 @@ #include <qpdf/JSON.hh> #include <qpdf/Pipeline.hh> +#include <qpdf/Pl_String.hh> #include <qpdf/QPDF.hh> #include <qpdf/QPDFObjectHandle.hh> + #include <iostream> static void @@ -131,6 +133,51 @@ test_main() " \"blob\": \"AQIDBAX//v38+w==\",\n" " \"normal\": \"string\"\n" "}"); + + // Check default constructed JSON object (order as per JSON.hh). + JSON uninitialized; + std::string ws; + auto pl = Pl_String ("", nullptr, ws); + uninitialized.write(&pl); + assert(ws == "null"); + assert(uninitialized.unparse() == "null"); + try { + uninitialized.addDictionaryMember("key", jarr); + assert(false); + } catch (std::runtime_error&) { + } + assert(jmap.addDictionaryMember("42", uninitialized).isNull()); + try { + uninitialized.addArrayElement(jarr); + assert(false); + } catch (std::runtime_error&) { + } + assert(jarr.addArrayElement(uninitialized).isNull()); + assert(!uninitialized.isArray()); + assert(!uninitialized.isDictionary()); + try { + uninitialized.checkDictionaryKeySeen("key"); + assert(false); + } catch (std::logic_error&) { + } + std::string st_out = "unchanged"; + assert(!uninitialized.getString(st_out)); + assert(!uninitialized.getNumber(st_out)); + bool b_out = true; + assert(!uninitialized.getBool(b_out)); + assert(b_out && st_out == "unchanged"); + assert(!uninitialized.isNull()); + assert(uninitialized.getDictItem("42").isNull()); + assert(!uninitialized.forEachDictItem([](auto k, auto v) {})); + assert(!uninitialized.forEachArrayItem([](auto v) {})); + std::list<std::string> e; + assert(!uninitialized.checkSchema(JSON(), 0, e)); + assert(!uninitialized.checkSchema(JSON(), e)); + assert(e.empty()); + uninitialized.setStart(0); + uninitialized.setEnd(0); + assert(uninitialized.getStart() == 0); + assert(uninitialized.getEnd() == 0); } static void diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index e7b6a8a2..51c3ea72 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -105,6 +105,7 @@ QPDF_encryption xref stream from encrypted file 0 QPDFJob unable to filter 0 QUtil non-trivial UTF-16 0 QPDF xref overwrite object 0 +QPDF xref overwrite invalid objgen 0 QPDF decoding error warning 0 qpdf-c called qpdf_init 0 qpdf-c called qpdf_cleanup 0 diff --git a/qpdf/qtest/qpdf/obj0-check.out b/qpdf/qtest/qpdf/obj0-check.out index 7a17e8a7..785131d4 100644 --- a/qpdf/qtest/qpdf/obj0-check.out +++ b/qpdf/qtest/qpdf/obj0-check.out @@ -5,5 +5,4 @@ checking obj0.pdf PDF Version: 1.3 File is not encrypted File is not linearized -WARNING: obj0.pdf (offset 15): object with ID 0 qpdf: operation succeeded with warnings |