Implement pdf_nametree_fuzzer.
Improve coverage for cpdf_nametree.cpp code that implements
functionality used by APIs in public/fpdf_attachment.h. Fix an assertion
failure that the fuzzer easily found.
Change-Id: Icc6521fe630aaf2fca7af5a2ba492205f455d135
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/61470
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Henrique Nakashima <hnakashima@chromium.org>
diff --git a/core/fpdfdoc/cpdf_nametree.cpp b/core/fpdfdoc/cpdf_nametree.cpp
index ff0fbbe..fd3f592 100644
--- a/core/fpdfdoc/cpdf_nametree.cpp
+++ b/core/fpdfdoc/cpdf_nametree.cpp
@@ -352,7 +352,9 @@
SearchNameNodeByIndex(m_pRoot.Get(), 0, 0, &nCurIndex, &csName, &pFind,
nullptr);
}
- ASSERT(pFind);
+ // Give up if that fails too.
+ if (!pFind)
+ return false;
// Insert the name and the object into the leaf array found. Note that the
// insertion position is right after the key-value pair returned by |index|.
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index f82c395..dccf6b6 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -66,6 +66,10 @@
}
}
}
+if (is_clang) {
+ # Fuzzers that use FuzzedDataProvider can only be built with Clang.
+ fuzzer_list += [ "pdf_nametree_fuzzer" ]
+}
group("fuzzers") {
testonly = true
@@ -414,6 +418,20 @@
}
}
+if (is_clang) {
+ pdfium_fuzzer("pdf_nametree_fuzzer") {
+ sources = [
+ "pdf_nametree_fuzzer.cc",
+ ]
+ deps = [
+ "../../core/fpdfapi/page",
+ "../../core/fpdfapi/parser",
+ "../../core/fpdfdoc",
+ "../../third_party:pdfium_base",
+ ]
+ }
+}
+
pdfium_fuzzer("pdf_cmap_fuzzer") {
sources = [
"pdf_cmap_fuzzer.cc",
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
new file mode 100644
index 0000000..9a37024
--- /dev/null
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -0,0 +1,75 @@
+// Copyright 2019 The PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cstdint>
+#include <vector>
+
+#include "core/fpdfapi/page/cpdf_streamparser.h"
+#include "core/fpdfapi/parser/cpdf_dictionary.h"
+#include "core/fpdfapi/parser/cpdf_object.h"
+#include "core/fpdfdoc/cpdf_nametree.h"
+#include "third_party/base/span.h"
+
+struct Params {
+ bool delete_backwards;
+ uint8_t count;
+ std::vector<WideString> names;
+};
+
+std::vector<WideString> GetNames(uint8_t count,
+ FuzzedDataProvider* data_provider) {
+ std::vector<WideString> names;
+ names.reserve(count);
+ for (size_t i = 0; i < count; ++i) {
+ // The name is not that interesting here. Keep it short.
+ constexpr size_t kMaxNameLen = 10;
+ std::string str = data_provider->ConsumeRandomLengthString(kMaxNameLen);
+ names.push_back(WideString::FromUTF16LE(
+ reinterpret_cast<const unsigned short*>(str.data()),
+ str.size() / sizeof(unsigned short)));
+ }
+ return names;
+}
+
+Params GetParams(FuzzedDataProvider* data_provider) {
+ Params params;
+ params.delete_backwards = data_provider->ConsumeBool();
+ params.count = data_provider->ConsumeIntegralInRange(1, 255);
+ params.names = GetNames(params.count, data_provider);
+ return params;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider data_provider(data, size);
+ Params params = GetParams(&data_provider);
+
+ // |remaining| needs to outlive |parser|.
+ std::vector<uint8_t> remaining =
+ data_provider.ConsumeRemainingBytes<uint8_t>();
+ if (remaining.empty())
+ return 0;
+
+ CPDF_StreamParser parser(remaining);
+ auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
+ CPDF_NameTree name_tree(dict.Get());
+ for (const auto& name : params.names) {
+ RetainPtr<CPDF_Object> obj = parser.ReadNextObject(
+ /*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0);
+ if (!obj)
+ break;
+
+ name_tree.AddValueAndName(std::move(obj), name);
+ }
+
+ if (params.delete_backwards) {
+ for (size_t i = params.count; i > 0; --i)
+ name_tree.DeleteValueAndName(i);
+ } else {
+ for (size_t i = 0; i < params.count; ++i)
+ name_tree.DeleteValueAndName(0);
+ }
+ return 0;
+}