aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO-pages.md4
-rw-r--r--fuzz/qtest/fuzz.test2
-rw-r--r--fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029bin0 -> 1048576 bytes
-rw-r--r--libqpdf/JSON.cc95
-rw-r--r--libqpdf/QPDF.cc4
-rw-r--r--libtests/json.cc47
-rw-r--r--qpdf/qpdf.testcov1
-rw-r--r--qpdf/qtest/qpdf/obj0-check.out1
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
new file mode 100644
index 00000000..6b1ffd1e
--- /dev/null
+++ b/fuzz/tiffpredictor_fuzzer_seed_corpus/2b124d759b85547cfec13a8e9a9fee44be041029
Binary files differ
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