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@xxxxxxxxx> --- 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