From 1532fa7577d3741c2e23555c2ccc08c6db77e733 Mon Sep 17 00:00:00 2001
From: Hamzeh Zawawy <hamzeh@google.com>
Date: Tue, 5 Mar 2024 12:13:52 -0800
Subject: [PATCH] Adding fuzzer ResXMLTree

Bug: 328272470
Test: m resxmlparser_fuzzer and then run binary
Change-Id: I389abf4f8cfb534c354b9846293c5b583987f4e2
---
 .../fuzz/resxmlparser_fuzzer/Android.bp       | 51 ++++++++++++
 .../resxmlparser_fuzzer.cpp                   | 80 +++++++++++++++++++
 .../testdata/attributes.xml                   | 10 +++
 .../resxmlparser_fuzzer/testdata/basic.xml    |  5 ++
 .../resxmlparser_fuzzer/testdata/cdata.xml    |  6 ++
 .../resxmlparser_fuzzer/xmlparser_fuzzer.dict | 11 +++
 6 files changed, 163 insertions(+)
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml
 create mode 100644 libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict

diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp b/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp
new file mode 100644
index 000000000000..4b008a7b4815
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/Android.bp
@@ -0,0 +1,51 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_libs_androidfw_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_libs_androidfw_license"],
+}
+
+cc_fuzz {
+    name: "resxmlparser_fuzzer",
+    srcs: [
+        "resxmlparser_fuzzer.cpp",
+    ],
+    host_supported: true,
+
+    static_libs: ["libgmock"],
+    target: {
+        android: {
+            shared_libs: [
+                "libandroidfw",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libandroidfw",
+                "libbase",
+                "libbinder",
+                "libcutils",
+                "liblog",
+                "libutils",
+            ],
+        },
+        darwin: {
+            // libbinder is not supported on mac
+            enabled: false,
+        },
+    },
+
+    include_dirs: [
+        "system/incremental_delivery/incfs/util/include/",
+    ],
+
+    corpus: ["testdata/*"],
+    dictionary: "xmlparser_fuzzer.dict",
+}
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
new file mode 100644
index 000000000000..829a39617012
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/resxmlparser_fuzzer.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <cstdint>
+#include <cstddef>
+#include <fuzzer/FuzzedDataProvider.h>
+#include "androidfw/ResourceTypes.h"
+
+static void populateDynamicRefTableWithFuzzedData(
+    android::DynamicRefTable& table,
+    FuzzedDataProvider& fuzzedDataProvider) {
+
+    const size_t numMappings = fuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, 5);
+    for (size_t i = 0; i < numMappings; ++i) {
+        const uint8_t packageId = fuzzedDataProvider.ConsumeIntegralInRange<uint8_t>(0x02, 0x7F);
+
+        // Generate a package name
+        std::string packageName;
+        size_t packageNameLength = fuzzedDataProvider.ConsumeIntegralInRange<size_t>(1, 128);
+        for (size_t j = 0; j < packageNameLength; ++j) {
+            // Consume characters only in the ASCII range (0x20 to 0x7E) to ensure valid UTF-8
+            char ch = fuzzedDataProvider.ConsumeIntegralInRange<char>(0x20, 0x7E);
+            packageName.push_back(ch);
+        }
+
+        // Convert std::string to String16 for compatibility
+        android::String16 androidPackageName(packageName.c_str(), packageName.length());
+
+        // Add the mapping to the table
+        table.addMapping(androidPackageName, packageId);
+    }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+    FuzzedDataProvider fuzzedDataProvider(data, size);
+
+    auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
+
+    // Populate the DynamicRefTable with fuzzed data
+    populateDynamicRefTableWithFuzzedData(*dynamic_ref_table, fuzzedDataProvider);
+
+    auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
+
+    std::vector<uint8_t> xmlData = fuzzedDataProvider.ConsumeRemainingBytes<uint8_t>();
+    if (tree.setTo(xmlData.data(), xmlData.size()) != android::NO_ERROR) {
+        return 0; // Exit early if unable to parse XML data
+    }
+
+    tree.restart();
+
+    size_t len = 0;
+    auto code = tree.next();
+    if (code == android::ResXMLParser::START_TAG) {
+        // Access element name
+        auto name = tree.getElementName(&len);
+
+        // Access attributes of the current element
+        for (size_t i = 0; i < tree.getAttributeCount(); i++) {
+            // Access attribute name
+            auto attrName = tree.getAttributeName(i, &len);
+        }
+    } else if (code == android::ResXMLParser::TEXT) {
+        const auto text = tree.getText(&len);
+    }
+    return 0; // Non-zero return values are reserved for future use.
+}
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml
new file mode 100644
index 000000000000..417fec72be6a
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/attributes.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <child id="1">
+        <subchild type="A">Content A</subchild>
+        <subchild type="B">Content B</subchild>
+    </child>
+    <child id="2" extra="data">
+        <subchild type="C">Content C</subchild>
+    </child>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml
new file mode 100644
index 000000000000..7e13db536fc9
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/basic.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <child1>Value 1</child1>
+    <child2>Value 2</child2>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml
new file mode 100644
index 000000000000..90cdf3513be9
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/testdata/cdata.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+    <!-- Example with special characters and CDATA -->
+    <data><![CDATA[Some <encoded> data & other "special" characters]]></data>
+    <message>Hello &amp; Welcome!</message>
+</root>
diff --git a/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict b/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict
new file mode 100644
index 000000000000..745ded4810f3
--- /dev/null
+++ b/libs/androidfw/fuzz/resxmlparser_fuzzer/xmlparser_fuzzer.dict
@@ -0,0 +1,11 @@
+root_tag=<root>
+child_tag=<child>
+end_child_tag=</child>
+id_attr=id="
+type_attr=type="
+cdata_start=<![CDATA[
+cdata_end=]]>
+ampersand_entity=&amp;
+xml_header=<?xml version="1.0" encoding="UTF-8"?>
+comment_start=<!--
+comment_end= -->
-- 
GitLab