This patch implements the harness for the Cloud-Hypervisor driver to fuzz
XML parsing and XML-to-JSON generation including virCHMonitorBuildVMJson
and virCHMonitorBuildNetJson.
Signed-off-by: Rayhan Faizel <rayhan.faizel@gmail.com>
---
src/ch/ch_monitor.c | 2 +-
src/ch/ch_monitor.h | 3 +
tests/fuzz/ch_xml_domain_fuzz.cc | 157 +++++++++++++++++++++++++++++++
tests/fuzz/meson.build | 17 ++++
tests/fuzz/proto_to_xml.cc | 18 ++++
tests/fuzz/proto_to_xml.h | 2 +
6 files changed, 198 insertions(+), 1 deletion(-)
create mode 100644 tests/fuzz/ch_xml_domain_fuzz.cc
diff --git a/src/ch/ch_monitor.c b/src/ch/ch_monitor.c
index 3e49902791..a10dd9f03d 100644
--- a/src/ch/ch_monitor.c
+++ b/src/ch/ch_monitor.c
@@ -423,7 +423,7 @@ virCHMonitorBuildDevicesJson(virJSONValue *content,
return 0;
}
-static int
+int
virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
char **jsonstr)
{
diff --git a/src/ch/ch_monitor.h b/src/ch/ch_monitor.h
index b35f5ea027..aec4a06de8 100644
--- a/src/ch/ch_monitor.h
+++ b/src/ch/ch_monitor.h
@@ -133,3 +133,6 @@ virCHMonitorBuildNetJson(virDomainNetDef *netdef,
int virCHMonitorBuildRestoreJson(virDomainDef *vmdef,
const char *from,
char **jsonstr);
+int
+virCHMonitorBuildVMJson(virCHDriver *driver, virDomainDef *vmdef,
+ char **jsonstr);
diff --git a/tests/fuzz/ch_xml_domain_fuzz.cc b/tests/fuzz/ch_xml_domain_fuzz.cc
new file mode 100644
index 0000000000..6733f55378
--- /dev/null
+++ b/tests/fuzz/ch_xml_domain_fuzz.cc
@@ -0,0 +1,157 @@
+/*
+ * ch_xml_domain_fuzz.cc: Cloud-Hypervisor 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 "ch/ch_conf.h"
+#include "ch/ch_monitor.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;
+uint64_t net = 0;
+uint64_t net_success = 0;
+
+bool enable_xml_dump = false;
+bool enable_xml_format = false;
+
+extern std::string emulator;
+
+static void
+fuzzXMLToCommandLine(virCHDriver *driver,
+ const char *xml_string)
+{
+ virDomainDef *def = NULL;
+ g_autofree char *formatted_xml = NULL;
+ g_autofree char *json_str = NULL;
+ size_t i;
+
+ parse_pass++;
+ if (!(def = virDomainDefParseString(xml_string, driver->xmlopt, NULL,
+ VIR_DOMAIN_DEF_PARSE_INACTIVE)))
+ goto cleanup;
+
+ if (enable_xml_format) {
+ format_pass++;
+ if (!(formatted_xml = virDomainDefFormat(def, driver->xmlopt,
+ VIR_DOMAIN_DEF_FORMAT_SECURE)))
+ goto cleanup;
+ }
+
+ command_line_pass++;
+
+ if (virCHMonitorBuildVMJson(driver, def, &json_str) < 0)
+ goto cleanup;
+ success++;
+
+ for (i = 0; i < def->nnets; i++) {
+ net++;
+ if (virCHMonitorBuildNetJson(def->nets[i], i, &json_str) == 0)
+ net_success++;
+ }
+
+ cleanup:
+ virDomainDefFree(def);
+}
+
+
+DEFINE_PROTO_FUZZER(const libvirt::MainObj &message)
+{
+ static virCHDriver *driver;
+ static bool initialized = false;
+
+ static const char *dump_xml_env = g_getenv("LPM_XML_DUMP_INPUT");
+ static const char *format_xml_env = g_getenv("LPM_XML_FORMAT_ENABLE");
+
+ std::string xml = "";
+
+
+ /*
+ * One-time setup of CH driver. Re-running them in every
+ * iteration incurs a significant penalty to the speed of the fuzzer.
+ */
+ if (!initialized) {
+ FUZZ_COMMON_INIT();
+
+ driver = g_new0(virCHDriver, 1);
+
+ if (!(driver->caps = virCHDriverCapsInit())) {
+ printf("Unable to initialize driver capabilities\n");
+ exit(EXIT_FAILURE);
+ }
+
+ virCapsGuest *guest = driver->caps->guests[0];
+
+ /* Add KVM and HYPERV capabilities */
+ virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_KVM,
+ NULL, NULL, 0, NULL);
+
+ virCapabilitiesAddGuestDomain(guest, VIR_DOMAIN_VIRT_HYPERV,
+ NULL, NULL, 0, NULL);
+
+ if (!(driver->xmlopt = chDomainXMLConfInit(driver))) {
+ printf("Unable to initialize driver XMLOPT\n");
+ exit(EXIT_FAILURE);
+ }
+
+ emulator = "/usr/bin/cloud-hypervisor";
+
+ /* 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;
+ }
+
+ convertProtoToCHXMLDomain(message, xml);
+
+ if (enable_xml_dump)
+ printf("%s\n", xml.c_str());
+
+ fuzzXMLToCommandLine(driver, xml.c_str());
+
+ if (parse_pass % 1000 == 0)
+ printf("[FUZZ METRICS] Parse: %lu, Format: %lu, Cmdline: %lu, Success: %lu, Network: %lu, Network Success: %lu\n",
+ parse_pass, format_pass, command_line_pass, success, net, net_success);
+
+}
diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build
index 9579f56749..bb27c2843d 100644
--- a/tests/fuzz/meson.build
+++ b/tests/fuzz/meson.build
@@ -57,6 +57,23 @@ if conf.has('WITH_QEMU')
]
endif
+if conf.has('WITH_CH')
+ fuzzer_src = [
+ 'ch_xml_domain_fuzz.cc',
+ 'proto_to_xml.cc',
+ ]
+
+ ch_libs = [
+ ch_driver_impl,
+ test_utils_lib,
+ libvirt_lib,
+ ]
+
+ xml_fuzzers += [
+ { 'name': 'ch_xml_domain_fuzz', 'src': [ fuzzer_src, xml_domain_proto_src ], 'libs': ch_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 36ad1028b1..40858e4779 100644
--- a/tests/fuzz/proto_to_xml.cc
+++ b/tests/fuzz/proto_to_xml.cc
@@ -216,6 +216,24 @@ void convertProtoToQEMUXMLDomain(const libvirt::MainObj &message,
}
+void convertProtoToCHXMLDomain(const libvirt::MainObj &message,
+ std::string &xml)
+{
+ xml = "<domain type='kvm'>\n"
+ " <name>MyGuest</name>\n"
+ " <uuid>4dea22b3-1d52-d8f3-2516-782e98ab3fa0</uuid>\n"
+ " <os>\n"
+ " <type>hvm</type>\n"
+ " <kernel>hypervisor-fw</kernel>\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 a87547a319..5c76772c5f 100644
--- a/tests/fuzz/proto_to_xml.h
+++ b/tests/fuzz/proto_to_xml.h
@@ -27,5 +27,7 @@
void convertProtoToQEMUXMLDomain(const libvirt::MainObj &message,
std::string arch,
std::string &xml);
+void convertProtoToCHXMLDomain(const libvirt::MainObj &message,
+ std::string &xml);
void convertProtoToXML(const libvirt::MainObj &message,
std::string &xml);
--
2.34.1