[PATCH 11/14] fuzz: Implement VMX XML domain fuzzer

Rayhan Faizel posted 14 patches 3 months ago
[PATCH 11/14] fuzz: Implement VMX XML domain fuzzer
Posted by Rayhan Faizel 3 months ago
This patch adds the harness for the VMX driver to fuzz XML parsing
and VMX config generation. VMX config generation is done with a hardcoded
version of 13.

Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
---
 tests/fuzz/meson.build            |  16 +++
 tests/fuzz/proto_to_xml.cc        |  18 +++
 tests/fuzz/proto_to_xml.h         |   3 +
 tests/fuzz/vmx_xml_domain_fuzz.cc | 208 ++++++++++++++++++++++++++++++
 4 files changed, 245 insertions(+)
 create mode 100644 tests/fuzz/vmx_xml_domain_fuzz.cc

diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build
index bb27c2843d..88b5fe103c 100644
--- a/tests/fuzz/meson.build
+++ b/tests/fuzz/meson.build
@@ -74,6 +74,22 @@ if conf.has('WITH_CH')
   ]
 endif
 
+if conf.has('WITH_VMX')
+  fuzzer_src = [
+    'vmx_xml_domain_fuzz.cc',
+    'proto_to_xml.cc',
+  ]
+
+  vmx_libs = [
+    test_utils_lib,
+    libvirt_lib,
+  ]
+
+  xml_fuzzers += [
+    { 'name': 'vmx_xml_domain_fuzz', 'src': [ fuzzer_src, xml_domain_proto_src ], 'libs': vmx_libs, 'macro': '-DXML_DOMAIN', 'deps': [ fuzz_autogen_xml_domain_dep ] },
+  ]
+endif
+
 foreach fuzzer: xml_fuzzers
   xml_domain_fuzz = executable(fuzzer['name'],
     fuzzer['src'],
diff --git a/tests/fuzz/proto_to_xml.cc b/tests/fuzz/proto_to_xml.cc
index 40858e4779..983256bcae 100644
--- a/tests/fuzz/proto_to_xml.cc
+++ b/tests/fuzz/proto_to_xml.cc
@@ -234,6 +234,24 @@ void convertProtoToCHXMLDomain(const libvirt::MainObj &message,
 }
 
 
+void convertProtoToVMXXMLDomain(const libvirt::MainObj &message,
+                                std::string arch,
+                                std::string &xml)
+{
+    xml = "<domain type='vmware'>\n"
+          "    <name>MyGuest</name>\n"
+          "    <uuid>4dea22b3-1d52-d8f3-2516-782e98ab3fa0</uuid>\n"
+          "    <os>\n"
+          "        <type arch='" + arch + "'>hvm</type>\n"
+          "    </os>\n"
+          "    <memory>4096</memory>\n";
+
+    convertProtoToXMLInternal(message, xml, true);
+
+    xml += "</domain>\n";
+}
+
+
 void convertProtoToXML(const libvirt::MainObj &message,
                        std::string &xml)
 {
diff --git a/tests/fuzz/proto_to_xml.h b/tests/fuzz/proto_to_xml.h
index 5c76772c5f..89e6726611 100644
--- a/tests/fuzz/proto_to_xml.h
+++ b/tests/fuzz/proto_to_xml.h
@@ -29,5 +29,8 @@ void convertProtoToQEMUXMLDomain(const libvirt::MainObj &message,
                                  std::string &xml);
 void convertProtoToCHXMLDomain(const libvirt::MainObj &message,
                                std::string &xml);
+void convertProtoToVMXXMLDomain(const libvirt::MainObj &message,
+                                std::string arch,
+                                std::string &xml);
 void convertProtoToXML(const libvirt::MainObj &message,
                        std::string &xml);
diff --git a/tests/fuzz/vmx_xml_domain_fuzz.cc b/tests/fuzz/vmx_xml_domain_fuzz.cc
new file mode 100644
index 0000000000..69859551bd
--- /dev/null
+++ b/tests/fuzz/vmx_xml_domain_fuzz.cc
@@ -0,0 +1,208 @@
+/*
+ * vmx_xml_domain_fuzz.cc: VMX domain fuzzing harness
+ *
+ * Copyright (C) 2024 Rayhan Faizel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include "proto_header_common.h"
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+extern "C" {
+#include "testutils.h"
+#include "internal.h"
+#include "viralloc.h"
+#include "vmx/vmx.h"
+}
+
+#include "port/protobuf.h"
+#include "proto_to_xml.h"
+#include "src/libfuzzer/libfuzzer_macro.h"
+
+uint64_t parse_pass = 0;
+uint64_t format_pass = 0;
+uint64_t command_line_pass = 0;
+uint64_t success = 0;
+
+bool enable_xml_dump = false;
+bool enable_xml_format = false;
+
+static void
+fuzzXMLToCommandLine(virVMXContext *ctx,
+                     virDomainXMLOption *xmlopt,
+                     const char *xml_string)
+{
+    virDomainDef *def = NULL;
+    g_autofree char *formatted_xml = NULL;
+    g_autofree char *formatted_config = NULL;
+
+    parse_pass++;
+    if (!(def = virDomainDefParseString(xml_string, xmlopt, NULL,
+                                        VIR_DOMAIN_DEF_PARSE_INACTIVE)))
+        goto cleanup;
+
+    if (enable_xml_format) {
+        format_pass++;
+        if (!(formatted_xml = virDomainDefFormat(def, xmlopt,
+                                                 VIR_DOMAIN_DEF_FORMAT_SECURE)))
+            goto cleanup;
+    }
+
+    command_line_pass++;
+
+    if (!(formatted_config = virVMXFormatConfig(ctx, xmlopt, def, 13))) {
+        goto cleanup;
+    }
+
+    success++;
+
+    cleanup:
+
+    virDomainDefFree(def);
+}
+
+
+virCaps *fuzzCapsInit() {
+    virCapsGuest *guest = NULL;
+    virCaps *caps = NULL;
+
+    caps = virCapabilitiesNew(VIR_ARCH_I686, true, true);
+
+    if (caps == NULL)
+        return NULL;
+
+    virCapabilitiesAddHostMigrateTransport(caps, "esx");
+
+    /* i686 guest */
+    guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM,
+                                    VIR_ARCH_I686,
+                                    NULL, NULL, 0, NULL);
+
+    virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_VMWARE,
+                                  NULL, NULL, 0, NULL);
+
+    /* x86_64 guest */
+    guest = virCapabilitiesAddGuest(caps, VIR_DOMAIN_OSTYPE_HVM,
+                                    VIR_ARCH_X86_64,
+                                    NULL, NULL, 0, NULL);
+
+    virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_VMWARE,
+                                  NULL, NULL, 0, NULL);
+
+    return caps;
+}
+
+
+static char *
+fuzzFormatVMXFileName(const char *src G_GNUC_UNUSED,
+                      void *opaque G_GNUC_UNUSED)
+{
+    return g_strdup("/vmfs/volumes/test1/test2");
+}
+
+
+static int
+fuzzAutodetectSCSIControllerModel(virDomainDiskDef *def G_GNUC_UNUSED,
+                                  int *model, void *opaque G_GNUC_UNUSED)
+{
+    *model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC;
+
+    return 0;
+}
+
+
+DEFINE_PROTO_FUZZER(const libvirt::MainObj &message)
+{
+    static virVMXContext ctx;
+    static virDomainXMLOption *xmlopt = NULL;
+    static virCaps *caps;
+
+    static bool initialized = false;
+
+    static const char *arch_env = g_getenv("LPM_FUZZ_ARCH");
+    static const char *dump_xml_env = g_getenv("LPM_XML_DUMP_INPUT");
+    static const char *format_xml_env = g_getenv("LPM_XML_FORMAT_ENABLE");
+
+    static std::string arch = "";
+
+    std::string xml = "";
+
+    /*
+     * One-time setup of VMX driver. Re-running them in every
+     * iteration incurs a significant penalty to the speed of the fuzzer.
+     */
+    if (!initialized) {
+        FUZZ_COMMON_INIT();
+
+        caps = fuzzCapsInit();
+
+        if (caps == NULL) {
+            exit(EXIT_FAILURE);
+        }
+
+        if (arch_env) {
+            arch = arch_env;
+        } else {
+            arch = "x86_64";
+        }
+
+        if (!(arch == "x86_64" || arch == "i686")) {
+            printf("Unsupported architecture: %s\n", arch.c_str());
+            exit(EXIT_FAILURE);
+        }
+
+        if (!(xmlopt = virVMXDomainXMLConfInit(caps)))
+            exit(EXIT_FAILURE);
+
+        ctx.opaque = NULL;
+        ctx.parseFileName = NULL;
+        ctx.formatFileName = fuzzFormatVMXFileName;
+        ctx.autodetectSCSIControllerModel = fuzzAutodetectSCSIControllerModel;
+        ctx.datacenterPath = NULL;
+
+        /* Enable printing of XML to stdout (useful for debugging crashes) */
+        if (dump_xml_env && STREQ(dump_xml_env, "YES"))
+            enable_xml_dump = true;
+
+        /* Enable fuzzing of XML formatting */
+        if (format_xml_env && STREQ(format_xml_env, "YES"))
+            enable_xml_format = true;
+
+        initialized = true;
+    }
+
+    convertProtoToVMXXMLDomain(message, arch, xml);
+
+    if (enable_xml_dump)
+        printf("%s\n", xml.c_str());
+
+    fuzzXMLToCommandLine(&ctx, xmlopt, xml.c_str());
+
+    if (parse_pass % 1000 == 0)
+        printf("[FUZZ METRICS] Parse: %lu, Format: %lu, Cmdline: %lu, Success: %lu\n",
+               parse_pass, format_pass, command_line_pass, success);
+
+}
-- 
2.34.1