From nobody Sat Nov 23 18:18:24 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=gmail.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1724084847940977.5646833380026; Mon, 19 Aug 2024 09:27:27 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id DFC7111E7; Mon, 19 Aug 2024 12:27:26 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 92C6E16D2; Mon, 19 Aug 2024 12:20:31 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 620CE1646; Mon, 19 Aug 2024 12:20:22 -0400 (EDT) Received: from mail-ed1-f42.google.com (mail-ed1-f42.google.com [209.85.208.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id AFA091646 for ; Mon, 19 Aug 2024 12:20:01 -0400 (EDT) Received: by mail-ed1-f42.google.com with SMTP id 4fb4d7f45d1cf-5a10835487fso6900207a12.1 for ; Mon, 19 Aug 2024 09:20:01 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.19.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:59 -0700 (PDT) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.6 required=5.0 tests=DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1724084400; x=1724689200; darn=lists.libvirt.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Tha3kiNDh6y49/rOK3UkcAIoJ9Gypmhj4HAizYIIBFA=; b=cw9zT0v8LzDKdxYkFYkX0uAjILZuSWajZVGYCR2LPFTMpZ9OE3YTn9tsRwARF1cAhA 8i2Gd9F/Q/vl08jMt/lYsMrfKLhTzWSzU16gSQxKCtZ5R5pK5sk8bvqpw5Un+NazDccx 0jhs8F8W7ESzYK/ckqT5SOxoqSsQ0LVaCwKjUvI3W7fH84M/mqtPpRMro02e58SWdpCI 1dDRSx0pi7pqh9xD3XURIFq6JUwbrRYleeSHWW+lTq6s6RNaq0NlxDevoRbuITut8htu VoXrYPAJesa79zNcBCinmFBbwzi2t+t0vodobOAKRxhI82IViKfy2j+NivOmLrRCFsMS OWxw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084400; x=1724689200; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Tha3kiNDh6y49/rOK3UkcAIoJ9Gypmhj4HAizYIIBFA=; b=dLsdu2uGdUXPObx8bmcPfsvrCDINBhoaS+AS8ns4M5k5daBo5IT72xBFRvvmGb/4V6 hHfp5TNmmXAeaYaiM2mOxSjjFy8yUPTg4Hr0xzxj67FCS8N9Zuvjt4X9zd/sMJJcRkYI lH0vKXj0XdfT3iPbNuPQV9RoirPBOeWfVn23IGFV36COQiYNPGCZ1oWuQoPbBT5fyIdG RhJH2n/IKEGabFwaEMZAIshEhCSiCKp5+HE0sf47NzPemnoEznAdX1n8mC0Y+oFILCsu 10feOdV+IbublcFVOJ3RmapekRpbCu1IBaFGVNsZXmRdFuM0fWvXI+xIyf388ELw6UP4 xUug== X-Gm-Message-State: AOJu0Yx166jBjqDySnyUYxHdgZgPzzz4TZ+t0ZWz0a8n5CChWJlhZpnt 9Pdy73YyGRejs0oL8OfBk800TFsKpmIy2gSZD4ElHT4HobaNDyNq64y09g== X-Google-Smtp-Source: AGHT+IEU3VBoWNS9qdlRZ8vbKgPzdewbuQqkJOav2Q+ioXO6Sbzk6qweBXlJ2xqxZnFawCYwsmzg6w== X-Received: by 2002:a05:6402:42c4:b0:5be:facd:aa51 with SMTP id 4fb4d7f45d1cf-5befacdabcamr3340895a12.31.1724084400141; Mon, 19 Aug 2024 09:20:00 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 07/14] fuzz: Implement QEMU XML domain fuzzer Date: Mon, 19 Aug 2024 21:39:45 +0530 Message-Id: <20240819160952.351383-8-rayhan.faizel@gmail.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240819160952.351383-1-rayhan.faizel@gmail.com> References: <20240819160952.351383-1-rayhan.faizel@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-ID-Hash: UKOL5JLMQIUOOUQ5YKBGMZDMWEMKHPZF X-Message-ID-Hash: UKOL5JLMQIUOOUQ5YKBGMZDMWEMKHPZF X-MailFrom: rayhan.faizel@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Rayhan Faizel X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1724084848697116600 Content-Type: text/plain; charset="utf-8" This patch implements the harness for the QEMU driver to fuzz XML parsing and command line generation. The harness uses existing test driver setup and test utilities under testutilsqemu and qemuxmlconftest. Three binaries are generated based on three protobuf definitions: xml_domain, xml_domain_disk_only and xml_domain_interface_only. This is useful to acheive deeper coverage of interfaces and disk definitions. The fuzzers also have the fake network, storage and secret drivers set up. Signed-off-by: Rayhan Faizel --- tests/fuzz/meson.build | 20 +++ tests/fuzz/proto_to_xml.cc | 18 ++ tests/fuzz/proto_to_xml.h | 3 + tests/fuzz/qemu_xml_domain_fuzz.cc | 277 +++++++++++++++++++++++++++++ 4 files changed, 318 insertions(+) create mode 100644 tests/fuzz/qemu_xml_domain_fuzz.cc diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index d0488c3e36..12f9a719f2 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -31,6 +31,26 @@ fuzz_autogen_xml_domain_dep =3D declare_dependency( ] ) =20 +if conf.has('WITH_QEMU') + fuzzer_src =3D [ + 'qemu_xml_domain_fuzz.cc', + 'proto_to_xml.cc', + ] + + qemu_libs =3D [ + test_qemu_driver_lib, + test_utils_lib, + test_utils_qemu_lib, + libvirt_lib, + ] + + xml_fuzzers +=3D [ + { 'name': 'qemu_xml_domain_fuzz', 'src': [ fuzzer_src, xml_domain_prot= o_src ], 'libs': qemu_libs, 'macro': '-DXML_DOMAIN', 'deps': [ fuzz_autogen= _xml_domain_dep ] }, + { 'name': 'qemu_xml_domain_fuzz_disk', 'src': [ fuzzer_src, xml_domain= _disk_only_proto_src ], 'libs': qemu_libs, 'macro': '-DXML_DOMAIN_DISK_ONLY= ', 'deps': [ fuzz_autogen_xml_domain_dep ] }, + { 'name': 'qemu_xml_domain_fuzz_interface', 'src': [ fuzzer_src, xml_d= omain_interface_only_proto_src ], 'libs': qemu_libs, 'macro': '-DXML_DOMAIN= _INTERFACE_ONLY', 'deps': [ fuzz_autogen_xml_domain_dep ] }, + ] +endif + foreach fuzzer: xml_fuzzers xml_domain_fuzz =3D executable(fuzzer['name'], fuzzer['src'], diff --git a/tests/fuzz/proto_to_xml.cc b/tests/fuzz/proto_to_xml.cc index 13393ecb34..36ad1028b1 100644 --- a/tests/fuzz/proto_to_xml.cc +++ b/tests/fuzz/proto_to_xml.cc @@ -198,6 +198,24 @@ convertProtoToXMLInternal(const Message &message, } =20 =20 +void convertProtoToQEMUXMLDomain(const libvirt::MainObj &message, + std::string arch, + std::string &xml) +{ + xml =3D "\n" + " MyGuest\n" + " 4dea22b3-1d52-d8f3-2516-782e98ab3fa0\n" + " \n" + " hvm\n" + " \n" + " 4096\n"; + + convertProtoToXMLInternal(message, xml, true); + + xml +=3D "\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 7fe9597a19..a87547a319 100644 --- a/tests/fuzz/proto_to_xml.h +++ b/tests/fuzz/proto_to_xml.h @@ -24,5 +24,8 @@ #include "port/protobuf.h" =20 =20 +void convertProtoToQEMUXMLDomain(const libvirt::MainObj &message, + std::string arch, + std::string &xml); void convertProtoToXML(const libvirt::MainObj &message, std::string &xml); diff --git a/tests/fuzz/qemu_xml_domain_fuzz.cc b/tests/fuzz/qemu_xml_domai= n_fuzz.cc new file mode 100644 index 0000000000..e1618561cf --- /dev/null +++ b/tests/fuzz/qemu_xml_domain_fuzz.cc @@ -0,0 +1,277 @@ +/* + * qemu_xml_domain_fuzz.cc: QEMU XML 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 + * . + */ + +#include +#include "proto_header_common.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +extern "C" { +#include "internal.h" +#include "viralloc.h" +#include "viridentity.h" +#include "qemu/qemu_block.h" +#include "qemu/qemu_capabilities.h" +#include "qemu/qemu_domain.h" +#include "qemu/qemu_migration.h" +#include "qemu/qemu_process.h" +#include "qemu/qemu_slirp.h" +#include "datatypes.h" +#include "conf/storage_conf.h" +#include "virfilewrapper.h" +#include "configmake.h" + +#include "testutils.h" +#include "testutilsqemu.h" +} + +#include "port/protobuf.h" +#include "proto_to_xml.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +uint64_t parse_pass =3D 0; +uint64_t format_pass =3D 0; +uint64_t command_line_pass =3D 0; +uint64_t success =3D 0; + +extern std::string emulator; + +bool enable_xml_dump =3D false; +bool enable_xml_format =3D false; + +static virCommand * +fuzzGenerateCommandLine(virQEMUDriver *driver, + virDomainObj *vm) +{ + if (qemuProcessCreatePretendCmdPrepare(driver, vm, NULL, + VIR_QEMU_PROCESS_START_COLD) < = 0) + return NULL; + + return qemuProcessCreatePretendCmdBuild(vm, NULL); +} + + +static void +fuzzXMLToCommandLine(virQEMUDriver *driver, + const char *xml_string) +{ + virDomainDef *def =3D NULL; + g_autofree char *formatted_xml =3D NULL; + virDomainObj *vm =3D NULL; + virCommand *command =3D NULL; + + parse_pass++; + if (!(def =3D virDomainDefParseString(xml_string, driver->xmlopt, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE))) + goto cleanup; + + if (enable_xml_format) { + format_pass++; + if (!(formatted_xml =3D virDomainDefFormat(def, driver->xmlopt, + VIR_DOMAIN_DEF_FORMAT_SEC= URE))) + goto cleanup; + } + + if (!(vm =3D virDomainObjNew(driver->xmlopt))) + goto cleanup; + + vm->def =3D def; + + command_line_pass++; + if (!(command =3D fuzzGenerateCommandLine(driver, vm))) + goto cleanup; + + success++; + + cleanup: + virCommandFree(command); + + if (vm) { + vm->def =3D NULL; + virObjectUnref(vm); + } + + virDomainDefFree(def); +} + + +static int +setupTestInfo(testQemuInfo *info, struct testQemuConf *testConf, ...) +{ + va_list ap; + + info->name =3D "fuzz"; + info->conf =3D testConf; + + va_start(ap, testConf); + testQemuInfoSetArgs(info, ap); + va_end(ap); + + if (testQemuInfoInitArgs(info) < 0) + return -1; + + return 0; +} + +/* Fake drivers identical to the one in the test suite. + * TODO: Try to replace some of the handlers such that file I/O is avoided. + */ + +static virStoragePoolPtr +fuzzStoragePoolLookupByName(virConnectPtr conn, + const char *name G_GNUC_UNUSED) +{ + return fakeStoragePoolLookupByName(conn, "pool-fs"); +} + + +static virNetworkPtr +fuzzNetworkLookupByName(virConnectPtr conn, + const char *name G_GNUC_UNUSED) +{ + return fakeNetworkLookupByName(conn, "nat-network;plug-network-basic"); +} + +static virStorageDriver fakeStorageDriver =3D { + .storagePoolLookupByName =3D fuzzStoragePoolLookupByName, + .storagePoolGetXMLDesc =3D fakeStoragePoolGetXMLDesc, + .storageVolLookupByName =3D fakeStorageVolLookupByName, + .storageVolGetInfo =3D fakeStorageVolGetInfo, + .storageVolGetPath =3D fakeStorageVolGetPath, + .storagePoolIsActive =3D fakeStoragePoolIsActive, +}; + +static virNetworkDriver fakeNetworkDriver =3D { + .networkLookupByName =3D fuzzNetworkLookupByName, + .networkGetXMLDesc =3D fakeNetworkGetXMLDesc, + .networkPortCreateXML =3D fakeNetworkPortCreateXML, + .networkPortGetXMLDesc =3D fakeNetworkPortGetXMLDesc, +}; + +static virSecretDriver fakeSecretDriver =3D { + .connectNumOfSecrets =3D NULL, + .connectListSecrets =3D NULL, + .secretLookupByUUID =3D fakeSecretLookupByUUID, + .secretLookupByUsage =3D fakeSecretLookupByUsage, + .secretDefineXML =3D NULL, + .secretGetXMLDesc =3D NULL, + .secretSetValue =3D NULL, + .secretGetValue =3D fakeSecretGetValue, + .secretUndefine =3D NULL, +}; + +DEFINE_PROTO_FUZZER(const libvirt::MainObj &message) +{ + static GHashTable *capscache =3D virHashNew(virObjectUnref); + static GHashTable *capslatest =3D testQemuGetLatestCaps(); + static GHashTable *qapiSchemaCache =3D virHashNew((GDestroyNotify) g_h= ash_table_unref); + + static struct testQemuConf testConf =3D { .capscache =3D capscache, + .capslatest =3D capslatest, + .qapiSchemaCache =3D qapiSchem= aCache }; + + static virQEMUDriver driver; + static virConnect *conn; + static bool initialized =3D false; + + static testQemuInfo *info =3D g_new0(testQemuInfo, 1); + + static const char *arch_env =3D g_getenv("LPM_FUZZ_ARCH"); + static const char *dump_xml_env =3D g_getenv("LPM_XML_DUMP_INPUT"); + static const char *format_xml_env =3D g_getenv("LPM_XML_FORMAT_ENABLE"= ); + + static std::string arch =3D ""; + + std::string xml =3D ""; + int ret =3D 0; + + /* + * One-time setup of QEMU driver and testQemuInfo. Re-running them in = every + * iteration incurs a significant penalty to the speed of the fuzzer. + */ + if (!initialized) { + FUZZ_COMMON_INIT(); + + if (qemuTestDriverInit(&driver) < 0) + exit(EXIT_FAILURE); + + if (!(conn =3D virGetConnect())) + exit(EXIT_FAILURE); + + conn->networkDriver =3D &fakeNetworkDriver; + conn->storageDriver =3D &fakeStorageDriver; + conn->secretDriver =3D &fakeSecretDriver; + + virSetConnectNetwork(conn); + virSetConnectStorage(conn); + virSetConnectSecret(conn); + + if (arch_env) { + arch =3D arch_env; + } else { + arch =3D "x86_64"; + } + + ret =3D setupTestInfo(info, &testConf, ARG_CAPS_ARCH, arch.c_str(), + ARG_CAPS_VER, "latest", + ARG_END); + + emulator =3D "/usr/bin/qemu-system-" + arch; + + if (ret < 0) { + printf("Unable to set up test information (invalid arch?)\n"); + exit(EXIT_FAILURE); + } + + /* QEMU Capabilities Cache Setup */ + virFileCacheClear(driver.qemuCapsCache); + if (qemuTestCapsCacheInsert(driver.qemuCapsCache, info->qemuCaps) = < 0) + exit(EXIT_FAILURE); + + /* Enable printing of XML to stdout (useful for debugging crashes)= */ + if (dump_xml_env && STREQ(dump_xml_env, "YES")) + enable_xml_dump =3D true; + + /* Enable fuzzing of XML formatting */ + if (format_xml_env && STREQ(format_xml_env, "YES")) + enable_xml_format =3D true; + + initialized =3D true; + } + + convertProtoToQEMUXMLDomain(message, arch, xml); + + if (enable_xml_dump) + printf("%s\n", xml.c_str()); + + fuzzXMLToCommandLine(&driver, xml.c_str()); + + if (parse_pass % 1000 =3D=3D 0) + printf("[FUZZ METRICS] Parse: %lu, Format: %lu, Cmdline: %lu, Succ= ess: %lu\n", + parse_pass, format_pass, command_line_pass, success); +} --=20 2.34.1