From nobody Sat Nov 23 15:01:47 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 1724084482836843.7957667754774; Mon, 19 Aug 2024 09:21:22 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id B24BC166D; Mon, 19 Aug 2024 12:21:21 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 6C6E1164C; Mon, 19 Aug 2024 12:20:00 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 100C0143E; Mon, 19 Aug 2024 12:19:55 -0400 (EDT) Received: from mail-lj1-f170.google.com (mail-lj1-f170.google.com [209.85.208.170]) (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 F31CE13EF for ; Mon, 19 Aug 2024 12:19:53 -0400 (EDT) Received: by mail-lj1-f170.google.com with SMTP id 38308e7fff4ca-2f15dd0b489so63447961fa.3 for ; Mon, 19 Aug 2024 09:19:53 -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.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:51 -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=1724084392; x=1724689192; 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=lHzqFYqrrbdfvPJNKfJ1lv4ytVJNuWwI9U3JryU92Gs=; b=Dplr7SIGYOPP8eCTQhf5T0rT96+RqQCgB6xOjVvmV8d1R6kdeDBxY1l/19r8FYDzny xB5ebNHqVbYOnbeaA7D7uboLDY/7qUdWZYyGMx0EHDZFEJp1NkU/HSYA4zpKtmf0hjmP hj89PRPTFydr+tY78DOkmz+bgdRMmv2exW//Fs8h6LUq52tNkUwcK6LQSnrg0v7I+vCl 4z0iLdv87xNisnxYjn/lWJ2LjG56AjV+HzX81JZKRnuudx3RYj2gYPi3OY+0gBP3gwo9 FTytniOUrch8j//Q4+x6ayYDWqViMqH4a8O3nrlyKHZGMRKeq0aSIQCarEgpkm79MAFh FGBg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084392; x=1724689192; 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=lHzqFYqrrbdfvPJNKfJ1lv4ytVJNuWwI9U3JryU92Gs=; b=hFvIjPzoFH/5/eezK8X7AhNzLY5EOYQK6FqxAZKIjalb6KGt0QwHGkwbUTEW/P/fog U2JoPySqvNx+nWqqQbsQysmprTm8CnyIFNu99+SE1gf2LUV+/pK9QqI4i9hqYop7+ukl wwEoQ2NH45El3NcaLMmwCxIWaFVRtYLdMDQWP+v/RE0c7OtnIJ6CUhe1yHSbT8SFjPRL J9LPBLZyC4LJbzfj7OELCbnM+4s1ZW92WwWQlS3mbY0sKGIeo1Un7VrZYsnd2lgNytAI 4ka+SY9S1U0YcHok6ZanYljMZYyKO/hH6nDnE1AWfu25euS8cXYuI8sD0uzzleAdW1fP RN6A== X-Gm-Message-State: AOJu0YxWHhSowsphw51znbClu059Lq6m+jAfDt56mDCyIg3l3LVYH2Ec a6LXH+bOlnTxtTOYfH2KOTlAAQPdtGcf0i63XTjg7HcOKQTUIIGC/OTD5g== X-Google-Smtp-Source: AGHT+IFNX4WjCUfW0VlrM8gmQvGdQdch7EcqNHV12fplPyRXcvSLHXtecvWH7vvpTENnfz0rrQfHxA== X-Received: by 2002:a2e:a596:0:b0:2f3:e2f0:af16 with SMTP id 38308e7fff4ca-2f3e2f0b27emr8564661fa.26.1724084391698; Mon, 19 Aug 2024 09:19:51 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 01/14] src: Tweak source code to allow C++ compilation Date: Mon, 19 Aug 2024 21:39:39 +0530 Message-Id: <20240819160952.351383-2-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: KJG3KIK2AQANOVYDRYDMVWL6DDOCC6IL X-Message-ID-Hash: KJG3KIK2AQANOVYDRYDMVWL6DDOCC6IL 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: 1724084483413116600 Content-Type: text/plain; charset="utf-8" Since the fuzzers require C++ and will include various libvirt headers, some minor fixups will be needed. 1. 'this' and 'namespace' are reserved C++ keywords so replace them. 2. There is an enum and struct sharing the same name virNetDevVPortProfile. This is not allowed under C++. 3. G_NO_INLINE works differently under C++. Some compile errors occur because of this. To work around this, we rearrange G_NO_INLINE to allow compilation while allowing libvirt to compile normally as before without fuzzing enabled. Signed-off-by: Rayhan Faizel --- src/conf/domain_conf.c | 18 +++++++++--------- src/conf/domain_conf.h | 4 ++-- src/conf/netdev_vport_profile_conf.c | 2 +- src/qemu/qemu_monitor.c | 6 +++--- src/qemu/qemu_monitor.h | 2 +- src/util/virfile.h | 2 +- src/util/virnetdev.h | 12 ++++++------ src/util/virnetdevip.h | 2 +- src/util/virnetdevmacvlan.h | 2 +- src/util/virnetdevvportprofile.c | 2 +- src/util/virnetdevvportprofile.h | 2 +- src/util/virnvme.c | 4 ++-- src/util/virnvme.h | 2 +- src/util/viruuid.h | 2 +- 14 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index d950921667..61418c4bc2 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -29863,15 +29863,15 @@ virDomainAudioBackendIsEqual(virDomainAudioDef *t= his, =20 =20 bool -virDomainAudioIsEqual(virDomainAudioDef *this, - virDomainAudioDef *that) -{ - return this->type =3D=3D that->type && - this->id =3D=3D that->id && - this->timerPeriod =3D=3D that->timerPeriod && - virDomainAudioIOCommonIsEqual(&this->input, &that->input) && - virDomainAudioIOCommonIsEqual(&this->output, &that->output) && - virDomainAudioBackendIsEqual(this, that); +virDomainAudioIsEqual(virDomainAudioDef *first, + virDomainAudioDef *second) +{ + return first->type =3D=3D second->type && + first->id =3D=3D second->id && + first->timerPeriod =3D=3D second->timerPeriod && + virDomainAudioIOCommonIsEqual(&first->input, &second->input) && + virDomainAudioIOCommonIsEqual(&first->output, &second->output) && + virDomainAudioBackendIsEqual(first, second); } =20 =20 diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index eae621f900..3e97dd6293 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -4346,8 +4346,8 @@ virDomainSoundModelSupportsCodecs(virDomainSoundDef *= def); bool virDomainAudioIOCommonIsSet(virDomainAudioIOCommon *common); bool -virDomainAudioIsEqual(virDomainAudioDef *this, - virDomainAudioDef *that); +virDomainAudioIsEqual(virDomainAudioDef *first, + virDomainAudioDef *second); =20 const char *virDomainChrSourceDefGetPath(virDomainChrSourceDef *chr); =20 diff --git a/src/conf/netdev_vport_profile_conf.c b/src/conf/netdev_vport_p= rofile_conf.c index 032a3147d7..f815ac6bf5 100644 --- a/src/conf/netdev_vport_profile_conf.c +++ b/src/conf/netdev_vport_profile_conf.c @@ -181,7 +181,7 @@ void virNetDevVPortProfileFormat(const virNetDevVPortProfile *virtPort, virBuffer *buf) { - enum virNetDevVPortProfile type; + virNetDevVPortProfileType type; bool noParameters; =20 if (!virtPort) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 7f65c23748..0a6acc5470 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -3464,16 +3464,16 @@ qemuMonitorNBDServerStart(qemuMonitor *mon, int qemuMonitorNBDServerAdd(qemuMonitor *mon, const char *deviceID, - const char *export, + const char *nbd_export, bool writable, const char *bitmap) { - VIR_DEBUG("deviceID=3D%s, export=3D%s, bitmap=3D%s", deviceID, NULLSTR= (export), + VIR_DEBUG("deviceID=3D%s, export=3D%s, bitmap=3D%s", deviceID, NULLSTR= (nbd_export), NULLSTR(bitmap)); =20 QEMU_CHECK_MONITOR(mon); =20 - return qemuMonitorJSONNBDServerAdd(mon, deviceID, export, writable, + return qemuMonitorJSONNBDServerAdd(mon, deviceID, nbd_export, writable, bitmap); } =20 diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 57d1b45bf5..0baf237f92 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -1197,7 +1197,7 @@ int qemuMonitorNBDServerStart(qemuMonitor *mon, ATTRIBUTE_NONNULL(2); int qemuMonitorNBDServerAdd(qemuMonitor *mon, const char *deviceID, - const char *export, + const char *nbd_export, bool writable, const char *bitmap); int qemuMonitorNBDServerStop(qemuMonitor *mon); diff --git a/src/util/virfile.h b/src/util/virfile.h index 7df3fcb840..871aa06f14 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -215,7 +215,7 @@ void virFileActivateDirOverrideForLib(void); =20 off_t virFileLength(const char *path, int fd) ATTRIBUTE_NONNULL(1); bool virFileIsDir (const char *file) ATTRIBUTE_NONNULL(1); -bool virFileExists(const char *file) ATTRIBUTE_NONNULL(1) G_NO_INLINE; +bool virFileExists(const char *file) G_NO_INLINE ATTRIBUTE_NONNULL(1); bool virFileIsExecutable(const char *file) ATTRIBUTE_NONNULL(1); bool virFileIsRegular(const char *file) ATTRIBUTE_NONNULL(1); =20 diff --git a/src/util/virnetdev.h b/src/util/virnetdev.h index c287a7b272..db22519a15 100644 --- a/src/util/virnetdev.h +++ b/src/util/virnetdev.h @@ -168,11 +168,11 @@ int virNetDevSetupControl(const char *ifname, G_GNUC_WARN_UNUSED_RESULT; =20 int virNetDevExists(const char *brname) - ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT G_NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; =20 int virNetDevSetOnline(const char *ifname, bool online) - ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT G_NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; int virNetDevGetOnline(const char *ifname, bool *online) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; @@ -180,7 +180,7 @@ int virNetDevGetOnline(const char *ifname, =20 int virNetDevSetMAC(const char *ifname, const virMacAddr *macaddr) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT G_= NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUS= ED_RESULT; int virNetDevGetMAC(const char *ifname, virMacAddr *macaddr) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; @@ -202,7 +202,7 @@ int virNetDevSetCoalesce(const char *ifname, =20 int virNetDevSetMTU(const char *ifname, int mtu) - ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT G_NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; int virNetDevSetMTUFromDevice(const char *ifname, const char *otherifname) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT; @@ -323,8 +323,8 @@ int virNetDevGetRcvAllMulti(const char *ifname, bool *r= eceive) int virNetDevSysfsFile(char **pf_sysfs_device_link, const char *ifname, const char *file) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) - G_GNUC_WARN_UNUSED_RESULT G_NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNUL= L(3) + G_GNUC_WARN_UNUSED_RESULT; =20 int virNetDevRunEthernetScript(const char *ifname, const char *script) G_NO_INLINE; diff --git a/src/util/virnetdevip.h b/src/util/virnetdevip.h index fdf116f509..2b45d360ae 100644 --- a/src/util/virnetdevip.h +++ b/src/util/virnetdevip.h @@ -59,7 +59,7 @@ int virNetDevIPAddrAdd(const char *ifname, virSocketAddr *addr, virSocketAddr *peer, unsigned int prefix) - ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUSED_RESULT G_= NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) G_GNUC_WARN_UNUS= ED_RESULT; int virNetDevIPRouteAdd(const char *ifname, virSocketAddr *addr, unsigned int prefix, diff --git a/src/util/virnetdevmacvlan.h b/src/util/virnetdevmacvlan.h index a5c34d6417..5846018df1 100644 --- a/src/util/virnetdevmacvlan.h +++ b/src/util/virnetdevmacvlan.h @@ -47,7 +47,7 @@ typedef enum { } virNetDevMacVLanCreateFlags; =20 bool virNetDevMacVLanIsMacvtap(const char *ifname) - ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT G_NO_INLINE; + G_NO_INLINE ATTRIBUTE_NONNULL(1) G_GNUC_WARN_UNUSED_RESULT; =20 int virNetDevMacVLanCreate(const char *ifname, const virMacAddr *macaddress, diff --git a/src/util/virnetdevvportprofile.c b/src/util/virnetdevvportprof= ile.c index c755fa79ec..221e0888b3 100644 --- a/src/util/virnetdevvportprofile.c +++ b/src/util/virnetdevvportprofile.c @@ -279,7 +279,7 @@ static int virNetDevVPortProfileMerge(virNetDevVPortProfile *orig, const virNetDevVPortProfile *mods) { - enum virNetDevVPortProfile otype; + virNetDevVPortProfileType otype; =20 if (!orig || !mods) return 0; diff --git a/src/util/virnetdevvportprofile.h b/src/util/virnetdevvportprof= ile.h index 600b2093c5..1714116e9c 100644 --- a/src/util/virnetdevvportprofile.h +++ b/src/util/virnetdevvportprofile.h @@ -25,7 +25,7 @@ =20 #define LIBVIRT_IFLA_VF_PORT_PROFILE_MAX 40 =20 -typedef enum virNetDevVPortProfile { +typedef enum _virNetDevVPortProfileType { VIR_NETDEV_VPORT_PROFILE_NONE, VIR_NETDEV_VPORT_PROFILE_8021QBG, VIR_NETDEV_VPORT_PROFILE_8021QBH, diff --git a/src/util/virnvme.c b/src/util/virnvme.c index 37333d515b..e996ae2c5b 100644 --- a/src/util/virnvme.c +++ b/src/util/virnvme.c @@ -63,7 +63,7 @@ VIR_ONCE_GLOBAL_INIT(virNVMe); =20 virNVMeDevice * virNVMeDeviceNew(const virPCIDeviceAddress *address, - unsigned long namespace, + unsigned long nvme_namespace, bool managed) { virNVMeDevice *dev =3D NULL; @@ -71,7 +71,7 @@ virNVMeDeviceNew(const virPCIDeviceAddress *address, dev =3D g_new0(virNVMeDevice, 1); =20 virPCIDeviceAddressCopy(&dev->address, address); - dev->namespace =3D namespace; + dev->namespace =3D nvme_namespace; dev->managed =3D managed; =20 return dev; diff --git a/src/util/virnvme.h b/src/util/virnvme.h index ceef402c4b..f1d0bb5da0 100644 --- a/src/util/virnvme.h +++ b/src/util/virnvme.h @@ -33,7 +33,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(virNVMeDeviceList, virObjec= tUnref); =20 virNVMeDevice * virNVMeDeviceNew(const virPCIDeviceAddress *address, - unsigned long namespace, + unsigned long nvme_namespace, bool managed); =20 void diff --git a/src/util/viruuid.h b/src/util/viruuid.h index 9667bd3200..9d0b6d1b00 100644 --- a/src/util/viruuid.h +++ b/src/util/viruuid.h @@ -41,7 +41,7 @@ =20 =20 int virSetHostUUIDStr(const char *host_uuid); -int virGetHostUUID(unsigned char *host_uuid) ATTRIBUTE_NONNULL(1) G_NO_INL= INE; +int virGetHostUUID(unsigned char *host_uuid) G_NO_INLINE ATTRIBUTE_NONNULL= (1); =20 bool virUUIDIsValid(const unsigned char *uuid); =20 --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084512703404.115027350717; Mon, 19 Aug 2024 09:21:52 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 92AC2D46; Mon, 19 Aug 2024 12:21:51 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 3341815D5; Mon, 19 Aug 2024 12:20:02 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 79A1111FB; Mon, 19 Aug 2024 12:19:55 -0400 (EDT) Received: from mail-ed1-f52.google.com (mail-ed1-f52.google.com [209.85.208.52]) (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 B5309141F for ; Mon, 19 Aug 2024 12:19:54 -0400 (EDT) Received: by mail-ed1-f52.google.com with SMTP id 4fb4d7f45d1cf-5bec87ececeso3486144a12.0 for ; Mon, 19 Aug 2024 09:19:54 -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.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:52 -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=1724084393; x=1724689193; 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=vNy2GFN4Th2OK5mwgWnm2u1jv9v+5LmNp0CO0ToGn/o=; b=VQgHxHR7VLqSepKSlltHkimA0Dj2FsBb7PRI470zvC50v+6tFl+B2sEoxC4FjTfrDt nzsYa1lCngAy9wnaOPL6tvTxluHX4aKzKWoXsRrr0teAJi7uLsGlfp2KHDiNGj1ll4Q2 fITuePAJd76T6YsNHrugA7iuzw/CBmEsaOsGaIVcsS6yaaq6IVIB6+4+pZjPlQQpgvGA LO+1HDdWhcG++/IkIT4l3L+PBhKRYOlMJeTrcGvl0InHz3Zk3Mz1kLA20MhDm1du/krl Zwpd1HUGRcU2RBeKprd2b0eVpZFV9ncd3ZAAyzS1BcYgfLYA7wqRc4MOp9RJp/+USeIS 8gyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084393; x=1724689193; 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=vNy2GFN4Th2OK5mwgWnm2u1jv9v+5LmNp0CO0ToGn/o=; b=Fd40zDKuIjkRr9y/4idOnrIz2uItHEy+OUtdlZLUKm3Kd/YEszVy1xFkHqragNfsBh 6bjBBt/cvn9Jo/KUJB3vKU/KbW6bQi2uzisPEu07qndRqwusvpEuNf8dg1Tr9P/Y8Lm4 lrdaNg0MKbVE14mygWbeQhP9NvkHHVbDgrlYPg+BoRM82e2nh4g6mQNCdvNEsrxBeDn9 Atvw0EUrcu+fz9pyrpEig/wi8r2oRweIbAAHPGiDW/XHargjWxwrxYfnZSf4nQJXKJZn 8Eq2bdF0e3Gsnro3CyYqPDA75i6v4ELoyAteABmWzoksVv5EbMNtnAoO2NBlmXKXNkra ZlRg== X-Gm-Message-State: AOJu0YzlBik17a/ntN9dvHF500+vHQxxHy+lwS91QXEmGiNO+sn6fQsq gREQosK7Aiya13pVmdt2pwgtb019JxiBleZql5WsKb7vpLZk/LI6dxMaaQ== X-Google-Smtp-Source: AGHT+IG+pfUzAt8yHVz8VeAKi6C+rIBaGe+fH1KbNcuRiud2C8mId9mks6Hbh/+ciBupjMi+PCedtg== X-Received: by 2002:a05:6402:35ca:b0:5bf:79f:3b6d with SMTP id 4fb4d7f45d1cf-5bf079f3e41mr1180545a12.3.1724084392906; Mon, 19 Aug 2024 09:19:52 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 02/14] meson: Add support for clang/LLVM coverage instrumentation Date: Mon, 19 Aug 2024 21:39:40 +0530 Message-Id: <20240819160952.351383-3-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: 5VLM7FD6DNJB4RZIHRFNPOBRBI5DTVSV X-Message-ID-Hash: 5VLM7FD6DNJB4RZIHRFNPOBRBI5DTVSV 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: 1724084513549116600 Content-Type: text/plain; charset="utf-8" Clang coverage is the de-facto standard for generating coverage reports in libFuzzer based fuzzers, so add support for the same. __LLVM_PROFILE_RT_INIT_ONCE is added automatically which breaks commandhelp= er so filter the same. Note that the existing gcov support can also be used to generate coverage. Signed-off-by: Rayhan Faizel --- meson.build | 11 +++++++++++ meson_options.txt | 3 ++- tests/commandhelper.c | 8 ++++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index f31485c395..904524abc6 100644 --- a/meson.build +++ b/meson.build @@ -2141,6 +2141,17 @@ if get_option('test_coverage') ] endif =20 +if get_option('test_coverage_clang') + if cc.get_id() !=3D 'clang' + error('test_coverage_clang can only be used with the Clang compiler.') + endif + + coverage_flags =3D [ + '-fprofile-instr-generate', + '-fcoverage-mapping', + ] +endif + =20 # Various definitions =20 diff --git a/meson_options.txt b/meson_options.txt index 2d440c63d8..153e325cb5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -8,7 +8,8 @@ option('unitdir', type: 'string', value: '', description: '= directory for systemd option('sysusersdir', type: 'string', value: '', description: 'directory f= or sysusers files') # dep:tests option('expensive_tests', type: 'feature', value: 'auto', description: 'se= t the default for enabling expensive tests (long timeouts)') -option('test_coverage', type: 'boolean', value: false, description: 'turn = on code coverage instrumentation') +option('test_coverage', type: 'boolean', value: false, description: 'turn = on code coverage instrumentation (gcov)') +option('test_coverage_clang', type: 'boolean', value: false, description: = 'turn on code coverage instrumentation (clang)') option('git_werror', type: 'feature', value: 'auto', description: 'use -We= rror if building from GIT') option('rpath', type: 'feature', value: 'auto', description: 'whether to i= nclude rpath information in installed binaries and libraries') option('docdir', type: 'string', value: '', description: 'documentation in= stallation directory') diff --git a/tests/commandhelper.c b/tests/commandhelper.c index d4629d824e..6f562c3e28 100644 --- a/tests/commandhelper.c +++ b/tests/commandhelper.c @@ -172,9 +172,13 @@ static int printEnvironment(FILE *log) for (i =3D 0; i < length; i++) { /* Ignore the variables used to instruct the loader into * behaving differently, as they could throw the tests off. - * Also ignore __CF_USER_TEXT_ENCODING, which is set by macOS. */ + * Also ignore __CF_USER_TEXT_ENCODING, which is set by macOS. + * + * If LLVM coverage is enabled, __LLVM_PROFILE_RT_INIT_ONCE is + * automatically set, which should be ignored. */ if (!STRPREFIX(newenv[i], "LD_") && - !STRPREFIX(newenv[i], "__CF_USER_TEXT_ENCODING=3D")) { + !STRPREFIX(newenv[i], "__CF_USER_TEXT_ENCODING=3D") && + !STRPREFIX(newenv[i], "__LLVM_PROFILE_RT_INIT_ONCE=3D")) { fprintf(log, "ENV:%s\n", newenv[i]); } } --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084562829693.8709384990804; Mon, 19 Aug 2024 09:22:42 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 9437211B4; Mon, 19 Aug 2024 12:22:41 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id ECB211631; Mon, 19 Aug 2024 12:20:11 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 406681660; Mon, 19 Aug 2024 12:20:08 -0400 (EDT) Received: from mail-ed1-f44.google.com (mail-ed1-f44.google.com [209.85.208.44]) (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 1FB24159C for ; Mon, 19 Aug 2024 12:19:56 -0400 (EDT) Received: by mail-ed1-f44.google.com with SMTP id 4fb4d7f45d1cf-5bede548f7cso2556597a12.2 for ; Mon, 19 Aug 2024 09:19:56 -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.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:53 -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=1724084395; x=1724689195; 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=5Ekr0NDBQTYSqRhFKCC1dyNasOjQzpJnRG9JR9puUYc=; b=YraFJu8t9euFgEai+FNZDVvNQ9BF8TmAniAD5+yQIFts408ZlYk5Zms3vyHki9E5DB NXJLgRs2OxXX8uRPfc1nlq5gQz6fYCU9bk7ptLaL5yb+8qO40YETHH2t5TMnpmQBkDje INX091nlfGvSQs4ZAfSuT8k3Pvig3OSPhp0caEgdiDUvt3Xsd8B/e+5JUYVs424BlaLV 6mv9KtJ92dyqIdvg/+elIdmCO/WiAww35zDIFYlC6ah1yOTRD2/BXgY4bci98+Ap5jdL f6+kMqoFSzFCBQNSvJgFHE+t9119WXoZnVQrkiLL+rTO+D9K3YawsV5idi41zDMomsdy FjmQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084395; x=1724689195; 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=5Ekr0NDBQTYSqRhFKCC1dyNasOjQzpJnRG9JR9puUYc=; b=JDD3wQHgiq8DKS7j/mRykeQJMmzNPEQGVZY5mlWutc/C1AOkuWPK1/jUoYH7HJy1q0 Vg3gnD6L/4NKBWIyM/YkUUZdRhOXJsoqTArSR5vrggfZIuz4ZfVCCraCfgw3JlQlduJM kBw9KuJvS2MRI2CSy07iReRggD/Hiwa4XX32oadRyOeHlAfI/ZPYWP9BCm1QfVmmOFne /WeTXBthdXDAJJQLqmWDTcvHMhX3F+aZ6jcLHC8e0LleR+McOUf39qFjKx3WZX1Nu8om ZQY9Us+9pjTO9OFpIxFpx3Tfi9vKsw/pmJOqWi0VIsCARueGfwiU60pt8lA8mp5MVnUl t+4g== X-Gm-Message-State: AOJu0YycWOrRgQzN0Z6ah42ijizl61tjHMNaDvMezuVpZ2AwOv4Lp+2+ Ua/+ri9slzJ9I0egHdxm8mDp73SXkcaoNhDU+UpPhuZJ5UDfAEU85PENng== X-Google-Smtp-Source: AGHT+IEXgTbKeAeqeaKdQ5a2fu3lcN+P/0XQKIuPmQH5CkB7FnLydUp9IIhxkkjnHsHeCp+K527aBg== X-Received: by 2002:a05:6402:84f:b0:5bb:9b22:68f4 with SMTP id 4fb4d7f45d1cf-5beca59a339mr7483167a12.18.1724084394513; Mon, 19 Aug 2024 09:19:54 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 03/14] tests: Export handlers for fake secondary drivers Date: Mon, 19 Aug 2024 21:39:41 +0530 Message-Id: <20240819160952.351383-4-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: 4IYTR7NLC6TOPP7UHVC4I4G3WERNQEHQ X-Message-ID-Hash: 4IYTR7NLC6TOPP7UHVC4I4G3WERNQEHQ 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: 1724084563870116600 Content-Type: text/plain; charset="utf-8" This patch exports handlers for fake network, storage and secret drivers which will be used by the QEMU XML domain fuzzer. Signed-off-by: Rayhan Faizel --- tests/qemuxmlconftest.c | 249 -------------------------------------- tests/testutilsqemu.c | 256 ++++++++++++++++++++++++++++++++++++++++ tests/testutilsqemu.h | 57 +++++++++ 3 files changed, 313 insertions(+), 249 deletions(-) diff --git a/tests/qemuxmlconftest.c b/tests/qemuxmlconftest.c index 89292af300..35830257c7 100644 --- a/tests/qemuxmlconftest.c +++ b/tests/qemuxmlconftest.c @@ -33,52 +33,6 @@ =20 static virQEMUDriver driver; =20 -static unsigned char * -fakeSecretGetValue(virSecretPtr obj G_GNUC_UNUSED, - size_t *value_size, - unsigned int fakeflags G_GNUC_UNUSED) -{ - char *secret; - secret =3D g_strdup("AQCVn5hO6HzFAhAAq0NCv8jtJcIcE+HOBlMQ1A"); - *value_size =3D strlen(secret); - return (unsigned char *) secret; -} - -static virSecretPtr -fakeSecretLookupByUsage(virConnectPtr conn, - int usageType, - const char *usageID) -{ - unsigned char uuid[VIR_UUID_BUFLEN]; - if (usageType =3D=3D VIR_SECRET_USAGE_TYPE_VOLUME) { - if (!STRPREFIX(usageID, "/storage/guest_disks/")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "test provided invalid volume storage prefix '%= s'", - usageID); - return NULL; - } - } else if (STRNEQ(usageID, "mycluster_myname") && - STRNEQ(usageID, "client.admin secret")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "test provided incorrect usage '%s'", usageID); - return NULL; - } - - if (virUUIDGenerate(uuid) < 0) - return NULL; - - return virGetSecret(conn, uuid, usageType, usageID); -} - -static virSecretPtr -fakeSecretLookupByUUID(virConnectPtr conn, - const unsigned char *uuid) -{ - /* NB: This mocked value could be "tls" or "volume" depending on - * which test is being run, we'll leave at NONE (or 0) */ - return virGetSecret(conn, uuid, VIR_SECRET_USAGE_TYPE_NONE, ""); -} - static virSecretDriver fakeSecretDriver =3D { .connectNumOfSecrets =3D NULL, .connectListSecrets =3D NULL, @@ -92,118 +46,6 @@ static virSecretDriver fakeSecretDriver =3D { }; =20 =20 -# define STORAGE_POOL_XML_PATH "storagepoolxml2xmlout/" -static const unsigned char fakeUUID[VIR_UUID_BUFLEN] =3D "fakeuuid"; - -static virStoragePoolPtr -fakeStoragePoolLookupByName(virConnectPtr conn, - const char *name) -{ - g_autofree char *xmlpath =3D NULL; - - if (STRNEQ(name, "inactive")) { - xmlpath =3D g_strdup_printf("%s/%s%s.xml", abs_srcdir, - STORAGE_POOL_XML_PATH, name); - - if (!virFileExists(xmlpath)) { - virReportError(VIR_ERR_NO_STORAGE_POOL, - "File '%s' not found", xmlpath); - return NULL; - } - } - - return virGetStoragePool(conn, name, fakeUUID, NULL, NULL); -} - - -static virStorageVolPtr -fakeStorageVolLookupByName(virStoragePoolPtr pool, - const char *name) -{ - g_auto(GStrv) volinfo =3D NULL; - - if (STREQ(pool->name, "inactive")) { - virReportError(VIR_ERR_OPERATION_INVALID, - "storage pool '%s' is not active", pool->name); - return NULL; - } - - if (STREQ(name, "nonexistent")) { - virReportError(VIR_ERR_NO_STORAGE_VOL, - "no storage vol with matching name '%s'", name); - return NULL; - } - - if (!(volinfo =3D g_strsplit(name, "+", 2))) - return NULL; - - if (!volinfo[1]) { - return virGetStorageVol(pool->conn, pool->name, name, "block", NUL= L, NULL); - } - - return virGetStorageVol(pool->conn, pool->name, volinfo[1], volinfo[0], - NULL, NULL); -} - -static int -fakeStorageVolGetInfo(virStorageVolPtr vol, - virStorageVolInfoPtr info) -{ - memset(info, 0, sizeof(*info)); - - info->type =3D virStorageVolTypeFromString(vol->key); - - if (info->type < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "Invalid volume type '%s'", vol->key); - return -1; - } - - return 0; -} - - -static char * -fakeStorageVolGetPath(virStorageVolPtr vol) -{ - return g_strdup_printf("/some/%s/device/%s", vol->key, vol->name); -} - - -static char * -fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool, - unsigned int flags_unused G_GNUC_UNUSED) -{ - g_autofree char *xmlpath =3D NULL; - char *xmlbuf =3D NULL; - - if (STREQ(pool->name, "inactive")) { - virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); - return NULL; - } - - xmlpath =3D g_strdup_printf("%s/%s%s.xml", abs_srcdir, STORAGE_POOL_XM= L_PATH, - pool->name); - - if (virTestLoadFile(xmlpath, &xmlbuf) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "failed to load XML file '%s'", - xmlpath); - return NULL; - } - - return xmlbuf; -} - -static int -fakeStoragePoolIsActive(virStoragePoolPtr pool) -{ - if (STREQ(pool->name, "inactive")) - return 0; - - return 1; -} - /* Test storage pool implementation * * These functions aid testing of storage pool related stuff when creating= a @@ -257,97 +99,6 @@ static virNWFilterDriver fakeNWFilterDriver =3D { }; =20 =20 -/* name of the fake network shall be constructed as: - * NETWORKXMLNAME;NETWORKPORTXMLNAME - * where: - * NETWORKXMLNAME resolves to abs_srcdir/networkxml2xmlin/NETWORKXMLNAME.= xml - * NETWORKPORTXMLNAME resolves to abs_srcdir/virnetworkportxml2xmldata/NE= TWORKPORTXMLNAME.xml - */ -static virNetworkPtr -fakeNetworkLookupByName(virConnectPtr conn, - const char *name) -{ - unsigned char uuid[VIR_UUID_BUFLEN]; - g_autofree char *netname =3D g_strdup(name); - g_autofree char *path =3D NULL; - char *tmp; - - memset(uuid, 0, VIR_UUID_BUFLEN); - - if ((tmp =3D strchr(netname, ';'))) { - *tmp =3D '\0'; - } else { - virReportError(VIR_ERR_NO_NETWORK, - "Malformed fake network name '%s'. See fakeNetworkL= ookupByName.", - name); - return NULL; - } - - path =3D g_strdup_printf(abs_srcdir "/networkxml2xmlin/%s.xml", netnam= e); - - if (!virFileExists(path)) { - virReportError(VIR_ERR_NO_NETWORK, "fake network '%s' not found", = path); - return NULL; - } - - return virGetNetwork(conn, name, uuid); -} - - -static char * -fakeNetworkGetXMLDesc(virNetworkPtr network, - unsigned int noflags G_GNUC_UNUSED) -{ - g_autofree char *netname =3D g_strdup(network->name); - g_autofree char *path =3D NULL; - char *xml =3D NULL; - - *(strchr(netname, ';')) =3D '\0'; - - path =3D g_strdup_printf(abs_srcdir "/networkxml2xmlin/%s.xml", netnam= e); - - if (virFileReadAll(path, 4 * 1024, &xml) < 0) - return NULL; - - return xml; -} - - -static virNetworkPortPtr -fakeNetworkPortCreateXML(virNetworkPtr net, - const char *xmldesc G_GNUC_UNUSED, - unsigned int noflags G_GNUC_UNUSED) -{ - unsigned char uuid[VIR_UUID_BUFLEN]; - g_autofree char *portname =3D g_strdup(strchr(net->name, ';') + 1); - g_autofree char *path =3D g_strdup_printf(abs_srcdir "/virnetworkportx= ml2xmldata/%s.xml", portname); - - memset(uuid, 0, VIR_UUID_BUFLEN); - - if (!virFileExists(path)) { - virReportError(VIR_ERR_NO_NETWORK_PORT, "fake network port '%s' no= t found", path); - return NULL; - } - - return virGetNetworkPort(net, uuid); -} - - -static char * -fakeNetworkPortGetXMLDesc(virNetworkPortPtr port, - unsigned int noflags G_GNUC_UNUSED) -{ - g_autofree char *portname =3D g_strdup(strchr(port->net->name, ';') + = 1); - g_autofree char *path =3D g_strdup_printf(abs_srcdir "/virnetworkportx= ml2xmldata/%s.xml", portname); - char *xml =3D NULL; - - if (virFileReadAll(path, 4 * 1024, &xml) < 0) - return NULL; - - return xml; -} - - static virNetworkDriver fakeNetworkDriver =3D { .networkLookupByName =3D fakeNetworkLookupByName, .networkGetXMLDesc =3D fakeNetworkGetXMLDesc, diff --git a/tests/testutilsqemu.c b/tests/testutilsqemu.c index ee6cae218a..4445fafd23 100644 --- a/tests/testutilsqemu.c +++ b/tests/testutilsqemu.c @@ -13,6 +13,7 @@ # include "virstring.h" # include "virfilecache.h" # include "virtpm.h" +# include "conf/storage_conf.h" =20 # include # include @@ -1076,3 +1077,258 @@ testQemuPrepareHostBackendChardevOne(virDomainDevic= eDef *dev, =20 return 0; } + + +# define STORAGE_POOL_XML_PATH "storagepoolxml2xmlout/" +static const unsigned char fakeUUID[VIR_UUID_BUFLEN] =3D "fakeuuid"; + +virStoragePoolPtr +fakeStoragePoolLookupByName(virConnectPtr conn, + const char *name) +{ + g_autofree char *xmlpath =3D NULL; + + if (STRNEQ(name, "inactive")) { + xmlpath =3D g_strdup_printf("%s/%s%s.xml", abs_srcdir, + STORAGE_POOL_XML_PATH, name); + + if (!virFileExists(xmlpath)) { + virReportError(VIR_ERR_NO_STORAGE_POOL, + "File '%s' not found", xmlpath); + return NULL; + } + } + + return virGetStoragePool(conn, name, fakeUUID, NULL, NULL); +} + + +virStorageVolPtr +fakeStorageVolLookupByName(virStoragePoolPtr pool, + const char *name) +{ + g_auto(GStrv) volinfo =3D NULL; + + if (STREQ(pool->name, "inactive")) { + virReportError(VIR_ERR_OPERATION_INVALID, + "storage pool '%s' is not active", pool->name); + return NULL; + } + + if (STREQ(name, "nonexistent")) { + virReportError(VIR_ERR_NO_STORAGE_VOL, + "no storage vol with matching name '%s'", name); + return NULL; + } + + if (!(volinfo =3D g_strsplit(name, "+", 2))) + return NULL; + + if (!volinfo[1]) { + return virGetStorageVol(pool->conn, pool->name, name, "block", NUL= L, NULL); + } + + return virGetStorageVol(pool->conn, pool->name, volinfo[1], volinfo[0], + NULL, NULL); +} + + +int +fakeStorageVolGetInfo(virStorageVolPtr vol, + virStorageVolInfoPtr info) +{ + memset(info, 0, sizeof(*info)); + + info->type =3D virStorageVolTypeFromString(vol->key); + + if (info->type < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "Invalid volume type '%s'", vol->key); + return -1; + } + + return 0; +} + + +char * +fakeStorageVolGetPath(virStorageVolPtr vol) +{ + return g_strdup_printf("/some/%s/device/%s", vol->key, vol->name); +} + + +char * +fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool, + unsigned int flags_unused G_GNUC_UNUSED) +{ + g_autofree char *xmlpath =3D NULL; + char *xmlbuf =3D NULL; + + if (STREQ(pool->name, "inactive")) { + virReportError(VIR_ERR_NO_STORAGE_POOL, NULL); + return NULL; + } + + xmlpath =3D g_strdup_printf("%s/%s%s.xml", abs_srcdir, STORAGE_POOL_XM= L_PATH, + pool->name); + + if (virTestLoadFile(xmlpath, &xmlbuf) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "failed to load XML file '%s'", + xmlpath); + return NULL; + } + + return xmlbuf; +} + + +int +fakeStoragePoolIsActive(virStoragePoolPtr pool) +{ + if (STREQ(pool->name, "inactive")) + return 0; + + return 1; +} + + +/* name of the fake network shall be constructed as: + * NETWORKXMLNAME;NETWORKPORTXMLNAME + * where: + * NETWORKXMLNAME resolves to abs_srcdir/networkxml2xmlin/NETWORKXMLNAME.= xml + * NETWORKPORTXMLNAME resolves to abs_srcdir/virnetworkportxml2xmldata/NE= TWORKPORTXMLNAME.xml + */ +virNetworkPtr +fakeNetworkLookupByName(virConnectPtr conn, + const char *name) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + g_autofree char *netname =3D g_strdup(name); + g_autofree char *path =3D NULL; + char *tmp; + + memset(uuid, 0, VIR_UUID_BUFLEN); + + if ((tmp =3D strchr(netname, ';'))) { + *tmp =3D '\0'; + } else { + virReportError(VIR_ERR_NO_NETWORK, + "Malformed fake network name '%s'. See fakeNetworkL= ookupByName.", + name); + return NULL; + } + + path =3D g_strdup_printf(abs_srcdir "/networkxml2xmlin/%s.xml", netnam= e); + + if (!virFileExists(path)) { + virReportError(VIR_ERR_NO_NETWORK, "fake network '%s' not found", = path); + return NULL; + } + + return virGetNetwork(conn, name, uuid); +} + + +char * +fakeNetworkGetXMLDesc(virNetworkPtr network, + unsigned int noflags G_GNUC_UNUSED) +{ + g_autofree char *netname =3D g_strdup(network->name); + g_autofree char *path =3D NULL; + char *xml =3D NULL; + + *(strchr(netname, ';')) =3D '\0'; + + path =3D g_strdup_printf(abs_srcdir "/networkxml2xmlin/%s.xml", netnam= e); + + if (virFileReadAll(path, 4 * 1024, &xml) < 0) + return NULL; + + return xml; +} + + +virNetworkPortPtr +fakeNetworkPortCreateXML(virNetworkPtr net, + const char *xmldesc G_GNUC_UNUSED, + unsigned int noflags G_GNUC_UNUSED) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + g_autofree char *portname =3D g_strdup(strchr(net->name, ';') + 1); + g_autofree char *path =3D g_strdup_printf(abs_srcdir "/virnetworkportx= ml2xmldata/%s.xml", portname); + + memset(uuid, 0, VIR_UUID_BUFLEN); + + if (!virFileExists(path)) { + virReportError(VIR_ERR_NO_NETWORK_PORT, "fake network port '%s' no= t found", path); + return NULL; + } + + return virGetNetworkPort(net, uuid); +} + + +char * +fakeNetworkPortGetXMLDesc(virNetworkPortPtr port, + unsigned int noflags G_GNUC_UNUSED) +{ + g_autofree char *portname =3D g_strdup(strchr(port->net->name, ';') + = 1); + g_autofree char *path =3D g_strdup_printf(abs_srcdir "/virnetworkportx= ml2xmldata/%s.xml", portname); + char *xml =3D NULL; + + if (virFileReadAll(path, 4 * 1024, &xml) < 0) + return NULL; + + return xml; +} + + +unsigned char * +fakeSecretGetValue(virSecretPtr obj G_GNUC_UNUSED, + size_t *value_size, + unsigned int fakeflags G_GNUC_UNUSED) +{ + char *secret; + secret =3D g_strdup("AQCVn5hO6HzFAhAAq0NCv8jtJcIcE+HOBlMQ1A"); + *value_size =3D strlen(secret); + return (unsigned char *) secret; +} + + +virSecretPtr +fakeSecretLookupByUsage(virConnectPtr conn, + int usageType, + const char *usageID) +{ + unsigned char uuid[VIR_UUID_BUFLEN]; + if (usageType =3D=3D VIR_SECRET_USAGE_TYPE_VOLUME) { + if (!STRPREFIX(usageID, "/storage/guest_disks/")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "test provided invalid volume storage prefix '%= s'", + usageID); + return NULL; + } + } else if (STRNEQ(usageID, "mycluster_myname") && + STRNEQ(usageID, "client.admin secret")) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "test provided incorrect usage '%s'", usageID); + return NULL; + } + + if (virUUIDGenerate(uuid) < 0) + return NULL; + + return virGetSecret(conn, uuid, usageType, usageID); +} + + +virSecretPtr +fakeSecretLookupByUUID(virConnectPtr conn, + const unsigned char *uuid) +{ + /* NB: This mocked value could be "tls" or "volume" depending on + * which test is being run, we'll leave at NONE (or 0) */ + return virGetSecret(conn, uuid, VIR_SECRET_USAGE_TYPE_NONE, ""); +} diff --git a/tests/testutilsqemu.h b/tests/testutilsqemu.h index 90632031ff..99d34fcaf5 100644 --- a/tests/testutilsqemu.h +++ b/tests/testutilsqemu.h @@ -188,4 +188,61 @@ testQemuInsertRealCaps(virFileCache *cache, GHashTable *capsCache, GHashTable *schemaCache, GHashTable **schema); + +void +testQemuPrepareDef(virDomainObj *vm, + testQemuInfo *info); + +virStoragePoolPtr +fakeStoragePoolLookupByName(virConnectPtr conn, + const char *name); + +virStorageVolPtr +fakeStorageVolLookupByName(virStoragePoolPtr pool, + const char *name); + +int +fakeStorageVolGetInfo(virStorageVolPtr vol, + virStorageVolInfoPtr info); + +char * +fakeStorageVolGetPath(virStorageVolPtr vol); + +char * +fakeStoragePoolGetXMLDesc(virStoragePoolPtr pool, + unsigned int flags_unused); + +int +fakeStoragePoolIsActive(virStoragePoolPtr pool); + +virNetworkPtr +fakeNetworkLookupByName(virConnectPtr conn, + const char *name); + +char * +fakeNetworkGetXMLDesc(virNetworkPtr network, + unsigned int noflags); + +virNetworkPortPtr +fakeNetworkPortCreateXML(virNetworkPtr net, + const char *xmldesc, + unsigned int noflags); + +char * +fakeNetworkPortGetXMLDesc(virNetworkPortPtr port, + unsigned int noflags); + +unsigned char * +fakeSecretGetValue(virSecretPtr obj, + size_t *value_size, + unsigned int fakeflags); + +virSecretPtr +fakeSecretLookupByUsage(virConnectPtr conn, + int usageType, + const char *usageID); + +virSecretPtr +fakeSecretLookupByUUID(virConnectPtr conn, + const unsigned char *uuid); #endif --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084600209808.4180842610176; Mon, 19 Aug 2024 09:23:20 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 0652816EF; Mon, 19 Aug 2024 12:23:18 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 85F7915FE; Mon, 19 Aug 2024 12:20:23 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 5FC471525; Mon, 19 Aug 2024 12:20:18 -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 126FF1570 for ; Mon, 19 Aug 2024 12:19:59 -0400 (EDT) Received: by mail-ed1-f42.google.com with SMTP id 4fb4d7f45d1cf-5beb6ea9ed6so4441384a12.1 for ; Mon, 19 Aug 2024 09:19:58 -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.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:55 -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=1724084397; x=1724689197; 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=H3C5iDEQvJuxJ0ZItThkuGdgj2D6zlT0IgW/3rdU2wU=; b=H+pou8V2e+39XsBZr5+tWcQICah+ZbD6l3krqySMz+OzG0Vm/fA+kYxHJctdcmewxm 95w+wRbcC2QOvqWR5b8kD6Q5jwuEA4pWcFedGgtTQYsBcAdHhSH8wY5mVaIRWPVyEBP6 xEwlKSHCnExG8sVbFJlhSkMehIM+HRRGBdw0fwMObVyO/IfgxYfhR7dgZ7LvxlS827Ad YdNI15FAOtaaBNti7vFqsronQGbYIwhVVOWVNV5o5mrllRUk1Ar83yI3ZIvRnvyrupQF 9EpktFHsAB6Za3RXvIA9ncp/6xcajfcHFCUtCS0YSFiJccZM/yWM43t2iJxZVGRirSxk ow+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084397; x=1724689197; 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=H3C5iDEQvJuxJ0ZItThkuGdgj2D6zlT0IgW/3rdU2wU=; b=C8gQWQiySS18RNV6P7Dloo375os/iqfMWTmhhjF0ByclevbEbLjJBC5Qu+2Ehv4zEJ 022xUrpxxMTaOdQLdyGf9ad6NJXRoPNQ5FFOe8+l3n92dio23Yb4RWvNsBcpZQrDCk2x lPgRVVCBfoW+Bm1gOWj1qO9EsLfbYzJwrXOnzxAHfZ/gq90oBnrTvtnV+Ezi0cc6Yd6t cxtpKNdr0wjUkZnj37eMiu2o1nec1ShwvgWewcaPDP2QhLQINurtY6CRMOG9SLfxyONg LHSeZRX4JmEgcXAbXam7VvYRMON21BnKgULX3omOYBxt5AMZ2BXF+xwY1ciFDdotfkjQ sZsg== X-Gm-Message-State: AOJu0Yyto0tNr21adcvjA93awy0tI8eSQMzlREv9+polOMrHSKtxpB1l ShMmQr+ExeyGKZMGbJjnnDQtcpnqO6TEEf1Wzvoul0o5jqzidPHVciG/uA== X-Google-Smtp-Source: AGHT+IFTmgsHDNQ2kTelhDKiccr6FOSvHqwTY8fwhLMAeoDgRX6X1ft2iA5oXUgPrdSNNE9deawq5g== X-Received: by 2002:a05:6402:84b:b0:5be:9bc5:f6b3 with SMTP id 4fb4d7f45d1cf-5beca4e23bamr7366941a12.2.1724084395864; Mon, 19 Aug 2024 09:19:55 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 04/14] schemas: Refactor relaxNG schema to ease protobuf conversion Date: Mon, 19 Aug 2024 21:39:42 +0530 Message-Id: <20240819160952.351383-5-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: MDW7XTUJEKU3JX44FZJ4JS3R2AXZDD6O X-Message-ID-Hash: MDW7XTUJEKU3JX44FZJ4JS3R2AXZDD6O 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: 1724084601925116600 Content-Type: text/plain; charset="utf-8" We make small modifications to the rng files to allow the relaxng-to-proto script to infer XML attribute types better. 1. Make iobase and irq refs so we can override their types in the script. 2. Replace deviceName with absFilePath so that vdpa devices get fuzzed corr= ectly with file paths. 3. The relaxng-to-proto script does not handle regexes, so convert some of = them to containing . Signed-off-by: Rayhan Faizel --- src/conf/schemas/basictypes.rng | 20 ++++++++++++++------ src/conf/schemas/domaincommon.rng | 11 +++++++---- src/conf/schemas/networkcommon.rng | 14 ++++++++------ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/conf/schemas/basictypes.rng b/src/conf/schemas/basictypes.= rng index 2931e316b7..db05c085fe 100644 --- a/src/conf/schemas/basictypes.rng +++ b/src/conf/schemas/basictypes.rng @@ -555,19 +555,27 @@ =20 + + + 0x[a-fA-F0-9]{1,4} + + + + + + 0x[a-fA-F0-9] + + + - - 0x[a-fA-F0-9]{1,4} - + - - 0x[a-fA-F0-9] - + diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincom= mon.rng index 05ba697924..d635d2b179 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -3670,7 +3670,7 @@ - + @@ -8495,9 +8495,12 @@ - - (vepa|bridge|private|passthrough) - + + vepa + bridge + private + passthrough + diff --git a/src/conf/schemas/networkcommon.rng b/src/conf/schemas/networkc= ommon.rng index 6df6d43f54..0254bac693 100644 --- a/src/conf/schemas/networkcommon.rng +++ b/src/conf/schemas/networkcommon.rng @@ -199,14 +199,16 @@ - - (tcp)|(udp) - + + tcp + udp + - - (ipv4)|(ipv6) - + + ipv4 + ipv6 + =20 --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 17240847070881004.1887571043392; Mon, 19 Aug 2024 09:25:07 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 139311525; Mon, 19 Aug 2024 12:25:06 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id EE972165F; Mon, 19 Aug 2024 12:20:26 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 61FAF1601; Mon, 19 Aug 2024 12:20:20 -0400 (EDT) Received: from mail-lj1-f179.google.com (mail-lj1-f179.google.com [209.85.208.179]) (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 5DDDB1647 for ; Mon, 19 Aug 2024 12:19:59 -0400 (EDT) Received: by mail-lj1-f179.google.com with SMTP id 38308e7fff4ca-2ef23d04541so54382531fa.2 for ; Mon, 19 Aug 2024 09:19:59 -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.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:56 -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,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=1724084398; x=1724689198; 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=kLTRrBtcHF2dvsJxcCLauGAexSPkfL50Ay5qDu/J3V4=; b=X3/XwHKquJyZnN0VqAcNq8uuVrc7hmOUUCZPin15CSiWgKwchXsG1md5tOwXpnpSs6 DQDOiab1j95YPNcYmpiYW215mQnL8Z6i9jAfGlEwwQ6TDJXmW82s4UOGIJPvcFzo4Q1J Nt/lgKgTPe3/FD8Jfgm/WH/t8R8MAD3Ud+tZI6GkEBPiMDbyPxQesXwM9x/pB2GUnCd6 HXc5qqxmY+sCHpMPyyu0mfypcV6bvdGxqHDTuxM4Y27l/fjfJnbrsRvznc9PSb3MstyR 8BwiYw+0m9s3NgG5tVuD6ZM91BgjWKBWAJL9IyxWQ79v3fhGA5xU1cE0l8Mc+UR7JlVU MrnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084398; x=1724689198; 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=kLTRrBtcHF2dvsJxcCLauGAexSPkfL50Ay5qDu/J3V4=; b=E7ICG3Ll0PoAtkcWvtShg3GJSI9gfPzfJX8HQG6rd5mXh9GQFw1UzEJfxLQ9xYam4O TIILgTKByFdFmCe6AVDmVIsDRtYbjI6mIAk4b+4hYSaEJ4K6M7pv3sarqfRgav28B8YA jN/GYXRvMpm3i1lmwCoQ5XSWaRfAvCmSs9TuqlSKxOMLb9d2zmAjRisJCbIXECUVvRKa k5UVoUQDZjcysc5ib49PyeEwOwaB62pt/SRJ/7Gq6sTYCRG2VRN8gd0Mnq9JwgygAxZN +mLNTANZUGCgnY/ZstZ0R1fAAjvcXrCLlvDoOXIBLvUPWfF7FWq2+Z4Rkz5+2ttWQtQ3 hgJg== X-Gm-Message-State: AOJu0YzSS5ZGFJaY4pwJAYpsSxPWNb6btH3bqyumUhcfLJDVBJFaO6Z3 KEZ7pj3dwN6E5jgml3/AkpOlXMxOTXdpZ0ZMOUgb8V+Kh15LF2m/m7Wh6A== X-Google-Smtp-Source: AGHT+IFeG8zCZin2ujq++TYvJmH+dWAoAJ4+0CfEzgIlb6cqoyx+JgUPLnxPzpk44vx0wZ3aAD7IAQ== X-Received: by 2002:a05:651c:211a:b0:2ef:20ae:d116 with SMTP id 38308e7fff4ca-2f3be3df1bdmr83325701fa.0.1724084397178; Mon, 19 Aug 2024 09:19:57 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 05/14] scripts: Add script to convert relaxNG to protobuf Date: Mon, 19 Aug 2024 21:39:43 +0530 Message-Id: <20240819160952.351383-6-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: ROHW6VHAGEMRGYKTRU3AL67VAMGVKWJB X-Message-ID-Hash: ROHW6VHAGEMRGYKTRU3AL67VAMGVKWJB 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: 1724084708394116600 Content-Type: text/plain; charset="utf-8" This script converts relaxNG schemas to an equivalent protobuf definition file. The script captures the general structure of the XML schema and tries to guess the attribute datatypes.=20 The protobuf definitions give the fuzzers awareness of the XML schema. The protobuf files will be used by the fuzzers to mutate protobuf data and serialize them to XML. Signed-off-by: Rayhan Faizel --- build-aux/syntax-check.mk | 1 + scripts/meson.build | 1 + scripts/relaxng-to-proto.py | 505 ++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 scripts/relaxng-to-proto.py diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk index 0759372b2b..a60e4a8082 100644 --- a/build-aux/syntax-check.mk +++ b/build-aux/syntax-check.mk @@ -844,6 +844,7 @@ http_sites +=3D www.inkscape.org http_sites +=3D www.innotek.de http_sites +=3D www.w3.org http_sites +=3D xmlns +http_sites +=3D relaxng.org =20 # Links in licenses http_sites +=3D scripts.sil.org diff --git a/scripts/meson.build b/scripts/meson.build index 2798e302ab..7249346e45 100644 --- a/scripts/meson.build +++ b/scripts/meson.build @@ -32,6 +32,7 @@ scripts =3D [ 'mock-noinline.py', 'prohibit-duplicate-header.py', 'qemu-replies-tool.py', + 'relaxng-to-proto.py', ] =20 foreach name : scripts diff --git a/scripts/relaxng-to-proto.py b/scripts/relaxng-to-proto.py new file mode 100644 index 0000000000..f13d6f7e40 --- /dev/null +++ b/scripts/relaxng-to-proto.py @@ -0,0 +1,505 @@ +#!/usr/bin/env python3 + +import re +import sys +import xml.etree.ElementTree as ET +import argparse + +# Track XML tree objects of all tags +define_table =3D {} + +# Store parsed tree of tag +define_trees =3D {} + +relaxng_ns =3D "{http://relaxng.org/ns/structure/1.0}" + +integer_refs =3D ["positiveInteger", "unsignedInt", "uint8", "uint16", "ui= nt24", "uint32", "hexuint"] +integer_datatypes =3D ["positiveInteger", "unsignedInt", "int", "long", "u= nsignedLong", "integer"] + +#Override attribute based on ref +custom_ref_table =3D { + "virYesNo": {"type": "bool"}, + "virOnOff": {"type": "Switch"}, + "ipAddr": {"type": "IPAddr"}, + "ipv4Addr": {"type": "IPAddr"}, + "ipv6Addr": {"type": "IPAddr"}, + "diskTargetDev": {"type": "TargetDev"}, + "UUID": {"type": "DummyUUID"}, + "usbIdDefault": {"values": ["-1",]}, + "usbClass": {"type": "uint32"}, + "usbId": {"type": "uint32"}, + "usbVersion": {"type": "uint32"}, + "usbAddr": {"type": "uint32"}, + "usbPort": {"type": "uint32"}, + "virtioserialPort": {"type": "uint32"}, + "timeDelta": {"type": "uint32"}, + "absFilePath": {"type": "DummyPath"}, + "filePath": {"type": "DummyPath"}, + "absDirPath": {"type": "DummyPath"}, + "dirPath": {"type": "DummyPath"}, + "cpuset": {"type": "CPUSet"}, + "pciSlot": {"type": "uint32"}, + "pciFunc": {"type": "uint32"}, + "ccidSlot": {"type": "uint32"}, + "ccwCssidRange": {"type": "uint32"}, + "ccwSsidRange": {"type": "uint32"}, + "ccwDevnoRange": {"type": "uint32"}, + "driveController": {"type": "uint32"}, + "driveBus": {"type": "uint32"}, + "driveSCSITarget": {"type": "uint32"}, + "driveUnit": {"type": "uint32"}, + "irq": {"type": "uint32"}, + "iobase": {"type": "uint32"}, + "uniMacAddr": {"type": "MacAddr"}, + } + +net_model_names =3D ["virtio", "virtio-transitional", "virtio-non-transiti= onal", "e1000", "e1000e", "igb", + "rtl8139", "netfront", "usb-net", "spapr-vlan", "lan911= 8", "scm91c111", "vlance", "vmxnet", + "vmxnet2", "vmxnet3", "Am79C970A", "Am79C973", "82540EM= ", "82545EM", "82543GC"] + +# Override attribute based on paths +attr_path_table =3D { + "domain.devices.interface.model.type": {"values": net_= model_names}, + } + +# Tag paths end with a dot while attributes don't. +xml_modify_path =3D { + "domain.devices.smartcard.certificate.": {"repeated": = True} + } + +def tree_add_tag(tree, element_name, set_repeat): + if "tags" not in tree: + tree["tags"] =3D {} + + if element_name not in tree["tags"]: + tree["tags"][element_name] =3D {} + + if set_repeat: + tree["tags"][element_name]["repeated"] =3D True + +def tree_add_attribute(tree, child, attrib_name, path): + if "attributes" not in tree: + tree["attributes"] =3D {} + + if attrib_name not in tree["attributes"]: + tree["attributes"][attrib_name] =3D {} + + if "type_list" not in tree["attributes"][attrib_name]: + tree["attributes"][attrib_name]["type_list"] =3D [] + + if "value_list" not in tree["attributes"][attrib_name]: + tree["attributes"][attrib_name]["value_list"] =3D [] + + parse_attribute(child, attrib_name, tree["attributes"][attrib_name], p= ath + attrib_name) + +def parse_datatype(root): + datatype =3D root.attrib["type"] + if datatype in integer_datatypes: + return "uint32" + else: + return "DummyString" + +# Parse , which may point to either a primitive data type, a type +# defined in custom_ref_table or simply an XML tree pointer. +def parse_ref_node(ref_node): + ref_tree =3D define_table[ref_node.attrib["name"]] + ref_name =3D ref_node.attrib["name"] + + if ref_name in integer_refs: + return {"type": "uint32"} + elif ref_name in custom_ref_table: + ref_type =3D custom_ref_table[ref_name] + return ref_type + else: + return {"tree": ref_tree} + +def add_to_attr_list(l, new_val): + if new_val not in l: + l.append(new_val) + +# Map custom ref table entries to appropriate values in value_list +# and type_list of an attribute tree. +def add_ref_type_to_attr_lists(attr_type, value_list, type_list): + if "types" in attr_type: + for type_name in attr_type["types"]: + add_to_attr_list(type_list, type_name) + elif "type" in attr_type: + add_to_attr_list(type_list, attr_type["type"]) + + if "values" in attr_type: + for value in attr_type["values"]: + add_to_attr_list(value_list, value) + +# Parse inside . +# +# may contain one or more , or tags +def parse_attribute_choices(attribute_node, value_list, type_list): + for value_node in attribute_node: + if (value_node.tag =3D=3D relaxng_ns + "value"): + value =3D value_node.text + add_to_attr_list(value_list, value) + elif (value_node.tag =3D=3D relaxng_ns + "ref"): + ref_parse =3D parse_ref_node(value_node) + + if "tree" not in ref_parse: + add_ref_type_to_attr_lists(ref_parse, value_list, type_lis= t) + else: + ref_tree =3D ref_parse["tree"] + parse_attribute_choices(ref_tree, value_list, type_list) + elif (value_node.tag =3D=3D relaxng_ns + "data"): + datatype =3D parse_datatype(value_node) + add_to_attr_list(type_list, datatype) + elif (value_node.tag =3D=3D relaxng_ns + "text"): + add_to_attr_list(type_list, "DummyString") + else: + parse_attribute_choices(value_node, value_list, type_list) + +# Parse and generate an attribute tree +# +# An attribute tree consists of: +# 1. 'type_list': List of data types (Eg: uint32, string, etc.) +# 2. 'value_list': List of enum values. +# +# type_list and value_list can be extended further throughout the +# parsing of the XML tree. +def parse_attribute(root, attribute_name, attribute_tree, path): + type_list =3D attribute_tree["type_list"] + value_list =3D attribute_tree["value_list"] + + if path in attr_path_table: + add_ref_type_to_attr_lists(attr_path_table[path], value_list, type= _list) + return + + if (len(root) =3D=3D 0): + # If there is nothing in , assuming string. + add_to_attr_list(type_list, "DummyString") + return + + attribute_node =3D root[0] + + if attribute_node.tag =3D=3D relaxng_ns + "value": + # Single corresponds to mono-valued enum + value =3D attribute_node.text + add_to_attr_list(value_list, value) + elif attribute_node.tag =3D=3D relaxng_ns + "choice": + # Parse + parse_attribute_choices(attribute_node, value_list, type_list) + elif attribute_node.tag =3D=3D relaxng_ns + "data": + # Primitive datatypes can be mapped to protobuf types directly + data_type =3D parse_datatype(attribute_node) + add_to_attr_list(type_list, data_type) + elif attribute_node.tag =3D=3D relaxng_ns + "ref": + ref_name =3D attribute_node.attrib["name"] + ref_parse =3D parse_ref_node(attribute_node) + if "tree" not in ref_parse: + add_ref_type_to_attr_lists(ref_parse, value_list, type_list) + else: + # Recurse into ref + parse_attribute(define_table[ref_name], attribute_name, attrib= ute_tree, path) + return + elif attribute_node.tag =3D=3D relaxng_ns + "text": + # is simply a generic string + add_to_attr_list(type_list, "DummyString") + else: + # We should never reach here + raise ValueError(f"Attribute {attribute_name} has unknown datatype= ") + +# Store XML text node data +def initialize_text_tree(tree): + if "text" not in tree: + tree["text"] =3D {"value_list": [], "type_list": []} + +# Parse and store data in intermediate tree. +# +# An intermediate tree will consist of +# 1. 'tags': List of nested tag trees, which may contain other tags or att= ributes. +# 2. 'attributes': List of attribute trees +# 3. 'text': Similar in structure to attribute tree, representing an XML t= ext node. +def parse_define(root, tree, ref_traverse, path=3D"", set_repeat=3DFalse): + if path in xml_modify_path: + # TODO: Allow overriding more stuff when required + xml_modify_path_entry =3D xml_modify_path[path] + if "repeated" in xml_modify_path_entry: + tree["repeated"] =3D xml_modify_path_entry["repeated"] + + for child in root: + tag =3D child.tag + attrib =3D child.attrib + + # Handle tags which will be represented as T_ fields in + # the protobuf. + if tag =3D=3D relaxng_ns + "element": + if "name" not in attrib: + continue + + element_name =3D attrib["name"] + + tree_add_tag(tree, element_name, set_repeat) + + parse_define(child, tree["tags"][element_name], ref_traverse, = path + element_name + ".") + + # Handle tags which will be represented as A_ fields in + # the protobuf. + elif tag =3D=3D relaxng_ns + "attribute": + attrib_name =3D attrib["name"] + + tree_add_attribute(tree, child, attrib_name, path) + + # points to another which is recursively traversed. + elif tag =3D=3D relaxng_ns + "ref": + ref_name =3D attrib["name"] + + # If ref encapsulates datatype, generate V_ field instead of t= raversing inside + ref_parse =3D parse_ref_node(child) + if ("tree" not in ref_parse): + initialize_text_tree(tree) + add_ref_type_to_attr_lists(ref_parse, tree["text"]["value_= list"], tree["text"]["type_list"]) + continue + + # Handle infinitely recursive refs + if (define_table[ref_name] in ref_traverse): + pass + else: + parse_define(define_table[ref_name], tree, ref_traverse + = [define_table[ref_name]], path, set_repeat) + + # If or is used, + # immediate elements under it will have 'repeated' specifier in the + # final protobuf. + elif tag =3D=3D relaxng_ns + "oneOrMore" or tag =3D=3D relaxng_ns = + "zeroOrMore": + parse_define(child, tree, ref_traverse, path, True) + + # , or residing outside of are + # XML text nodes, represented by V_ fields. + elif tag =3D=3D relaxng_ns + "value": + initialize_text_tree(tree) + add_to_attr_list(tree["text"]["value_list"], child.text) + elif tag =3D=3D relaxng_ns + "data": + initialize_text_tree(tree) + add_to_attr_list(tree["text"]["type_list"], parse_datatype(chi= ld)) + elif tag =3D=3D relaxng_ns + "text": + initialize_text_tree(tree) + add_to_attr_list(tree["text"]["type_list"], "DummyString") + else: + parse_define(child, tree, ref_traverse, path, set_repeat) + +# Find all tags and store them to resolve tags later +# +# Also parse all tags in order to add more tags to the +# table +def get_defines(schema_path): + schema_tree =3D ET.parse(schema_path) + root =3D schema_tree.getroot() + + for child in root: + tag =3D child.tag + attrib =3D child.attrib + + if tag =3D=3D relaxng_ns + "start": + define_table["rng_entrypoint"] =3D child + if tag =3D=3D relaxng_ns + "define": + define_name =3D attrib["name"] + define_table[define_name] =3D child + elif tag =3D=3D relaxng_ns + "include": + include_href =3D attrib["href"] + get_defines(f"../src/conf/schemas/{include_href}") + +def padding(text, level): + return " " * level * 4 + text + +# Generate enum protobuf +def enum_to_proto(tree, level, scope): + proto =3D "" + enum_index =3D 0 + restricted_words =3D ["unix", "linux"] + for value in tree["values"]: + formatted_value =3D re.sub("[^a-zA-Z0-9_]", "_", value) + + if re.match("^[0-9]", formatted_value): + formatted_value =3D "_" + formatted_value + + if formatted_value in restricted_words: + formatted_value =3D "const_" + formatted_value + + while formatted_value in scope: + formatted_value =3D "_" + formatted_value + + proto +=3D padding(f"{formatted_value} =3D {enum_index}", level) + + if formatted_value !=3D value: + proto +=3D f" [(real_value) =3D '{value}'];\n" + else: + proto +=3D ";\n" + + scope.add(formatted_value) + enum_index +=3D 1 + + return proto + +# Generate oneof protobuf containing multiple protobuf fields. +def oneof_to_proto(tree, attribute, protobuf_index, level, proto_opt, scop= e): + proto =3D "" + if "enum" in tree["types"]: + proto +=3D padding(f"enum {attribute}Enum {{\n", level) + proto +=3D enum_to_proto(tree["types"]["enum"], level + 1, scope) + proto +=3D padding("}\n", level) + + optnum =3D 0 + proto +=3D padding(f"oneof {attribute}Option {{\n", level) + for datatype in tree["types"]: + if datatype =3D=3D "enum": + datatype =3D f"{attribute}Enum" + proto +=3D padding(f"{datatype} A_OPT{str(optnum).zfill(2)}_{attri= bute} =3D {protobuf_index}{proto_opt};\n", level + 1) + protobuf_index +=3D 1 + optnum +=3D 1 + + proto +=3D padding(f"}}\n", level) + + return (proto, protobuf_index - 1) + +# Given an attribute tree with type_list and value_list, +# determine how the protobuf field must be generated, i.e +# what field type it is and if it can take on multiple types. +def generate_attribute_type(attribute_tree): + result =3D {} + + type_list =3D attribute_tree["type_list"] + value_list =3D attribute_tree["value_list"] + # Number of data types possible for an attribute + # (enum values count as an additional type) + type_count =3D len(type_list) + (1 if len(value_list) > 0 else 0) + + if type_count =3D=3D 1: + if len(type_list) =3D=3D 1: + result["type"] =3D type_list[0] + elif len(value_list) > 0: + result["type"] =3D "enum" + result["values"] =3D value_list + else: + # If there are more than two data types for the attribute, + # it should be oneof in the protobuf. + result["type"] =3D "oneof" + result["types"] =3D {} + for datatype in type_list: + result["types"][datatype] =3D {"type": datatype} + + if (len(value_list) > 0): + result["types"]["enum"] =3D {"type": "enum", "values": value_l= ist} + + return result + +# Convert intermediate tree to protobuf +def define_tree_to_proto(tree, level): + tags =3D tree.get("tags", {}) + attributes =3D tree.get("attributes", {}) + content_type =3D tree.get("content_type", None) + + # Due to how protobuf scoping works, we can't have the same enum ideni= tifers + # under the same message. We need to keep track of the scope ourselves. + current_scope =3D set() + + proto =3D "" + protobuf_index =3D 1 + + for attribute in attributes: + renamed_attr =3D attribute + proto_opt =3D "" + if re.search("[^a-zA-Z0-9_]", attribute): + renamed_attr =3D re.sub("[^a-zA-Z0-9_]", "_", attribute) + proto_opt =3D f" [(real_name) =3D '{attribute}']" + + attribute_type =3D generate_attribute_type(attributes[attribute]) + datatype =3D attribute_type["type"] + + if datatype =3D=3D "oneof": + new_proto, new_index =3D oneof_to_proto(attribute_type, rename= d_attr, protobuf_index, level, proto_opt, current_scope) + proto +=3D new_proto + protobuf_index =3D new_index + elif datatype =3D=3D "enum": + proto +=3D padding(f"enum {renamed_attr}Enum {{\n", level) + proto +=3D enum_to_proto(attribute_type, level + 1, current_sc= ope) + proto +=3D padding("}\n", level) + proto +=3D padding(f"optional {renamed_attr}Enum A_{renamed_at= tr} =3D {protobuf_index}{proto_opt};\n", level) + else: + proto +=3D padding(f"optional {datatype} A_{renamed_attr} =3D = {protobuf_index}{proto_opt};\n", level) + + protobuf_index +=3D 1 + + protobuf_tag_index =3D 10000 + + if "text" in tree: + # Note that if both V_ and T_ fields are present, V_ will be favo= ured + # if its presence returns true (since it's optional), otherwise T_= fields + # will be used. + text_tree =3D tree["text"] + text_type =3D generate_attribute_type(text_tree) + datatype =3D text_type["type"] + + if datatype =3D=3D "oneof": + print("WARN: oneof of V_ not yet supported!") + elif datatype =3D=3D "enum": + proto +=3D padding(f"enum ValueEnum {{\n", level) + proto +=3D enum_to_proto(text_type, level + 1, current_scope) + proto +=3D padding("}\n", level) + proto +=3D padding(f"optional ValueEnum V_value =3D {protobuf_= tag_index};\n", level) + else: + proto +=3D padding(f"optional {datatype} V_value =3D {protobuf= _tag_index};\n", level) + + protobuf_tag_index +=3D 1 + + for tag in tags: + renamed_tag =3D tag + proto_opt =3D "" + if re.search("[^a-zA-Z0-9_]", tag): + renamed_tag =3D re.sub("[^a-zA-Z0-9_]", "_", tag) + proto_opt +=3D f" [(real_name) =3D '{tag}']" + + proto +=3D padding(f"message {renamed_tag}Tag {{\n", level) + proto +=3D define_tree_to_proto(tags[tag], level + 1) + proto +=3D padding("}\n", level) + + specifier =3D "optional" + if (tags[tag].get("repeated", False)): + specifier =3D "repeated" + + if level !=3D 0: + proto +=3D padding(f"{specifier} {renamed_tag}Tag T_{renamed_t= ag} =3D {protobuf_tag_index}{proto_opt};\n", level) + + protobuf_tag_index +=3D 1 + + return proto + +parser =3D argparse.ArgumentParser(formatter_class=3Dargparse.RawDescripti= onHelpFormatter, + description=3D"RelaxNG schema to protobuf= converter") + +parser.add_argument('rngfile', help=3D'Specify .rng file to process') + +parser.add_argument('protofile', help=3D'Specify .proto file to output') + +parser.add_argument('--defines', nargs=3D'*', default=3D[ 'rng_entrypoint'= ], + help=3D'Specify defines to be converted to equivalent = protobuf messages',) + +args =3D parser.parse_args() + +allowed_defines =3D args.defines +infile =3D args.rngfile +outfile =3D args.protofile + +get_defines(infile) + +for define_name in allowed_defines: + define_trees[define_name] =3D {} + parse_define(define_table[define_name], define_trees[define_name], []) + +prologue =3D """\ +syntax =3D 'proto2'; +package libvirt; + +import 'xml_datatypes.proto'; +""" + +with open(outfile, "w") as out_file: + out_file.write(prologue) + + for define_name in allowed_defines: + out_file.write(define_tree_to_proto(define_trees[define_name], 0)) + out_file.write("\n") --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084794684148.9218295955776; Mon, 19 Aug 2024 09:26:34 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 52189161D; Mon, 19 Aug 2024 12:26:26 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 9B51C146E; Mon, 19 Aug 2024 12:20:28 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 76ECA1459; Mon, 19 Aug 2024 12:20:21 -0400 (EDT) Received: from mail-ed1-f47.google.com (mail-ed1-f47.google.com [209.85.208.47]) (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 7DCE015BE for ; Mon, 19 Aug 2024 12:20:00 -0400 (EDT) Received: by mail-ed1-f47.google.com with SMTP id 4fb4d7f45d1cf-5bec7ee6f44so3967340a12.0 for ; Mon, 19 Aug 2024 09:20:00 -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.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:19:58 -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=1724084399; x=1724689199; 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=MuJfop0yQJ6CoS9dJtFL0hhW5+dFtj+ff/M0fQyEPqo=; b=ces3JFpp+GAnzByjkw829T4pIQ7PSV6cBs2o1RX1gR0F19vzLNbSgMiP7mqSbpN3QF sQ30yR01k2xju2qypIPao7mImhgF26FHrTKZWH483oQOQ8UYMD5JhARZZEFxmUtYqFVX M540LY/0UVcNf1utrcHfqQ/4K1dnfp5Ykou2DqWdOGkz0VSZSPhOKxTfzIxEScFQ0vOP GmcBHNML7wdVJIIyBRuEDFF3/EuqrK4VXCjEPe/AcrqZvKR0muz/l3r1UQHGKY8HTR+d Mpi2u30iIx6E8LoMXS34eXs2sjZ9lAXEI4LXND8NuSUwIsCM+A014YEB10R5mZoR4DqH STTg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084399; x=1724689199; 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=MuJfop0yQJ6CoS9dJtFL0hhW5+dFtj+ff/M0fQyEPqo=; b=r7QEjfDVKAOQpXmMBgBl/DY49Y2zsFBwdZpxF8GH9RX7mQ83SmaTeGQi9dIIdOzqYU artouf1ryzan6wZKIMhMbiWmknsC1s24nMhnRSFWn9hOeOoDcajRMOtZVt/YkALUHaQ/ iE2ln5kqAdXPovHLCPpdC/3zKom5A7TGNJSszU6Rx7YqI1Du4MnHZGOmFZ0N/Vmu0ier 2ZpR2Kd59sVSWLdMcHmVFsUpl9PT1UN+hkSpFQu6TEvhsqZs+t+TyR889+PFRjQnsqLj Vdnge1G5wCZfUKEoFD+ze7kjgeL2VzBFO2Yr6qGq57KjLTfxqiZlXI244cST1WVw75tx z3ug== X-Gm-Message-State: AOJu0YykgC88OuCF7s/vBLCzACyvR4EneqvxXcU/kdYN+4Is6AtYUdNH kqyjNq81zjFDXNDObqnrhMU0vflWd2eypDKQJXq6yxL8Pc0L+vKTJN/xnA== X-Google-Smtp-Source: AGHT+IHEbcNRZZVoqw+oCuP0J5wDrtJaGIGBBtwAmPf9U3LnKC8pxmKXCdC/JOlBQFPiDaaVU+VUhQ== X-Received: by 2002:a05:6402:1ec3:b0:5a1:83c4:c5a8 with SMTP id 4fb4d7f45d1cf-5beca592b86mr7734259a12.14.1724084398615; Mon, 19 Aug 2024 09:19:58 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 06/14] fuzz: Implement base fuzzing setup for XML domain Date: Mon, 19 Aug 2024 21:39:44 +0530 Message-Id: <20240819160952.351383-7-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: F2RGGE53GGQ6TPAURSSCZBN4I2KPJKWW X-Message-ID-Hash: F2RGGE53GGQ6TPAURSSCZBN4I2KPJKWW 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: 1724084796641116600 Content-Type: text/plain; charset="utf-8" This patch sets up most of the baseline code which will be used by various fuzzers as well as the build setup. proto_to_xml.cc includes logic to convert protobuf to XML. It does in a generalized fashion by using reflection APIs to recursively iterate through all protobuf fields and generating XML tags, attributes or text based on th= eir identifiers and field types. This way, different protobuf definitions can be used with minimal modifications. proto_custom_datatypes.cc includes certain attribute datatypes that cannot = be encapsulated by existing protobuf datatypes, so we implement various handle= rs to handle their XML serialization. run_fuzz is a helper script which sets up necessary libFuzzer flags and environment variables before executing the actual fuzzer binaries. Three protobuf definitions are provided which serve as a base for all XML domain fuzzers to follow: 1. xml_domain: Most of the XML domain format 2. xml_domain_disk_only: Only the definition 3. xml_domain_interface_only: Only the definition Signed-off-by: Rayhan Faizel --- meson.build | 44 ++++ meson_options.txt | 2 + tests/fuzz/meson.build | 59 +++++ tests/fuzz/proto_custom_datatypes.cc | 146 +++++++++++++ tests/fuzz/proto_custom_datatypes.h | 30 +++ tests/fuzz/proto_header_common.h | 43 ++++ tests/fuzz/proto_to_xml.cc | 205 ++++++++++++++++++ tests/fuzz/proto_to_xml.h | 28 +++ tests/fuzz/protos/meson.build | 36 +++ tests/fuzz/protos/xml_datatypes.proto | 72 ++++++ tests/fuzz/protos/xml_domain.proto | 62 ++++++ tests/fuzz/protos/xml_domain_disk_only.proto | 21 ++ .../protos/xml_domain_interface_only.proto | 21 ++ tests/fuzz/run_fuzz.in | 122 +++++++++++ tests/meson.build | 5 + 15 files changed, 896 insertions(+) create mode 100644 tests/fuzz/meson.build create mode 100644 tests/fuzz/proto_custom_datatypes.cc create mode 100644 tests/fuzz/proto_custom_datatypes.h create mode 100644 tests/fuzz/proto_header_common.h create mode 100644 tests/fuzz/proto_to_xml.cc create mode 100644 tests/fuzz/proto_to_xml.h create mode 100644 tests/fuzz/protos/meson.build create mode 100644 tests/fuzz/protos/xml_datatypes.proto create mode 100644 tests/fuzz/protos/xml_domain.proto create mode 100644 tests/fuzz/protos/xml_domain_disk_only.proto create mode 100644 tests/fuzz/protos/xml_domain_interface_only.proto create mode 100644 tests/fuzz/run_fuzz.in diff --git a/meson.build b/meson.build index 904524abc6..abc67e0062 100644 --- a/meson.build +++ b/meson.build @@ -2153,6 +2153,49 @@ if get_option('test_coverage_clang') endif =20 =20 +# fuzzing + +if get_option('fuzz').enabled() + add_languages('cpp') + cxx =3D meson.get_compiler('cpp') + + libprotobuf_mutator_dep =3D dependency('libprotobuf-mutator') + + if get_option('external_protobuf_dir') !=3D '' + external_protobuf_dir =3D get_option('external_protobuf_dir') + protoc_prog =3D find_program(external_protobuf_dir / 'bin' / 'protoc') + + protobuf_inc =3D external_protobuf_dir / 'include' + protobuf_libdir =3D external_protobuf_dir / 'lib' + + protobuf_list_cmd =3D run_command('find', protobuf_libdir, '-name', '*= .a', '-printf', '%f\n', check: true) + protobuf_lib_files =3D protobuf_list_cmd.stdout().strip().split('\n') + + protobuf_libs =3D [] + + foreach lib: protobuf_lib_files + protobuf_libs +=3D cxx.find_library(lib.substring(3, -2), dirs: prot= obuf_libdir) + endforeach + + protobuf_dep =3D declare_dependency( + dependencies: [ protobuf_libs ], + include_directories: [ protobuf_inc ], + ) + else + protobuf_dep =3D dependency('protobuf') + protoc_prog =3D find_program('protoc') + endif + + if cc.has_argument('-fsanitize=3Dfuzzer') and cc.has_argument('-fsanitiz= e=3Dfuzzer-no-link') + add_project_arguments('-fsanitize=3Dfuzzer-no-link', language: [ 'c', = 'cpp' ]) + else + error('Your compiler does not support libFuzzer') + endif + + conf.set('WITH_FUZZ', 1) +endif + + # Various definitions =20 # Python3 < 3.7 treats the C locale as 7-bit only. We must force env vars = so @@ -2389,6 +2432,7 @@ summary(win_summary, section: 'Windows', bool_yn: tru= e) test_summary =3D { 'Expensive': use_expensive_tests, 'Coverage': coverage_flags.length() > 0, + 'Fuzzing': conf.has('WITH_FUZZ'), } summary(test_summary, section: 'Test suite', bool_yn: true) =20 diff --git a/meson_options.txt b/meson_options.txt index 153e325cb5..b961192ee5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -15,6 +15,8 @@ option('rpath', type: 'feature', value: 'auto', descripti= on: 'whether to include option('docdir', type: 'string', value: '', description: 'documentation in= stallation directory') option('docs', type: 'feature', value: 'auto', description: 'whether to ge= nerate documentation') option('tests', type: 'feature', value: 'auto', description: 'whether to b= uild and run tests') +option('fuzz', type: 'feature', value: 'auto', description: 'whether to bu= ild and run fuzzing engines') +option('external_protobuf_dir', type: 'string', value: '', description: 'e= xternal protobuf directory for fuzzer compilation') =20 =20 # build dependencies options diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build new file mode 100644 index 0000000000..d0488c3e36 --- /dev/null +++ b/tests/fuzz/meson.build @@ -0,0 +1,59 @@ +fuzz_dep =3D declare_dependency( + dependencies: [ + tests_dep, + libprotobuf_mutator_dep, + protobuf_dep, + ], + include_directories: [ + test_dir, + ], + ) + +subdir('protos') + +available_fuzzers =3D '' +xml_fuzzers =3D [] + +fuzz_autogen_xml_domain_lib =3D static_library( + 'fuzz_autogen_xml_domain_lib', + [ + autogen_xml_domain_proto_src, + xml_datatypes_proto_src, + 'proto_custom_datatypes.cc', + ], + dependencies: [ fuzz_dep ], +) + +fuzz_autogen_xml_domain_dep =3D declare_dependency( + link_whole: [ fuzz_autogen_xml_domain_lib ], + include_directories: [ + fuzz_autogen_xml_domain_lib.private_dir_include(), + ] +) + +foreach fuzzer: xml_fuzzers + xml_domain_fuzz =3D executable(fuzzer['name'], + fuzzer['src'], + dependencies: [ fuzz_dep, fuzzer['deps'] ], + cpp_args: [ '-fsanitize=3Dfuzzer', fuzzer['macro'] ], + link_args: [ '-fsanitize=3Dfuzzer', libvirt_no_indirect ], + link_with: fuzzer['libs'], + ) + + available_fuzzers +=3D '"' + fuzzer['name'] + '"' + ',' +endforeach + +run_conf =3D configuration_data({ + 'abs_builddir': meson.project_build_root(), + 'available_fuzzers': available_fuzzers, + 'sanitizers': get_option('b_sanitize'), + 'coverage_clang': get_option('test_coverage_clang'), +}) + +configure_file( + input: 'run_fuzz.in', + output: '@BASENAME@', + configuration: run_conf, +) + +run_command('chmod', 'a+x', meson.current_build_dir() / 'run_fuzz', check:= true) diff --git a/tests/fuzz/proto_custom_datatypes.cc b/tests/fuzz/proto_custom= _datatypes.cc new file mode 100644 index 0000000000..d89a6d4f59 --- /dev/null +++ b/tests/fuzz/proto_custom_datatypes.cc @@ -0,0 +1,146 @@ +/* + * proto_custom_datatypes.cc: Custom protobuf-based datatype handlers + * + * 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 "xml_datatypes.pb.h" +#include "port/protobuf.h" + +#include + +using google::protobuf::Message; +using google::protobuf::Reflection; +using google::protobuf::EnumDescriptor; + +typedef std::string (*typeHandlerPtr)(const Message &); +std::string emulator =3D ""; + +static +std::string convertCPUSet(const Message &message) +{ + std::string value; + const libvirt::CPUSet &cpuset =3D (libvirt::CPUSet &) message; + uint64_t bitmap =3D cpuset.bitmap(); + + int cpu =3D 0; + for (uint8_t bit =3D bitmap & 1; bitmap !=3D 0; bitmap =3D bitmap >> 1= , bit =3D bitmap & 1, cpu++) + { + if (bit) { + if (value.empty()) + value +=3D std::to_string(cpu); + else + value +=3D "," + std::to_string(cpu); + } + } + + return value; +} + + +static +std::string convertEmulatorString(const Message &message) +{ + (void) message; /* Silence warning about unused parameter */ + return emulator; +} + + +static +std::string convertIPAddr(const Message &message) { + char value[64] =3D {0}; + const libvirt::IPAddr &ip_addr =3D (libvirt::IPAddr &) message; + + if (ip_addr.ipv6() =3D=3D true) { + sprintf(value, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", + (uint16_t) ip_addr.lo_qword() & 0xffff, + (uint16_t) (ip_addr.lo_qword() >> 16) & 0xffff, + (uint16_t) (ip_addr.lo_qword() >> 32) & 0xffff, + (uint16_t) (ip_addr.lo_qword() >> 48) & 0xffff, + (uint16_t) ip_addr.hi_qword() & 0xffff, + (uint16_t) (ip_addr.hi_qword() >> 16) & 0xffff, + (uint16_t) (ip_addr.hi_qword() >> 32) & 0xffff, + (uint16_t) (ip_addr.hi_qword() >> 48) & 0xffff); + } else { + sprintf(value, "%d.%d.%d.%d", + (uint8_t) ip_addr.lo_qword() & 0xff, + (uint8_t) (ip_addr.lo_qword() >> 8) & 0xff, + (uint8_t) (ip_addr.lo_qword() >> 16) & 0xff, + (uint8_t) (ip_addr.lo_qword() >> 24) & 0xff); + } + + return std::string(value); +} + + +static +std::string convertMacAddr(const Message &message) { + char value[64] =3D {0}; + const libvirt::MacAddr &mac_addr =3D (libvirt::MacAddr &) message; + + sprintf(value, "%02x:%02x:%02x:%02x:%02x:%02x", + (uint8_t) mac_addr.qword() & 0xff, + (uint8_t) (mac_addr.qword() >> 8) & 0xff, + (uint8_t) (mac_addr.qword() >> 16) & 0xff, + (uint8_t) (mac_addr.qword() >> 24) & 0xff, + (uint8_t) (mac_addr.qword() >> 32) & 0xff, + (uint8_t) (mac_addr.qword() >> 40) & 0xff); + + return std::string(value); +} + + +static +std::string convertDiskTarget(const Message &message) +{ + std::string value; + const libvirt::TargetDev &target_dev =3D (libvirt::TargetDev &) messag= e; + const EnumDescriptor *enum_desc =3D libvirt::TargetDev_TargetDevEnum_d= escriptor(); + std::string prefix =3D enum_desc->FindValueByNumber(target_dev.prefix(= ))->name(); + + value =3D prefix + "a"; + + return value; +} + + +std::unordered_map type_handler_table =3D { + {"libvirt.CPUSet", convertCPUSet}, + {"libvirt.EmulatorString", convertEmulatorString}, + {"libvirt.IPAddr", convertIPAddr}, + {"libvirt.MacAddr", convertMacAddr}, + {"libvirt.TargetDev", convertDiskTarget}, +}; + + +void handleCustomDatatype(const Message &message, + std::string tag, + bool is_attr, + std::string &xml) +{ + std::string type_name =3D message.GetTypeName(); + std::string value =3D ""; + + if (type_handler_table.find(type_name) !=3D type_handler_table.end()) { + value =3D type_handler_table[type_name](message); + + if (is_attr) + xml +=3D " " + tag + "=3D" + "'" + value + "'"; + else + xml +=3D value; + } +} diff --git a/tests/fuzz/proto_custom_datatypes.h b/tests/fuzz/proto_custom_= datatypes.h new file mode 100644 index 0000000000..9446b6235b --- /dev/null +++ b/tests/fuzz/proto_custom_datatypes.h @@ -0,0 +1,30 @@ +/* + * proto_custom_datatypes.h: Custom protobuf-based datatype handlers + * + * 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 + * . + */ + +#pragma once + +#include "port/protobuf.h" + +using google::protobuf::Message; + +void handleCustomDatatype(const Message &message, + std::string tag, + bool is_attr, + std::string &xml); diff --git a/tests/fuzz/proto_header_common.h b/tests/fuzz/proto_header_com= mon.h new file mode 100644 index 0000000000..5ee510896d --- /dev/null +++ b/tests/fuzz/proto_header_common.h @@ -0,0 +1,43 @@ +/* + * proto_header_common.h: Protobuf header selection and common code + * + * 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 + * . + */ + +#pragma once + +#pragma GCC diagnostic ignored "-Wignored-attributes" +#pragma GCC diagnostic ignored "-Wextern-c-compat" + +#ifdef XML_DOMAIN +#include "xml_domain.pb.h" +#endif + +#ifdef XML_DOMAIN_DISK_ONLY +#include "xml_domain_disk_only.pb.h" +#endif + +#ifdef XML_DOMAIN_INTERFACE_ONLY +#include "xml_domain_interface_only.pb.h" +#endif + + +#define FUZZ_COMMON_INIT(...) \ + if (virErrorInitialize() < 0) \ + exit(EXIT_FAILURE); \ + if (g_getenv("LPM_EXE_PATH")) \ + virFileActivateDirOverrideForProg(g_getenv("LPM_EXE_PATH")); diff --git a/tests/fuzz/proto_to_xml.cc b/tests/fuzz/proto_to_xml.cc new file mode 100644 index 0000000000..13393ecb34 --- /dev/null +++ b/tests/fuzz/proto_to_xml.cc @@ -0,0 +1,205 @@ +/* + * proto_to_xml.cc: Protobuf to XML converter + * + * 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 "proto_header_common.h" +#include "port/protobuf.h" +#include "proto_custom_datatypes.h" + + +using google::protobuf::Message; +using google::protobuf::Descriptor; +using google::protobuf::FieldDescriptor; +using google::protobuf::EnumValueDescriptor; +using google::protobuf::OneofDescriptor; +using google::protobuf::Reflection; + + +static void +convertProtoFieldToXML(const Message &message, + const FieldDescriptor *field, + std::string tag, + bool is_attr, + std::string &xml) +{ + const Reflection *reflection =3D message.GetReflection(); + const EnumValueDescriptor *enum_desc; + std::string value =3D ""; + std::string alternate_value; + bool value_set =3D false; + + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + { + const Message &field_message =3D reflection->GetMessage(messag= e, field); + handleCustomDatatype(field_message, tag, is_attr, xml); + return; + } + break; + case FieldDescriptor::CPPTYPE_ENUM: + enum_desc =3D reflection->GetEnum(message, field); + + /* Use extension to fill certain XML enum values containing sp= ecial characters */ + alternate_value =3D enum_desc->options().GetExtension(libvirt:= :real_value); + + if (!alternate_value.empty()) { + value =3D alternate_value; + } else { + value =3D enum_desc->name(); + } + + value_set =3D true; + break; + case FieldDescriptor::CPPTYPE_BOOL: + value =3D reflection->GetBool(message, field) ? "yes" : "no"; + value_set =3D true; + break; + case FieldDescriptor::CPPTYPE_UINT32: + value =3D std::to_string(reflection->GetUInt32(message, field)= ); + value_set =3D true; + break; + case FieldDescriptor::CPPTYPE_UINT64: + value =3D std::to_string(reflection->GetUInt64(message, field)= ); + value_set =3D true; + break; + case FieldDescriptor::CPPTYPE_STRING: + value =3D reflection->GetString(message, field); + value_set =3D true; + break; + default: + break; + } + + if (value_set) { + if (is_attr) + xml +=3D " " + tag + "=3D'" + value + "'"; + else + xml +=3D value; + } +} + + +static int +convertProtoToXMLInternal(const Message &message, + std::string &xml, + bool root) +{ + const Descriptor *descriptor =3D message.GetDescriptor(); + const Reflection *reflection =3D message.GetReflection(); + size_t field_count =3D descriptor->field_count(); + int children =3D 0; + bool opening_tag_close =3D false; + size_t i, j; + size_t repeated_size =3D 0; + + for (i =3D 0; i < field_count; i++) { + const FieldDescriptor *field =3D descriptor->field(i); + std::string field_name =3D field->name(); + std::string tag =3D field_name.substr(2, std::string::npos); + char field_type =3D field_name[0]; + std::string alternate_name =3D field->options().GetExtension(libvi= rt::real_name); + + /* Fields inside oneof are prefixed with A_OPTXX_ */ + if (tag.substr(0, 3) =3D=3D "OPT") + tag =3D tag.substr(6, std::string::npos); + + /* Use extension to generate XML tags with names having special ch= aracters */ + if (!alternate_name.empty()) + tag =3D alternate_name; + + if (!field->is_repeated() && !reflection->HasField(message, field)= ) { + continue; + } + + switch (field_type) { + /* Handle XML attributes/properties */ + case 'A': + /* Attributes must come before inner tags in the protobuf defi= nition */ + assert(!opening_tag_close); + + convertProtoFieldToXML(message, field, tag, true, xml); + break; + + /* Handle inner tags of current XML tag */ + case 'T': + /* We don't want to accidentally close off the tag on empty re= peated fields */ + if (field->is_repeated()) { + repeated_size =3D reflection->FieldSize(message, field); + if (repeated_size =3D=3D 0) + continue; + } + + if (!opening_tag_close && !root) { + xml +=3D ">\n"; + opening_tag_close =3D true; + } + + if (field->is_repeated()) { + for (j =3D 0; j < repeated_size; j++) { + const Message &field_message =3D reflection->GetRepeat= edMessage(message, field, j); + children +=3D 1; + xml +=3D "<" + tag; + int inner_children =3D convertProtoToXMLInternal(field= _message, xml, false); + if (inner_children =3D=3D 0) { + xml +=3D "/>\n"; + } else { + xml +=3D "\n"; + } + } + } else { + const Message &field_message =3D reflection->GetMessage(me= ssage, field); + children +=3D 1; + xml +=3D "<" + tag; + int inner_children =3D convertProtoToXMLInternal(field_mes= sage, xml, false); + + if (inner_children =3D=3D 0) { + xml +=3D "/>\n"; + } else { + xml +=3D "\n"; + } + } + break; + + /* Handle XML text nodes. Eg: (text here) */ + case 'V': + if (!opening_tag_close && !root) { + xml +=3D ">"; + opening_tag_close =3D true; + } + assert(children =3D=3D 0); + children =3D 1; + + convertProtoFieldToXML(message, field, "", false, xml); + + return children; + + default: + break; + } + } + + return children; +} + + +void convertProtoToXML(const libvirt::MainObj &message, + std::string &xml) +{ + convertProtoToXMLInternal(message, xml, true); +} diff --git a/tests/fuzz/proto_to_xml.h b/tests/fuzz/proto_to_xml.h new file mode 100644 index 0000000000..7fe9597a19 --- /dev/null +++ b/tests/fuzz/proto_to_xml.h @@ -0,0 +1,28 @@ +/* + * proto_to_xml.h: Protobuf to XML converter + * + * 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 + * . + */ + +#pragma once + +#include "proto_header_common.h" +#include "port/protobuf.h" + + +void convertProtoToXML(const libvirt::MainObj &message, + std::string &xml); diff --git a/tests/fuzz/protos/meson.build b/tests/fuzz/protos/meson.build new file mode 100644 index 0000000000..42c3a7f6a9 --- /dev/null +++ b/tests/fuzz/protos/meson.build @@ -0,0 +1,36 @@ +protos =3D [ + 'xml_datatypes.proto', + 'xml_domain.proto', + 'xml_domain_disk_only.proto', + 'xml_domain_interface_only.proto', +] + +autogen_proto_xml_domain_proto =3D custom_target('autogen_xml_domain.proto= ', + output : 'autogen_xml_domain.proto', + input : meson.project_source_root() / 'src' / 'conf' / 'schemas' / 'doma= in.rng', + command : [relaxng_to_proto_prog, '@INPUT@', '@OUTPUT@'], +) + +protoc_generator =3D generator(protoc_prog, + output: [ + '@BASENAME@.pb.cc', + '@BASENAME@.pb.h', + ], + arguments: [ + '--proto_path=3D@CURRENT_SOURCE_DIR@', + '--proto_path=3D@BUILD_ROOT@/tests/fuzz/protos/', + '--cpp_out=3D@BUILD_DIR@', + '@INPUT@', + ], + depends: [ + autogen_proto_xml_domain_proto, + ], +) + +autogen_xml_domain_proto_src =3D protoc_generator.process(autogen_proto_xm= l_domain_proto) + +foreach proto: protos + proto_src_name =3D proto.split('.')[0].underscorify() + proto_src =3D protoc_generator.process(proto) + set_variable('@0@_proto_src'.format(proto_src_name), proto_src) +endforeach diff --git a/tests/fuzz/protos/xml_datatypes.proto b/tests/fuzz/protos/xml_= datatypes.proto new file mode 100644 index 0000000000..1229b9810f --- /dev/null +++ b/tests/fuzz/protos/xml_datatypes.proto @@ -0,0 +1,72 @@ +syntax =3D "proto2"; +package libvirt; + +import "google/protobuf/descriptor.proto"; + +/* + * Use EnumValueOptions extension to insert XML attribute values that do + * contain characters outside [A-Za-z0-9_] + */ + +extend google.protobuf.EnumValueOptions { + optional string real_value =3D 10000; +} + +/* + * Use FieldOptions extension to create XML tags/attributes + * with identifiers containing characters outside [A-Za-z0-9_] + */ + +extend google.protobuf.FieldOptions { + optional string real_name =3D 10000; +} + +enum Switch { + on =3D 0; + off =3D 1; +} + +/* If we use string, it could blow up the size of the protobuf in-memory */ +enum DummyString { + dummy =3D 0; + dummy2 =3D 1; + dummy3 =3D 2; + dummy4 =3D 3; +} + +enum DummyUUID { + dummy_uuid =3D 0 [(real_value) =3D "df6fdea1-10c3-474c-ae62-e63def80de0b= "]; +} + +message IPAddr { + required bool ipv6 =3D 1; + required uint64 lo_qword =3D 2; + required uint64 hi_qword =3D 3; +} + +message MacAddr { + required uint64 qword =3D 1; +} + +message TargetDev { + enum TargetDevEnum { + hd =3D 0; + sd =3D 1; + vd =3D 2; + xvd =3D 3; + ubd =3D 4; + } + required TargetDevEnum prefix =3D 1; +} + +enum DummyPath { + devnull =3D 0 [(real_value) =3D "/dev/null"]; + devzero =3D 1 [(real_value) =3D "/dev/zero"]; + invalidpath =3D 2 [(real_value) =3D "/this/path/doesnt/exist"]; +} + +message CPUSet { + required uint64 bitmap =3D 1; +} + +message EmulatorString {} diff --git a/tests/fuzz/protos/xml_domain.proto b/tests/fuzz/protos/xml_dom= ain.proto new file mode 100644 index 0000000000..9720af70d3 --- /dev/null +++ b/tests/fuzz/protos/xml_domain.proto @@ -0,0 +1,62 @@ +syntax =3D "proto2"; + +import "autogen_xml_domain.proto"; +import "xml_datatypes.proto"; + +package libvirt; + +/* + * container tag + * For more effective coverage, it might be a good idea to comment + * out some protobuf fields and fuzz only a few devices at a time. + */ + +message devicesTag { + repeated domainTag.devicesTag.soundTag T_sound =3D 1; + repeated domainTag.devicesTag.filesystemTag T_filesystem =3D 2; + repeated domainTag.devicesTag.inputTag T_input =3D 3; + repeated domainTag.devicesTag.diskTag T_disk =3D 4; + repeated domainTag.devicesTag.interfaceTag T_interface =3D 5; + repeated domainTag.devicesTag.graphicsTag T_graphics =3D 6; + repeated domainTag.devicesTag.serialTag T_serial =3D 7; + repeated domainTag.devicesTag.parallelTag T_parallel =3D 8; + repeated domainTag.devicesTag.channelTag T_channel =3D 9; + repeated domainTag.devicesTag.consoleTag T_console =3D 10; + repeated domainTag.devicesTag.controllerTag T_controller =3D 11; + repeated domainTag.devicesTag.videoTag T_video =3D 12; + repeated domainTag.devicesTag.rngTag T_rng =3D 13; + repeated domainTag.devicesTag.watchdogTag T_watchdog =3D 14; + repeated domainTag.devicesTag.memballoonTag T_memballoon =3D 15; + repeated domainTag.devicesTag.smartcardTag T_smartcard =3D 16; + repeated domainTag.devicesTag.redirdevTag T_redirdev =3D 17; + repeated domainTag.devicesTag.audioTag T_audio =3D 18; + repeated domainTag.devicesTag.cryptoTag T_crypto =3D 19; + repeated domainTag.devicesTag.panicTag T_panic =3D 20; + repeated domainTag.devicesTag.tpmTag T_tpm =3D 21; + repeated domainTag.devicesTag.shmemTag T_shmem =3D 22; + repeated domainTag.devicesTag.hostdevTag T_hostdev =3D 23; + repeated domainTag.devicesTag.leaseTag T_lease =3D 24; + repeated domainTag.devicesTag.redirfilterTag T_redirfilter =3D 25; + repeated domainTag.devicesTag.iommuTag T_iommu =3D 26; + repeated domainTag.devicesTag.vsockTag T_vsock =3D 27; + repeated domainTag.devicesTag.nvramTag T_nvram =3D 28; + + message EmulatorTag { + required EmulatorString V_value =3D 1; + } + required EmulatorTag T_emulator =3D 1000; +} + +/* Full XML Domain */ + +message MainObj { + optional domainTag.clockTag T_clock =3D 1; + optional domainTag.vcpuTag T_vcpu =3D 2; + optional domainTag.vcpusTag T_vcpus =3D 3; + optional domainTag.cpuTag T_cpu =3D 4; + optional domainTag.cputuneTag T_vcputune =3D 5; + optional domainTag.memtuneTag T_memtune =3D 6; + optional domainTag.blkiotuneTag T_blkiotune =3D 7; + + required devicesTag T_devices =3D 1000; +} diff --git a/tests/fuzz/protos/xml_domain_disk_only.proto b/tests/fuzz/prot= os/xml_domain_disk_only.proto new file mode 100644 index 0000000000..a6af43003c --- /dev/null +++ b/tests/fuzz/protos/xml_domain_disk_only.proto @@ -0,0 +1,21 @@ +syntax =3D "proto2"; + +import "autogen_xml_domain.proto"; +import "xml_datatypes.proto"; + +package libvirt; + +message devicesTag { + repeated domainTag.devicesTag.diskTag T_disk =3D 4; + + message EmulatorTag { + required EmulatorString V_value =3D 1; + } + required EmulatorTag T_emulator =3D 1000; +} + +/* Full XML Domain */ + +message MainObj { + required devicesTag T_devices =3D 1000; +} diff --git a/tests/fuzz/protos/xml_domain_interface_only.proto b/tests/fuzz= /protos/xml_domain_interface_only.proto new file mode 100644 index 0000000000..3bac94bf79 --- /dev/null +++ b/tests/fuzz/protos/xml_domain_interface_only.proto @@ -0,0 +1,21 @@ +syntax =3D "proto2"; + +import "autogen_xml_domain.proto"; +import "xml_datatypes.proto"; + +package libvirt; + +message devicesTag { + repeated domainTag.devicesTag.interfaceTag T_interface =3D 5; + + message EmulatorTag { + required EmulatorString V_value =3D 1; + } + required EmulatorTag T_emulator =3D 1000; +} + +/* Full XML Domain */ + +message MainObj { + required devicesTag T_devices =3D 1000; +} diff --git a/tests/fuzz/run_fuzz.in b/tests/fuzz/run_fuzz.in new file mode 100644 index 0000000000..da3c7935b7 --- /dev/null +++ b/tests/fuzz/run_fuzz.in @@ -0,0 +1,122 @@ +#!/usr/bin/env python3 + +import argparse +import os +import sys + +available_fuzzers =3D [ @available_fuzzers@ ] +abs_builddir =3D "@abs_builddir@" +fuzz_dir =3D f"{abs_builddir}/tests/fuzz/" +llvm_symbolizer_path =3D f"{fuzz_dir}/llvm-symbolizer-wrapper" + +sanitizers =3D "@sanitizers@" +coverage_clang =3D @coverage_clang@ + +parser =3D argparse.ArgumentParser(formatter_class=3Dargparse.RawDescripti= onHelpFormatter, + description=3D"Fuzzing Helper") + +parser.add_argument('fuzzer', + help=3D'Select fuzzer binary to run', + choices=3Davailable_fuzzers) + +parser.add_argument('--testcase', nargs=3D'+', + help=3D'Individual test case') + +parser.add_argument('--corpus', + help=3D'Corpus Directory') + +parser.add_argument('--dump-xml', action=3D'store_true', + help=3D'Log XML test cases') + +parser.add_argument('--format-xml', action=3D'store_true', + help=3D'Run XML domain format functions') + +parser.add_argument('--arch', + help=3D'XML domain architecture to fuzz') + +parser.add_argument('--llvm-profile-file', + help=3D'Collect coverage report') + +parser.add_argument('--total-time', type=3Dint, + help=3D'Fuzz for specific amount of time (in seconds)') + +parser.add_argument('--timeout', type=3Dint, + help=3D'Max duration for individual test case (in seco= nds)') + +parser.add_argument('-j', '--jobs', type=3Dint, default=3D1, + help=3D'Run parallel fuzzers') + +parser.add_argument('--parallel-mode', choices=3D["jobs", "fork"], default= =3D"jobs", + help=3D'Set parallel fuzzing method') + +parser.add_argument('--use-value-profile', action=3D'store_true', + help=3D'Use value profile') + +parser.add_argument('--libfuzzer-options', + help=3D'Pass additional raw libFuzzer flags') + +args =3D parser.parse_args() + +process_args =3D [fuzz_dir + args.fuzzer] +env =3D os.environ + +if args.corpus and args.testcase: + print("--testcase and --corpus can't be used together") + sys.exit(1) + +if args.corpus: + process_args.extend([args.corpus]) + +if args.testcase: + process_args.extend(args.testcase) + +if args.dump_xml: + env["LPM_XML_DUMP_INPUT"] =3D "YES" + +if args.format_xml: + env["LPM_XML_FORMAT_ENABLE"] =3D "YES" + +if args.arch: + env["LPM_FUZZ_ARCH"] =3D args.arch + +if args.llvm_profile_file: + if not coverage_clang: + print("Please recompile your build with -Dtest_coverage_clang=3Dtr= ue") + sys.exit(1) + env["LLVM_PROFILE_FILE"] =3D args.llvm_profile_file + + if not args.corpus: + print("Coverage report requires --corpus. If you do not have a cor= pus, do normal fuzzing to prepare one.") + sys.exit(1) + + process_args.extend(["-runs=3D0"]) + +if args.total_time: + process_args.extend([f"-max_total_time=3D{str(args.total_time)}"]) + +if args.timeout: + process_args.extend([f"-timeout=3D{str(args.timeout)}"]) + +if args.use_value_profile: + process_args.extend(["-use_value_profile=3D1"]) + +if args.jobs > 1: + if args.parallel_mode =3D=3D "fork": + process_args.extend([f"-fork=3D{args.jobs}", "-ignore_crashes=3D1"= , "-ignore_ooms=3D1", "-ignore_timeouts=3D1"]) + elif args.parallel_mode =3D=3D "jobs": + process_args.extend([f"-jobs=3D{args.jobs}", f"-workers=3D{args.jo= bs}"]) + +exe_path =3D fuzz_dir + args.fuzzer + +# If libvirt is not installed in system dir, some fuzzers (QEMU, etc.) wil= l fail +env["LPM_EXE_PATH"] =3D exe_path + +process_args.extend(["-print_funcs=3D-1"]) + +if args.libfuzzer_options: + process_args.extend([x for x in args.libfuzzer_options.split(' ') if x= !=3D '']) + +print("Selected fuzzer:", args.fuzzer) +print("Active sanitizers:", sanitizers) + +os.execvpe(exe_path, process_args, env) diff --git a/tests/meson.build b/tests/meson.build index 2f1eda1f95..18743ac45e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -34,6 +34,7 @@ tests_dep =3D declare_dependency( ), ) =20 +test_dir =3D include_directories('.') tests_env =3D [ 'abs_builddir=3D@0@'.format(meson.current_build_dir()), 'abs_srcdir=3D@0@'.format(meson.current_source_dir()), @@ -750,6 +751,10 @@ endforeach testenv =3D runutf8 testenv +=3D 'VIR_TEST_FILE_ACCESS=3D1' =20 +if conf.has('WITH_FUZZ') + subdir('fuzz') +endif + add_test_setup( 'access', env: testenv, --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 From nobody Sat Nov 23 15:01:47 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 1724084898164898.9929173142983; Mon, 19 Aug 2024 09:28:18 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 136B71279; Mon, 19 Aug 2024 12:28:17 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id C7D9F16E0; Mon, 19 Aug 2024 12:20:33 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id E45E41677; Mon, 19 Aug 2024 12:20:25 -0400 (EDT) Received: from mail-ed1-f51.google.com (mail-ed1-f51.google.com [209.85.208.51]) (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 1F4B415E2 for ; Mon, 19 Aug 2024 12:20:03 -0400 (EDT) Received: by mail-ed1-f51.google.com with SMTP id 4fb4d7f45d1cf-5bed83487aeso3217914a12.2 for ; Mon, 19 Aug 2024 09:20:03 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:01 -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=1724084402; x=1724689202; 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=PBUUJnlan3sjIrjy4IqGTJaPH1Xt96XJBmHknOhRulw=; b=GcQeCZW4xR6d5FsjYXWSUB9eudZndx84fIdGuo6GCE5YN4fQFpOyQVPtdSW+Hi8Wwq jV4P0CPSbGSm4t/0jaPGAzseiKfrNXctL48kffx4+hj3hfuNCRkXjEOkZNBVmW698OUQ W9P5n3d9Y/aBGRRvYrvq+YvEQahupYm8E7jkDoyePjjpjsBR1KdplCGMoGVXdvoA57Bj D0JsvOWxaMGpHbzD8HGYma9r7rtfvxLhtFWkU4sIpQ4uN26iJnNx1/idXPQuv8mkNYL9 lVOTBXABWU+xGzmhUVmqu2qLLUuxrVVjiaiMlSHOQOfv5dHKO+GtMJFomITYol/jKDvO 5nfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084402; x=1724689202; 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=PBUUJnlan3sjIrjy4IqGTJaPH1Xt96XJBmHknOhRulw=; b=QbNqjoBB2ttYGU3+wEmJIosI6F/0+2dDmiutTHTW8LXfwRNDDPVPV1/6CiPiXRRxHO USIsQnpMQmf+y1+eH6L2O/mFX+oejdJXGCuvq6tGJ7PufMCXLAA6M2QW1aFgBRXRFTeR wgmMokDtBb4vERCx+zdiz0lS2MDG+JXrHym8KVLEY5yrTbura5001MBSb5YuQ+2OqZyl jgGSDD9vB3vBWo15FE/wB4FLzMR7Z6mNAxxYAlmN6v80nsK34o+dNGdvHLzOboMVNQs2 3W60oU49o+DYSmbZV6bKctZE1LJi7S0jzUiMpGZxrZEneFn1nrOq8CGR9dbNGER2VzcS /1Hg== X-Gm-Message-State: AOJu0YxqxY+dpQaBCZk5c7FPANjFRRNYUHNT/TvLcbuppXMkv/jpQHov y0yyL6Ktp2sN5ZUMlKIZ5WdRcfJ8PJ1iKedulouToNCEmtldAWq8IFqeOw== X-Google-Smtp-Source: AGHT+IFJijcux2d9lvNUFh38NLzPgIYNGjaZiBdqBFOBY+fCGaRTpH+zfvmqkuKqj3ICqqiG6JOaYw== X-Received: by 2002:aa7:c54d:0:b0:5a4:6dec:cd41 with SMTP id 4fb4d7f45d1cf-5beca76e4ebmr6422578a12.28.1724084401410; Mon, 19 Aug 2024 09:20:01 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 08/14] fuzz: Implement QEMU XML hotplug fuzzer Date: Mon, 19 Aug 2024 21:39:46 +0530 Message-Id: <20240819160952.351383-9-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: QSYKOO4XBG7EMVO5MS4HVXA77FP7ZWB5 X-Message-ID-Hash: QSYKOO4XBG7EMVO5MS4HVXA77FP7ZWB5 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: 1724084898919116600 Content-Type: text/plain; charset="utf-8" This patch implements the harness for hotplugging devices under the QEMU driver. The fuzzer exercises both attachment and detachment of various devices. It uses its own proto file xml_hotplug to fuzz only one device type at a time. Monitor setup is done such that QMP commands are processed indefinitely and removal wait times are eliminated. A table of dummy responses is referred to for generating QMP responses. A semi-hardcoded domain XML is used to hotplug and unhotplug device XMLs against it. It consists of various controllers to allow successful hotplugg= ing. Depending on the architecture selected, some controllers may be omitted or modified. LeakSanitizer slows down the fuzzer severely, so we disable it in run_fuzz for now. Signed-off-by: Rayhan Faizel --- src/conf/domain_conf.h | 2 + src/qemu/qemu_hotplug.c | 4 + tests/fuzz/meson.build | 6 + tests/fuzz/proto_header_common.h | 4 + tests/fuzz/protos/meson.build | 1 + tests/fuzz/protos/xml_hotplug.proto | 38 ++++ tests/fuzz/qemu_xml_hotplug_fuzz.cc | 340 ++++++++++++++++++++++++++++ tests/fuzz/run_fuzz.in | 5 + tests/qemumonitortestutils.c | 48 ++++ tests/qemumonitortestutils.h | 6 + 10 files changed, 454 insertions(+) create mode 100644 tests/fuzz/protos/xml_hotplug.proto create mode 100644 tests/fuzz/qemu_xml_hotplug_fuzz.cc diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 3e97dd6293..1327604fbd 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -3288,6 +3288,8 @@ struct _virDomainObj { int taint; size_t ndeprecations; char **deprecations; + + bool fuzz; }; =20 G_DEFINE_AUTOPTR_CLEANUP_FUNC(virDomainObj, virObjectUnref); diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 75b97cf736..a4a5a32346 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5455,6 +5455,10 @@ qemuDomainWaitForDeviceRemoval(virDomainObj *vm) qemuDomainObjPrivate *priv =3D vm->privateData; unsigned long long until; =20 + /* Skip the wait entirely if fuzzing is active */ + if (vm->fuzz) + return 1; + if (virTimeMillisNow(&until) < 0) return 1; until +=3D qemuDomainGetUnplugTimeout(vm); diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 12f9a719f2..9579f56749 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -37,6 +37,11 @@ if conf.has('WITH_QEMU') 'proto_to_xml.cc', ] =20 + hotplug_src =3D [ + 'qemu_xml_hotplug_fuzz.cc', + 'proto_to_xml.cc', + ] + qemu_libs =3D [ test_qemu_driver_lib, test_utils_lib, @@ -48,6 +53,7 @@ if conf.has('WITH_QEMU') { '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 ] }, + { 'name': 'qemu_xml_hotplug_fuzz', 'src': [ hotplug_src, xml_hotplug_p= roto_src ], 'libs': [ qemu_libs, test_utils_qemu_monitor_lib ], 'macro': '-= DXML_HOTPLUG', 'deps': [ fuzz_autogen_xml_domain_dep ] }, ] endif =20 diff --git a/tests/fuzz/proto_header_common.h b/tests/fuzz/proto_header_com= mon.h index 5ee510896d..3f135c48e1 100644 --- a/tests/fuzz/proto_header_common.h +++ b/tests/fuzz/proto_header_common.h @@ -35,6 +35,10 @@ #include "xml_domain_interface_only.pb.h" #endif =20 +#ifdef XML_HOTPLUG +#include "xml_hotplug.pb.h" +#endif + =20 #define FUZZ_COMMON_INIT(...) \ if (virErrorInitialize() < 0) \ diff --git a/tests/fuzz/protos/meson.build b/tests/fuzz/protos/meson.build index 42c3a7f6a9..0731ef1eca 100644 --- a/tests/fuzz/protos/meson.build +++ b/tests/fuzz/protos/meson.build @@ -3,6 +3,7 @@ protos =3D [ 'xml_domain.proto', 'xml_domain_disk_only.proto', 'xml_domain_interface_only.proto', + 'xml_hotplug.proto', ] =20 autogen_proto_xml_domain_proto =3D custom_target('autogen_xml_domain.proto= ', diff --git a/tests/fuzz/protos/xml_hotplug.proto b/tests/fuzz/protos/xml_ho= tplug.proto new file mode 100644 index 0000000000..0490b2fdb6 --- /dev/null +++ b/tests/fuzz/protos/xml_hotplug.proto @@ -0,0 +1,38 @@ +syntax =3D "proto2"; + +import "autogen_xml_domain.proto"; + +package libvirt; + +message MainObj { + oneof new_device { + domainTag.devicesTag.soundTag T_sound =3D 1; + domainTag.devicesTag.filesystemTag T_filesystem =3D 2; + domainTag.devicesTag.inputTag T_input =3D 3; + domainTag.devicesTag.diskTag T_disk =3D 4; + domainTag.devicesTag.interfaceTag T_interface =3D 5; + domainTag.devicesTag.graphicsTag T_graphics =3D 6; + domainTag.devicesTag.serialTag T_serial =3D 7; + domainTag.devicesTag.parallelTag T_parallel =3D 8; + domainTag.devicesTag.channelTag T_channel =3D 9; + domainTag.devicesTag.consoleTag T_console =3D 10; + domainTag.devicesTag.controllerTag T_controller =3D 11; + domainTag.devicesTag.videoTag T_video =3D 12; + domainTag.devicesTag.rngTag T_rng =3D 13; + domainTag.devicesTag.watchdogTag T_watchdog =3D 14; + domainTag.devicesTag.memballoonTag T_memballoon =3D 15; + domainTag.devicesTag.smartcardTag T_smartcard =3D 16; + domainTag.devicesTag.redirdevTag T_redirdev =3D 17; + domainTag.devicesTag.audioTag T_audio =3D 18; + domainTag.devicesTag.cryptoTag T_crypto =3D 19; + domainTag.devicesTag.panicTag T_panic =3D 20; + domainTag.devicesTag.tpmTag T_tpm =3D 21; + domainTag.devicesTag.shmemTag T_shmem =3D 22; + domainTag.devicesTag.hostdevTag T_hostdev =3D 23; + domainTag.devicesTag.leaseTag T_lease =3D 24; + domainTag.devicesTag.redirfilterTag T_redirfilter =3D 25; + domainTag.devicesTag.iommuTag T_iommu =3D 26; + domainTag.devicesTag.vsockTag T_vsock =3D 27; + domainTag.devicesTag.nvramTag T_nvram =3D 28; + } +} diff --git a/tests/fuzz/qemu_xml_hotplug_fuzz.cc b/tests/fuzz/qemu_xml_hotp= lug_fuzz.cc new file mode 100644 index 0000000000..6e391c1f51 --- /dev/null +++ b/tests/fuzz/qemu_xml_hotplug_fuzz.cc @@ -0,0 +1,340 @@ +/* + * qemu_xml_hotplug_fuzz.cc: QEMU hotplug 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 "qemu/qemu_alias.h" +#include "qemu/qemu_conf.h" +#include "qemu/qemu_hotplug.h" +#include "qemumonitortestutils.h" +#include "testutils.h" +#include "testutilsqemu.h" +#include "testutilsqemuschema.h" +#include "virhostdev.h" +#include "virfile.h" +} + +#include "port/protobuf.h" +#include "proto_to_xml.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +#define QEMU_HOTPLUG_FUZZ_DOMAIN_ID 7 + +uint64_t device_parse_pass =3D 0; +uint64_t device_attach_pass =3D 0; +uint64_t success_pass =3D 0; +uint64_t detach_success_pass =3D 0; + +bool enable_xml_dump =3D false; + +typedef struct { + virQEMUCaps *caps; + GHashTable *schema; +} qemuFuzzHotplugData; + +#define QMP_OK "{\"return\": {}}" +#define QMP_EMPTY_ARRAY "{\"return\": []}" + +std::string getBaseDomainXML(std::string arch) { + std::string result =3D "\n" + " MyGuest\n" + " 4dea22b3-1d52-d8f3-2516-782e98ab3fa0\n" + " \n" + " hvm\n" + " \n" + " 4096\n" + " \n"; + + if (arch =3D=3D "aarch64" || arch =3D=3D "armv7l" || + arch =3D=3D "riscv64" || + arch =3D=3D "loongarch64") { + result +=3D "\n"; + } else { + result +=3D "\n"; + } + + /* s390x does not support USB */ + if (arch !=3D "s390x") { + result +=3D "\n"; + result +=3D "\n"; + } + + /* SATA is not supported on s390x and SPARC */ + if (arch !=3D "s390x" && arch !=3D "sparc") + result +=3D "\n"; + + result +=3D "\n"; + result +=3D "\n"; + + result +=3D "/usr/bin/qemu-system-" + arch + "\n" + "\n" + "\n"; + + return result; +} + + +std::string getDeviceDeletedResponse(std::string dev) { + std::string result =3D "{" \ + " \"timestamp\": {" \ + " \"seconds\": 1374137171," \ + " \"microseconds\": 2659" \ + " }," \ + " \"event\": \"DEVICE_DELETED\"," \ + " \"data\": {" \ + " \"device\": \"" + dev + "\"," \ + " \"path\": \"/machine/peripheral/" + dev + "\"" \ + " }" \ + "}\r\n"; + + return result; +} + + +/* Table of QMP commands and dummy responses */ +std::unordered_map qmp_cmd_table =3D { + {"device_add", QMP_OK}, + {"object-add", QMP_OK}, + {"object-del", QMP_OK}, + {"netdev_add", QMP_OK}, + {"netdev_del", QMP_OK}, + {"chardev-add", QMP_OK}, + {"chardev-remove", QMP_OK}, + {"blockdev-add", QMP_OK}, + {"blockdev-del", QMP_OK}, + {"qom-list", QMP_EMPTY_ARRAY}, + {"query-block", QMP_EMPTY_ARRAY}, + {"query-fds", QMP_EMPTY_ARRAY}, + {"set-action", QMP_OK}, + {"set_link", QMP_OK}, + {"query-fdsets", QMP_EMPTY_ARRAY}, + {"add-fd", "{ \"return\": { \"fdset-id\": 1, \"fd\": 95 }}"}, + {"block_set_io_throttle", QMP_OK}, +}; + + +char *qemuMonitorFuzzGetResponse(const char *command, + virJSONValue *cmdargs) +{ + if (STREQ(command, "device_del")) { + const char *id =3D virJSONValueObjectGetString(cmdargs, "id"); + std::string result =3D getDeviceDeletedResponse(id); + + return g_strdup(result.c_str()); + } + + if (qmp_cmd_table.find(command) !=3D qmp_cmd_table.end()) { + return g_strdup(qmp_cmd_table[command].c_str()); + } + + /* If QMP command is unknown, assume QMP_OK and warn the user of the s= ame. */ + printf("[FUZZ WARN]: Unknown QMP command: %s\n", command); + + return g_strdup(QMP_OK); +} + + +static void +fuzzXMLHotplug(virQEMUDriver *driver, + qemuFuzzHotplugData *hotplugData, + const char *domain_xml_string, + const char *device_xml_string) +{ + virDomainObj *vm =3D NULL; + virDomainDef *def =3D NULL; + virDomainDeviceDef *dev =3D NULL; + virDomainDeviceDef *remove_dev =3D NULL; + qemuDomainObjPrivate *priv =3D NULL; + g_autoptr(qemuMonitorTest) test_mon =3D NULL; + bool attach_success =3D false; + + if (!(def =3D virDomainDefParseString(domain_xml_string, driver->xmlop= t, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE))) { + + printf("Failed to parse domain XML!\n"); + exit(EXIT_FAILURE); + } + + if (!(vm =3D virDomainObjNew(driver->xmlopt))) + goto cleanup; + + vm->def =3D def; + priv =3D (qemuDomainObjPrivate *) vm->privateData; + priv->qemuCaps =3D hotplugData->caps; + + if (qemuDomainAssignAddresses(vm->def, hotplugData->caps, + driver, vm, true) < 0) { + goto cleanup; + } + + if (qemuAssignDeviceAliases(vm->def) < 0) + goto cleanup; + + vm->def->id =3D QEMU_HOTPLUG_FUZZ_DOMAIN_ID; + vm->fuzz =3D true; + + if (qemuDomainSetPrivatePaths(driver, vm) < 0) + goto cleanup; + + device_parse_pass++; + if (!(dev =3D virDomainDeviceDefParse(device_xml_string, vm->def, + driver->xmlopt, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE))) + goto cleanup; + + /* Initialize test monitor + * + * Keep it after virDomainDeviceDefParse to avoid wasting time with mo= nitor + * creation. + */ + + if (!(test_mon =3D qemuMonitorTestNew(driver->xmlopt, vm, NULL, hotplu= gData->schema))) + goto cleanup; + + /* Enable fuzzing mode of the test monitor */ + qemuMonitorTestFuzzSetup(test_mon, qemuMonitorFuzzGetResponse); + + priv->mon =3D qemuMonitorTestGetMonitor(test_mon); + + virObjectUnlock(priv->mon); + + device_attach_pass++; + + if (qemuDomainAttachDeviceLive(vm, dev, driver) =3D=3D 0) { + success_pass++; + attach_success =3D true; + } + + if (attach_success) { + /* The previous virDomainDeviceDefParse cleared out the data in de= v, so + * we need to reparse it before doing the detachment. + */ + remove_dev =3D virDomainDeviceDefParse(device_xml_string, vm->def, + driver->xmlopt, NULL, + 0); + + if (remove_dev && qemuDomainDetachDeviceLive(vm, remove_dev, drive= r, false) =3D=3D 0) { + detach_success_pass++; + } + } + + virObjectLock(priv->mon); + + cleanup: + if (vm) { + priv->qemuCaps =3D NULL; + priv->mon =3D NULL; + vm->def =3D NULL; + + virDomainObjEndAPI(&vm); + } + + virDomainDeviceDefFree(remove_dev); + virDomainDeviceDefFree(dev); + virDomainDefFree(def); +} + + +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 qemuFuzzHotplugData *hotplugData =3D g_new0(qemuFuzzHotplugData= , 1); + + static virQEMUDriver driver; + static bool initialized =3D false; + + 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 std::string arch =3D ""; + static std::string domain_xml =3D ""; + + std::string device_xml =3D ""; + + /* + * One-time setup of QEMU driver. 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); + + driver.lockManager =3D virLockManagerPluginNew("nop", "qemu", + driver.config->config= BaseDir, + 0); + + if (arch_env) { + arch =3D arch_env; + } else { + arch =3D "x86_64"; + } + + if (!(hotplugData->caps =3D testQemuGetRealCaps(arch.c_str(), "lat= est", "", + capslatest, capscach= e, + qapiSchemaCache, &ho= tplugData->schema))) { + printf("Failed to setup QEMU capabilities (invalid arch?)\n"); + exit(EXIT_FAILURE); + } + + if (qemuTestCapsCacheInsert(driver.qemuCapsCache, hotplugData->cap= s) < 0) + exit(EXIT_FAILURE); + + if (!(driver.hostdevMgr =3D virHostdevManagerGetDefault())) + exit(EXIT_FAILURE); + + virEventRegisterDefaultImpl(); + + domain_xml =3D getBaseDomainXML(arch); + + /* Enable printing of XML to stdout (useful for debugging crashes)= */ + if (dump_xml_env && STREQ(dump_xml_env, "YES")) + enable_xml_dump =3D true; + + initialized =3D true; + } + + convertProtoToXML(message, device_xml); + + if (enable_xml_dump) + printf("%s\n", device_xml.c_str()); + + fuzzXMLHotplug(&driver, hotplugData, domain_xml.c_str(), device_xml.c_= str()); + + if (device_parse_pass % 1000 =3D=3D 0) + printf("[FUZZ METRICS] Device parse: %lu, Device Attach: %lu, Atta= ched: %lu, Detached: %lu\n", + device_parse_pass, device_attach_pass, success_pass, detach= _success_pass); +} diff --git a/tests/fuzz/run_fuzz.in b/tests/fuzz/run_fuzz.in index da3c7935b7..414b99b6cf 100644 --- a/tests/fuzz/run_fuzz.in +++ b/tests/fuzz/run_fuzz.in @@ -113,6 +113,11 @@ env["LPM_EXE_PATH"] =3D exe_path =20 process_args.extend(["-print_funcs=3D-1"]) =20 +if args.fuzzer =3D=3D "qemu_xml_hotplug_fuzz": + # LSAN slows down the hotplug fuzzer to a crawl, + # so we have to disable LeakSanitizer + env["ASAN_OPTIONS"] =3D "detect_leaks=3D0" + if args.libfuzzer_options: process_args.extend([x for x in args.libfuzzer_options.split(' ') if x= !=3D '']) =20 diff --git a/tests/qemumonitortestutils.c b/tests/qemumonitortestutils.c index 88a369188e..448710957e 100644 --- a/tests/qemumonitortestutils.c +++ b/tests/qemumonitortestutils.c @@ -83,8 +83,14 @@ struct _qemuMonitorTest { =20 virDomainObj *vm; GHashTable *qapischema; + + bool fuzz; + qemuMonitorFuzzResponseCallback fuzz_response_cb; }; =20 +static int +qemuMonitorFuzzProcessCommandDefault(qemuMonitorTest *test, + const char *cmdstr); =20 static void qemuMonitorTestItemFree(qemuMonitorTestItem *item) @@ -227,6 +233,11 @@ qemuMonitorTestProcessCommand(qemuMonitorTest *test, =20 VIR_DEBUG("Processing string from monitor handler: '%s", cmdstr); =20 + /* In fuzzing mode, process indefinite number of commands */ + if (test->fuzz) { + return qemuMonitorFuzzProcessCommandDefault(test, cmdstr); + } + if (test->nitems =3D=3D 0) { qemuMonitorTestError("unexpected command: '%s'", cmdstr); } else { @@ -595,6 +606,34 @@ qemuMonitorTestAddItem(qemuMonitorTest *test, } =20 =20 +static int +qemuMonitorFuzzProcessCommandDefault(qemuMonitorTest *test, + const char *cmdstr) +{ + g_autoptr(virJSONValue) val =3D NULL; + virJSONValue *cmdargs =3D NULL; + + const char *cmdname =3D NULL; + g_autofree char *response =3D NULL; + + if (!(val =3D virJSONValueFromString(cmdstr))) + return -1; + + if (!(cmdname =3D virJSONValueObjectGetString(val, "execute"))) { + qemuMonitorTestError("Missing command name in %s", cmdstr); + return -1; + } + + cmdargs =3D virJSONValueObjectGet(val, "arguments"); + + response =3D test->fuzz_response_cb(cmdname, cmdargs); + + qemuMonitorTestAddResponse(test, response); + + return 0; +} + + static int qemuMonitorTestProcessCommandVerbatim(qemuMonitorTest *test, qemuMonitorTestItem *item, @@ -1021,6 +1060,15 @@ qemuMonitorTestSkipDeprecatedValidation(qemuMonitorT= est *test, } =20 =20 +void +qemuMonitorTestFuzzSetup(qemuMonitorTest *test, + qemuMonitorFuzzResponseCallback response_cb) +{ + test->fuzz =3D true; + test->fuzz_response_cb =3D response_cb; +} + + static int qemuMonitorTestFullAddItem(qemuMonitorTest *test, const char *filename, diff --git a/tests/qemumonitortestutils.h b/tests/qemumonitortestutils.h index 6d26526f60..105bd10486 100644 --- a/tests/qemumonitortestutils.h +++ b/tests/qemumonitortestutils.h @@ -29,6 +29,8 @@ typedef struct _qemuMonitorTestItem qemuMonitorTestItem; typedef int (*qemuMonitorTestResponseCallback)(qemuMonitorTest *test, qemuMonitorTestItem *item, const char *message); +typedef char *(*qemuMonitorFuzzResponseCallback)(const char *command, + virJSONValue *cmdargs); =20 void qemuMonitorTestAddHandler(qemuMonitorTest *test, @@ -61,6 +63,10 @@ void qemuMonitorTestSkipDeprecatedValidation(qemuMonitorTest *test, bool allowRemoved); =20 +void +qemuMonitorTestFuzzSetup(qemuMonitorTest *test, + qemuMonitorFuzzResponseCallback response_cb); + int qemuMonitorTestAddItem(qemuMonitorTest *test, const char *command_name, --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 172408492871794.67756917628594; Mon, 19 Aug 2024 09:28:48 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id A177E16AD; Mon, 19 Aug 2024 12:28:47 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 2F9381621; Mon, 19 Aug 2024 12:20:44 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 597E415C9; Mon, 19 Aug 2024 12:20:38 -0400 (EDT) Received: from mail-lj1-f179.google.com (mail-lj1-f179.google.com [209.85.208.179]) (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 51D8015EE for ; Mon, 19 Aug 2024 12:20:04 -0400 (EDT) Received: by mail-lj1-f179.google.com with SMTP id 38308e7fff4ca-2f3e071eb64so14519821fa.1 for ; Mon, 19 Aug 2024 09:20:04 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:02 -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=1724084403; x=1724689203; 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=zUhLBWSDQrU5PtLoFGtCFfmUi3Ew+BkgaMOK2Pl4YU4=; b=jHdrDULFd4NcvkRHLiX35oydXvG2Rina0JKOKqFF6iwMEFTZ9ltx4ikU9MyK1ZOhho upGxCQX3KtzcZbcoggbbRfeekhZZygf4rzRAnblbK0I9V36+OZ7my5a26tKe4IxBOWjK UdrQOHf/1uwM1QZ+A2pd+UrcBt4O3Ew9+IxC1NctWiKO6Nt8cX49DYW+rLDFauLXNj0w es3im59EyDQAmoQmIZ+TeBuF7ZO5NM+J6dgGqw1k5cRI6slP7ltkaZp7shk4NZ3O5gP3 Ubo2TbsjZORC+Jm8hg5EhO3LJ9dd6Dodbc6GbKEjvIX+iDEXZg1UtH/rc+WEJSgRR07p KYTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084403; x=1724689203; 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=zUhLBWSDQrU5PtLoFGtCFfmUi3Ew+BkgaMOK2Pl4YU4=; b=UHuDvOuFLFUUlpUlrJwf+mWemvtwkvjedI3xDgVPRm/ZGmbPZabLAq3TJG8lGI4vGd SzXzIem37h9XHq+PLtZrvuz4TQqHEkkiOdZY2XB5kd9rk3t/RADv0tF/eaFjDR+zpKgy Sc7aoPDv6AHGLt0/0OBYXxb5qV93ypRkaqji3YM0GHchp2tfsEocOqDCLPoBRXcTgFlN KJZJt8t10soGcqns/XfAtWkIODi9dM/vTXExXDl6pwLHV4ojl7DoqRtQq59689jzcLJM rfgW6ezly2XKjLmUfc6c4crB6NBwgiPi3XRwWRNi0ix603TNMzcJreALnsBBlOnmAo3f z5FQ== X-Gm-Message-State: AOJu0Ywc74d+kmyXeCho9uaWHSqMLwIrQMf973j54ed6iTed5DBoyQEc fXpPBeu4w/C4ERt7ro19Sjfk2x5qQgLL9mrvIRJ5iiF7ujoyzzUdonxaIg== X-Google-Smtp-Source: AGHT+IHM3qZbLuYgCHzZQcZapxrfe9zNlfdhbexLgZlw5eU5+tkc9gUoEJH7NUhXhBhdduelBp5FWA== X-Received: by 2002:a2e:742:0:b0:2ef:2677:7b74 with SMTP id 38308e7fff4ca-2f3be5f02eemr83974551fa.41.1724084402517; Mon, 19 Aug 2024 09:20:02 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 09/14] ch: Remove unused variables Date: Mon, 19 Aug 2024 21:39:47 +0530 Message-Id: <20240819160952.351383-10-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: K3FCVHHZHKA6WAKLD75LSQHKIKJD6FPR X-Message-ID-Hash: K3FCVHHZHKA6WAKLD75LSQHKIKJD6FPR 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: 1724084929266116600 Content-Type: text/plain; charset="utf-8" Unused variables in ch_process.c cause compilation errors so remove them. Signed-off-by: Rayhan Faizel --- src/ch/ch_process.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ch/ch_process.c b/src/ch/ch_process.c index 9816509e49..12da020c7b 100644 --- a/src/ch/ch_process.c +++ b/src/ch/ch_process.c @@ -656,7 +656,6 @@ chProcessAddNetworkDevices(virCHDriver *driver, for (i =3D 0; i < vmdef->nnets; i++) { g_autofree int *tapfds =3D NULL; g_autofree char *payload =3D NULL; - g_autofree char *response =3D NULL; size_t tapfd_len; size_t payload_len; int saved_errno; @@ -967,7 +966,6 @@ virCHProcessStartRestore(virCHDriver *driver, virDomain= Obj *vm, const char *from g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; g_auto(virBuffer) http_headers =3D VIR_BUFFER_INITIALIZER; g_autofree char *payload =3D NULL; - g_autofree char *response =3D NULL; VIR_AUTOCLOSE mon_sockfd =3D -1; g_autofree int *tapfds =3D NULL; g_autofree int *nicindexes =3D NULL; --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084959616442.35191878971; Mon, 19 Aug 2024 09:29:19 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 8E80C167; Mon, 19 Aug 2024 12:29:18 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 82EC61656; Mon, 19 Aug 2024 12:20:45 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 419B916F1; Mon, 19 Aug 2024 12:20:39 -0400 (EDT) Received: from mail-lj1-f173.google.com (mail-lj1-f173.google.com [209.85.208.173]) (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 141A215F6 for ; Mon, 19 Aug 2024 12:20:06 -0400 (EDT) Received: by mail-lj1-f173.google.com with SMTP id 38308e7fff4ca-2f189a2a7f8so46113281fa.2 for ; Mon, 19 Aug 2024 09:20:06 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:03 -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=1724084404; x=1724689204; 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=FKD7hqnqLY5k6DiUCOER6txYcP8d6H+JUruQucXc450=; b=iptReZvHAvjViMcGT/+xwF32bJgRqbOXCh9k0xec0qOUYtwClCvIkbBH6naiysYoxZ Pnk+oiYsDJTnPTc/naEk+quR7RyZ6xfDDUXSGQANx7CnOPhrXbfdez5xfgKm44ST0QQh Alb+J5rJ1/krOdHTIMqlYZYldD5sjwHqYQ/zKVNbyaLYEM/WOQAepIZi1LG/CI0bK1Du XZ79mYMus9UiJLMaIKBiZiLyxq13eKD8gAcY/AU2ULhMPf1ahZq4+Uvf3qO/cHhILAZ+ foqdpB1SjzSk+xw0Jzc5Acd5169KGq+biGU/V4G+Eyyjk/nRs4lEZu9JYeNPjrwvQFkr Q+AQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084404; x=1724689204; 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=FKD7hqnqLY5k6DiUCOER6txYcP8d6H+JUruQucXc450=; b=dovRn8kVHqG+Up7aFJslj+qD80/HpYWqPnd6pWP+xNX48iSskXaoYP9rmYzY1nPitq QSsEOkLLY+wCpkeF7qaAG1qlDzhuOixBd8Avj7n8zztEPuz5DqbQPSSQh4+1kOrO4yGf eoXvE1DMGFbwLSuLmpqu3K+0UcS/jdL82ZD7kP49h2/ZqaVgHvoYHIA6Vaez2MwB9MFp mFW5UIOqjnKlGPy8tzIU3x1Aa1O+eaSyTxKZX/1V7Qo37oiihR0A9AyQ5ItTNJU6twMk vvpCyufR6RI2qmV3siZumCmakYzBFRWz/2ocZAk5dTm8wRd930dxTtVjKgVxVIuzb2R0 q/MA== X-Gm-Message-State: AOJu0YwDbtlpfwvtXBmb88clMaKbvv9Y8Q57sQlXXay1uPQTWkRyIKP4 D0MXZk7UOzVyaaxbHG8RJh4hQNKhptW5w4SonB+jT3I0Vqr242J/hlo+Pw== X-Google-Smtp-Source: AGHT+IEvwr5jkfeiPonLKyu+wIM8WDGhPljBVbE+OA+jFjJhciYztgkpy2pEChvssG4F926klQ8VeQ== X-Received: by 2002:a2e:9c86:0:b0:2ef:216c:c97 with SMTP id 38308e7fff4ca-2f3be59b7eemr72539661fa.19.1724084403903; Mon, 19 Aug 2024 09:20:03 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 10/14] fuzz: Implement CH XML domain fuzzer Date: Mon, 19 Aug 2024 21:39:48 +0530 Message-Id: <20240819160952.351383-11-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: BZTH7JUEFHTNM4KB5FO47PSN4DQFCTKB X-Message-ID-Hash: BZTH7JUEFHTNM4KB5FO47PSN4DQFCTKB 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: 1724084961148116600 Content-Type: text/plain; charset="utf-8" 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 --- 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; } =20 -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_fu= zz.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 + * . + */ + +#include +#include "proto_header_common.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +extern "C" { +#include +#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 =3D 0; +uint64_t format_pass =3D 0; +uint64_t command_line_pass =3D 0; +uint64_t success =3D 0; +uint64_t net =3D 0; +uint64_t net_success =3D 0; + +bool enable_xml_dump =3D false; +bool enable_xml_format =3D false; + +extern std::string emulator; + +static void +fuzzXMLToCommandLine(virCHDriver *driver, + const char *xml_string) +{ + virDomainDef *def =3D NULL; + g_autofree char *formatted_xml =3D NULL; + g_autofree char *json_str =3D NULL; + size_t i; + + 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; + } + + command_line_pass++; + + if (virCHMonitorBuildVMJson(driver, def, &json_str) < 0) + goto cleanup; + success++; + + for (i =3D 0; i < def->nnets; i++) { + net++; + if (virCHMonitorBuildNetJson(def->nets[i], i, &json_str) =3D=3D 0) + net_success++; + } + + cleanup: + virDomainDefFree(def); +} + + +DEFINE_PROTO_FUZZER(const libvirt::MainObj &message) +{ + static virCHDriver *driver; + static bool initialized =3D false; + + 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"= ); + + std::string xml =3D ""; + + + /* + * 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 =3D g_new0(virCHDriver, 1); + + if (!(driver->caps =3D virCHDriverCapsInit())) { + printf("Unable to initialize driver capabilities\n"); + exit(EXIT_FAILURE); + } + + virCapsGuest *guest =3D 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 =3D chDomainXMLConfInit(driver))) { + printf("Unable to initialize driver XMLOPT\n"); + exit(EXIT_FAILURE); + } + + emulator =3D "/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 =3D true; + + /* Enable fuzzing of XML formatting */ + if (format_xml_env && STREQ(format_xml_env, "YES")) + enable_xml_format =3D true; + + initialized =3D true; + } + + convertProtoToCHXMLDomain(message, 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, Network: %lu, Network Success: %lu\n", + parse_pass, format_pass, command_line_pass, success, net, n= et_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 =20 +if conf.has('WITH_CH') + fuzzer_src =3D [ + 'ch_xml_domain_fuzz.cc', + 'proto_to_xml.cc', + ] + + ch_libs =3D [ + ch_driver_impl, + test_utils_lib, + libvirt_lib, + ] + + xml_fuzzers +=3D [ + { '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 =3D 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::MainOb= j &message, } =20 =20 +void convertProtoToCHXMLDomain(const libvirt::MainObj &message, + std::string &xml) +{ + xml =3D "\n" + " MyGuest\n" + " 4dea22b3-1d52-d8f3-2516-782e98ab3fa0\n" + " \n" + " hvm\n" + " hypervisor-fw\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 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); --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724084989417927.8633713928589; Mon, 19 Aug 2024 09:29:49 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 618C015F8; Mon, 19 Aug 2024 12:29:48 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 6B01716F1; Mon, 19 Aug 2024 12:20:47 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 906D115AB; Mon, 19 Aug 2024 12:20:41 -0400 (EDT) Received: from mail-ed1-f51.google.com (mail-ed1-f51.google.com [209.85.208.51]) (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 AFB0D15DB for ; Mon, 19 Aug 2024 12:20:06 -0400 (EDT) Received: by mail-ed1-f51.google.com with SMTP id 4fb4d7f45d1cf-5bed72ff443so3435199a12.1 for ; Mon, 19 Aug 2024 09:20:06 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:04 -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=1724084405; x=1724689205; 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=XRpE58r9w4OK8gP3DtqkI40B6Fz9zSRb+2imLuhC4JU=; b=KphR0HTEfMCPeG3zX3pAWhDm0NEI5xtYXeB/baPcgmsnm/yvnGew4/4aDD2OHij+Oz RTa3kMeyEdIF/a/2oAfYsyw7+waAs/D7pgUcWJ4w0dv4ba0Fi3QIcjpuwQBppZ+rNsjn zemBnui/qHwF1WVx9dAQq/SIoZRQQHbzvi4Tb86DvQwBbbWBkEOz7jl8FFJprBm2kaha MxS2K5tymKI1OBT5wuuhTsI2NRgn2K1hsSZ3sISZIY7UXMxnDrg8YaTGHNRSyF4EgkLi +0U6yWKCbbDB3Xc9VhUpmoJx7/OEI7koz/33Ns3mMi+gdIvARBwoBaFpT1vfsSaaKk0P TBEQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084405; x=1724689205; 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=XRpE58r9w4OK8gP3DtqkI40B6Fz9zSRb+2imLuhC4JU=; b=JUiuIi3RYIFJU1HRuVFuMCuRAnqLrreHQfyoWXhLFULZstw+HWsWFx2Ru9jm/Ekhp0 4PCdYyGydlE5U5qSUiDntVRYMbyePoJdNNH3/+gg5nj5+y+V1yi0XLRbZ3zF/qMIcujV 4H/IVkFXNqM5OPm0RdrbvUxVTtSYALyTPLw1NBH8PGhp/gw+kx21ufiwJZGRopg5j4Kj /hlKaw8CwvALOLLaKyl3XslFOhT/FwICxQ8baJ28PiXpqmkaedwFNbjGiPwKFlpPitsv y2qBQWVpPvSzEIpP8Y86FlkaEHim0R5CC1Im5Je3A3suzWHiFFYuY9uy9kpk0iTYGzSN CuSg== X-Gm-Message-State: AOJu0YyKDa49Uo+2vbEZw7dWC9dfkmbyYdggVBAyz4XJiOdV5jOQB+Lu YN8nwJZ7fFgcLbZz6S/BCsYOEvcRCwSLEjE/l55WjMEbPr5bKRU3r9T8Sg== X-Google-Smtp-Source: AGHT+IHt2Gb8E0hrjoJSeZgJ+YTGUjff9VVm8QG//gc9vaOMY2oAjrYIIE6EpkI2uBqVEPaKWvQ39Q== X-Received: by 2002:a05:6402:35cc:b0:5be:f101:9297 with SMTP id 4fb4d7f45d1cf-5bef10199d9mr3559389a12.2.1724084405228; Mon, 19 Aug 2024 09:20:05 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 11/14] fuzz: Implement VMX XML domain fuzzer Date: Mon, 19 Aug 2024 21:39:49 +0530 Message-Id: <20240819160952.351383-12-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: Z2IYTCIQD27SZ3W4R6K4KBYBMGAY43IR X-Message-ID-Hash: Z2IYTCIQD27SZ3W4R6K4KBYBMGAY43IR 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: 1724084991253116600 Content-Type: text/plain; charset="utf-8" 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 --- 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 =20 +if conf.has('WITH_VMX') + fuzzer_src =3D [ + 'vmx_xml_domain_fuzz.cc', + 'proto_to_xml.cc', + ] + + vmx_libs =3D [ + test_utils_lib, + libvirt_lib, + ] + + xml_fuzzers +=3D [ + { 'name': 'vmx_xml_domain_fuzz', 'src': [ fuzzer_src, xml_domain_proto= _src ], 'libs': vmx_libs, 'macro': '-DXML_DOMAIN', 'deps': [ fuzz_autogen_x= ml_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 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, } =20 =20 +void convertProtoToVMXXMLDomain(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 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 + * . + */ + +#include +#include "proto_header_common.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +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 =3D 0; +uint64_t format_pass =3D 0; +uint64_t command_line_pass =3D 0; +uint64_t success =3D 0; + +bool enable_xml_dump =3D false; +bool enable_xml_format =3D false; + +static void +fuzzXMLToCommandLine(virVMXContext *ctx, + virDomainXMLOption *xmlopt, + const char *xml_string) +{ + virDomainDef *def =3D NULL; + g_autofree char *formatted_xml =3D NULL; + g_autofree char *formatted_config =3D NULL; + + parse_pass++; + if (!(def =3D virDomainDefParseString(xml_string, xmlopt, NULL, + VIR_DOMAIN_DEF_PARSE_INACTIVE))) + goto cleanup; + + if (enable_xml_format) { + format_pass++; + if (!(formatted_xml =3D virDomainDefFormat(def, xmlopt, + VIR_DOMAIN_DEF_FORMAT_SEC= URE))) + goto cleanup; + } + + command_line_pass++; + + if (!(formatted_config =3D virVMXFormatConfig(ctx, xmlopt, def, 13))) { + goto cleanup; + } + + success++; + + cleanup: + + virDomainDefFree(def); +} + + +virCaps *fuzzCapsInit() { + virCapsGuest *guest =3D NULL; + virCaps *caps =3D NULL; + + caps =3D virCapabilitiesNew(VIR_ARCH_I686, true, true); + + if (caps =3D=3D NULL) + return NULL; + + virCapabilitiesAddHostMigrateTransport(caps, "esx"); + + /* i686 guest */ + guest =3D 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 =3D 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 =3D VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC; + + return 0; +} + + +DEFINE_PROTO_FUZZER(const libvirt::MainObj &message) +{ + static virVMXContext ctx; + static virDomainXMLOption *xmlopt =3D NULL; + static virCaps *caps; + + static bool initialized =3D false; + + 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 ""; + + /* + * 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 =3D fuzzCapsInit(); + + if (caps =3D=3D NULL) { + exit(EXIT_FAILURE); + } + + if (arch_env) { + arch =3D arch_env; + } else { + arch =3D "x86_64"; + } + + if (!(arch =3D=3D "x86_64" || arch =3D=3D "i686")) { + printf("Unsupported architecture: %s\n", arch.c_str()); + exit(EXIT_FAILURE); + } + + if (!(xmlopt =3D virVMXDomainXMLConfInit(caps))) + exit(EXIT_FAILURE); + + ctx.opaque =3D NULL; + ctx.parseFileName =3D NULL; + ctx.formatFileName =3D fuzzFormatVMXFileName; + ctx.autodetectSCSIControllerModel =3D fuzzAutodetectSCSIController= Model; + ctx.datacenterPath =3D NULL; + + /* 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; + } + + convertProtoToVMXXMLDomain(message, arch, xml); + + if (enable_xml_dump) + printf("%s\n", xml.c_str()); + + fuzzXMLToCommandLine(&ctx, xmlopt, 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 From nobody Sat Nov 23 15:01:47 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 1724085034162274.0938267987351; Mon, 19 Aug 2024 09:30:34 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id ECECD15C6; Mon, 19 Aug 2024 12:30:32 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 7CCCF161F; Mon, 19 Aug 2024 12:20:51 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 517F415E8; Mon, 19 Aug 2024 12:20:42 -0400 (EDT) Received: from mail-ed1-f52.google.com (mail-ed1-f52.google.com [209.85.208.52]) (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 0E9391610 for ; Mon, 19 Aug 2024 12:20:08 -0400 (EDT) Received: by mail-ed1-f52.google.com with SMTP id 4fb4d7f45d1cf-5becc379f3fso3111149a12.3 for ; Mon, 19 Aug 2024 09:20:07 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:06 -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=1724084407; x=1724689207; 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=CnnutAHY444kPD7qK5BKaZO6tDzfei0SOOfnSThq1m4=; b=Puq+gs8ZjZ3vq4d6gJUMOxrbclhgLc8dUtiajNOZioWfKJgAXiEoJvvax3nF25zD9x vwvXT9KsT4gGyGfCLdcpBGoLb1dKeSfI9H5eiYSbqWQPl0+SVT2Nxt7GQRejpITTMHpf reZ2ihqdVqSb/h4mfzM50XC24KOWCbdwmKdp9TZfyBu+RnHyccZR5PUVmV3YXczVYbR7 MAPK1TOZONoKpSpw7zmtH8cI/RiiFMSf2Wqu6YvjtcjO1G2267U1P9N7ACix1ep5arNJ xYogTvSPB/oWd3q/kx3ebyWdt4jXzSb2muD9kTZr7wVRRZmTTZmJj+5FHskETvZT48q9 W/rg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084407; x=1724689207; 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=CnnutAHY444kPD7qK5BKaZO6tDzfei0SOOfnSThq1m4=; b=DCksRvd3LTr+BDfDdOnhOxZCIFDtn/bj4QlwwOWy6Z/5HDTrxYoLnFV3a67K0Y9Yav F7uZavkOflVskyB2yGdaVu4yI+/OVfNApt0mox8UsCNIa6GctNOHqXRFQuJs5Rr/Xi2Y HH8TciZTbGfHQCPRDSfIVzGGdbg4nDzl3Yua533hiaQl4OeCi7sfiVMdXiOpAzPH5LKm 1ExcIKUueNI0s5eBzqr1rK7XmtKfQPGrsMIRkU/gFU1DrrJ4yt6VKqMLnrnKxav7e6h8 VCeXdazH7yLY4hCDBc+p2VEzR6Ng/Y0fbzB2I498O7qqWWvvzH9zmZcaejD1bvzsy4g8 21Hg== X-Gm-Message-State: AOJu0Yyx5bjoG80pPZXLq2tI7mU1hUuL5ThHKNF7qux7HS53b52wiOW0 PFA7XsOyPQtrHySr1GAjuNITCSOF9w2GOo1YcDgsFkNoHoX/qznBp1C9IA== X-Google-Smtp-Source: AGHT+IGdLbVvggaxMklrqofhCgdoulVbiALQTbEhGJaMEbtez4vGoYPkOi0wRSD028cm9dWFpSp0XQ== X-Received: by 2002:a05:6402:35d2:b0:5be:d704:e724 with SMTP id 4fb4d7f45d1cf-5bed704ea8amr6855461a12.10.1724084406480; Mon, 19 Aug 2024 09:20:06 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 12/14] fuzz: Implement libXL XML domain fuzzer Date: Mon, 19 Aug 2024 21:39:50 +0530 Message-Id: <20240819160952.351383-13-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: FFR2M6GMDRN7CWLBYK7H7IM5L725QOJS X-Message-ID-Hash: FFR2M6GMDRN7CWLBYK7H7IM5L725QOJS 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: 1724085035512116600 Content-Type: text/plain; charset="utf-8" This patch adds the harness for the xen driver to fuzz XML parsing, domconfig generation and JSON conversion. libxlmock.so is required to make the libXL fuzzer work outside of xen hosts. However, LD_PRELOAD breaks the fuzzer when ASAN/UBSAN is present. This is because LD_PRELOAD gets propagated to llvm-symbolizer which is undesirable and causes various undefined symbol errors. We add a wrapper binary which e= xecs the real llvm-symbolizer with the environment unset. However, LD_PRELOAD still breaks parallel fuzzing so it is disabled for the time being. Signed-off-by: Rayhan Faizel --- tests/fuzz/libxl_xml_domain_fuzz.cc | 159 +++++++++++++++++++++++++++ tests/fuzz/llvm_symbolizer_wrapper.c | 11 ++ tests/fuzz/meson.build | 31 ++++++ tests/fuzz/proto_to_xml.cc | 18 +++ tests/fuzz/proto_to_xml.h | 3 + tests/fuzz/run_fuzz.in | 15 +++ 6 files changed, 237 insertions(+) create mode 100644 tests/fuzz/libxl_xml_domain_fuzz.cc create mode 100644 tests/fuzz/llvm_symbolizer_wrapper.c diff --git a/tests/fuzz/libxl_xml_domain_fuzz.cc b/tests/fuzz/libxl_xml_dom= ain_fuzz.cc new file mode 100644 index 0000000000..a8fcb62d06 --- /dev/null +++ b/tests/fuzz/libxl_xml_domain_fuzz.cc @@ -0,0 +1,159 @@ +/* + * libxl_xml_domain_fuzz.cc: libXL 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 "testutils.h" +#include "internal.h" +#include "libxl/libxl_conf.h" +#include "testutilsxen.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 config_pass =3D 0; +uint64_t json_pass =3D 0; +uint64_t command_line_pass =3D 0; +uint64_t success =3D 0; + +bool enable_xml_dump =3D false; +bool enable_xml_format =3D false; + +static void +fuzzXMLToCommandLine(libxlDriverPrivate *driver, + const char *xml_string) +{ + virDomainDef *def =3D NULL; + const char *formatted_xml =3D NULL; + g_autofree char *json_string =3D NULL; + libxl_domain_config config; + virPortAllocatorRange *gports =3D NULL; + bool config_init =3D false; + + g_autoptr(libxlDriverConfig) cfg =3D libxlDriverConfigGet(driver); + + 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; + } + + libxl_domain_config_init(&config); + config_init =3D true; + + if (!(gports =3D virPortAllocatorRangeNew("vnc", 5900, 6000))) + goto cleanup; + + config_pass++; + if (libxlBuildDomainConfig(gports, def, cfg, &config) < 0) + goto cleanup; + + json_pass++; + if (!(json_string =3D libxl_domain_config_to_json(cfg->ctx, &config))) + goto cleanup; + + success++; + + cleanup: + + virPortAllocatorRangeFree(gports); + if (config_init) + libxl_domain_config_dispose(&config); + virDomainDefFree(def); +} + + +DEFINE_PROTO_FUZZER(const libvirt::MainObj &message) +{ + static libxlDriverPrivate *driver =3D NULL; + + static bool initialized =3D false; + + 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 ""; + + + /* + * One-time setup of libXL driver. Re-running them in every + * iteration incurs a significant penalty to the speed of the fuzzer. + */ + if (!initialized) { + FUZZ_COMMON_INIT(); + + /* NOTE: Driver initialization will fail without libxlmock */ + if ((driver =3D testXLInitDriver()) =3D=3D NULL) + exit(EXIT_FAILURE); + + if (arch_env) { + arch =3D arch_env; + } else { + arch =3D "x86_64"; + } + + /* 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; + } + + convertProtoTolibXLXMLDomain(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, Config: %lu, JSON:= %lu, Success: %lu\n", + parse_pass, format_pass, config_pass, json_pass, success); + +} diff --git a/tests/fuzz/llvm_symbolizer_wrapper.c b/tests/fuzz/llvm_symboli= zer_wrapper.c new file mode 100644 index 0000000000..d0aeba61fe --- /dev/null +++ b/tests/fuzz/llvm_symbolizer_wrapper.c @@ -0,0 +1,11 @@ +#include + +#include +#include + +int main(int argc, char **argv) { + // Avoid unused error. + (void) argc; + + execve("/usr/bin/llvm-symbolizer", argv, NULL); +} diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 88b5fe103c..417b8dc1ef 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -90,6 +90,24 @@ if conf.has('WITH_VMX') ] endif =20 +if conf.has('WITH_LIBXL') + fuzzer_src =3D [ + 'libxl_xml_domain_fuzz.cc', + 'proto_to_xml.cc', + ] + + libxl_libs =3D [ + test_xen_driver_lib, + test_utils_xen_lib, + test_utils_lib, + libvirt_lib, + ] + + xml_fuzzers +=3D [ + { 'name': 'libxl_xml_domain_fuzz', 'src': [ fuzzer_src, xml_domain_pro= to_src ], 'libs': libxl_libs, 'macro': '-DXML_DOMAIN', 'deps': [ fuzz_autog= en_xml_domain_dep, libxl_dep ] }, + ] +endif + foreach fuzzer: xml_fuzzers xml_domain_fuzz =3D executable(fuzzer['name'], fuzzer['src'], @@ -102,6 +120,19 @@ foreach fuzzer: xml_fuzzers available_fuzzers +=3D '"' + fuzzer['name'] + '"' + ',' endforeach =20 +# Wrapper binary which execs the real llvm-symbolizer but +# unsets the env vars to avoid propagating LD_PRELOAD to the +# real llvm-symbolizer and causing undefined symbol errors when ASAN/UBSAN +# is enabled. + +llvm_symbolizer_wrapper =3D executable( + 'llvm-symbolizer-wrapper', + 'llvm_symbolizer_wrapper.c', + dependencies: [ + tests_dep, + ], +) + run_conf =3D configuration_data({ 'abs_builddir': meson.project_build_root(), 'available_fuzzers': available_fuzzers, diff --git a/tests/fuzz/proto_to_xml.cc b/tests/fuzz/proto_to_xml.cc index 983256bcae..fde3394cc7 100644 --- a/tests/fuzz/proto_to_xml.cc +++ b/tests/fuzz/proto_to_xml.cc @@ -252,6 +252,24 @@ void convertProtoToVMXXMLDomain(const libvirt::MainObj= &message, } =20 =20 +void convertProtoTolibXLXMLDomain(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 89e6726611..b4849890cc 100644 --- a/tests/fuzz/proto_to_xml.h +++ b/tests/fuzz/proto_to_xml.h @@ -32,5 +32,8 @@ void convertProtoToCHXMLDomain(const libvirt::MainObj &me= ssage, void convertProtoToVMXXMLDomain(const libvirt::MainObj &message, std::string arch, std::string &xml); +void convertProtoTolibXLXMLDomain(const libvirt::MainObj &message, + std::string arch, + std::string &xml); void convertProtoToXML(const libvirt::MainObj &message, std::string &xml); diff --git a/tests/fuzz/run_fuzz.in b/tests/fuzz/run_fuzz.in index 414b99b6cf..a2b0c66c32 100644 --- a/tests/fuzz/run_fuzz.in +++ b/tests/fuzz/run_fuzz.in @@ -118,6 +118,21 @@ if args.fuzzer =3D=3D "qemu_xml_hotplug_fuzz": # so we have to disable LeakSanitizer env["ASAN_OPTIONS"] =3D "detect_leaks=3D0" =20 +if args.fuzzer =3D=3D "libxl_xml_domain_fuzz": + # To make LD_PRELOAD work without running into undefined symbol + # errors, we need to override ASAN_SYMBOLIZER_PATH with a wrapper + # that unsets LD_PRELOAD before running the real llvm-symbolizer. + + env["ASAN_SYMBOLIZER_PATH"] =3D llvm_symbolizer_path + env["LD_PRELOAD"] =3D f"{abs_builddir}/tests/libxlmock.so" + + # Note: Above workaround breaks multithreaded mode, + # so inform the user of the same. + + if args.jobs > 1: + print("libxl_xml_domain_fuzz cannot be used in parallel mode.") + sys.exit(1) + if args.libfuzzer_options: process_args.extend([x for x in args.libfuzzer_options.split(' ') if x= !=3D '']) =20 --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724085075435347.43572138341824; Mon, 19 Aug 2024 09:31:15 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 5BF701707; Mon, 19 Aug 2024 12:31:14 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 4AAAD16FB; Mon, 19 Aug 2024 12:20:52 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 672011609; Mon, 19 Aug 2024 12:20:43 -0400 (EDT) Received: from mail-lj1-f174.google.com (mail-lj1-f174.google.com [209.85.208.174]) (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 CE05F161A for ; Mon, 19 Aug 2024 12:20:09 -0400 (EDT) Received: by mail-lj1-f174.google.com with SMTP id 38308e7fff4ca-2f3cd4ebf84so25557151fa.3 for ; Mon, 19 Aug 2024 09:20:09 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:07 -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=1724084408; x=1724689208; 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=bxozF8c0y3WpPJuN2tfu9REXrj4N76GIme9sTGR+wcs=; b=KcYsSwS1WDsOWcBaafREoIKjVHiobgQIBFdEYjeO3QbgMMex7giESbhWTi57LyM3+7 1xZj2j/04aoDdYFrTAVbXm8Dhhk46nLOBW+gvxC6DHUeVYcv9YqVXkP7D4dvWtyyDPuG UzK+CifIbIkWhUVo0Mqq9nJbRls9RJCiMSOxKzjNxR5U2ZrH1XimVxhzfk+Q+IiVXIuy KqrqZfLT8UFIUs7Se63UYx2dyNYZXvRsLS6zW7YUmVWhQF77WT6Zqvj/8Oniidr26lyW jXil8T5qoWmk+qMtLCYiCgGGq1keUsraJ42an7paBXL4wEgcf1dDObteMKa2rt642yK/ NNXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084408; x=1724689208; 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=bxozF8c0y3WpPJuN2tfu9REXrj4N76GIme9sTGR+wcs=; b=JJ926cYblHRHwF3LQY8X3xlJgrJicR4QvnL1Bh+9mjbOcVpp5TNEUvYcVmt4f23b9e LYgLJVDszNOSY2bsB8GwzvBZx+0wF0EvIn4d7igWAEivjTogLQdxMs/qXcLIGqfj8qqG ZevOkOPanbNxCWQqfykrHZiX6hDMpxRlcoi481p+UGiLRcthEY8+TiSdKWtUn0Vt8YYi 24KTl7VUi0eej4Na/BnG9+Ac8T1tlncfXKlxmX+YLjUfSPJOPNXW/rtNAQwfm4RRw6M0 pyR3sKC9WUJ7u3e3tyjSl5Nd/KWQT+4lUkFRVLp042TsGlKwKUC8QQBwk5UKcwpqtixf U3DQ== X-Gm-Message-State: AOJu0YyU6GSi9ONwmFOLuo8BhIl1CLYxYVUIdYR1IqImN/WM5r7RBvms HAaDn0G0mNr3SZ+cu0STLZnNkOabi0JNWtgzZYrE64pJQDVk4TfcELvyvw== X-Google-Smtp-Source: AGHT+IEPJtytK8HtZpuMvAz/xvZaAOEi8Na+uLd33+EzcS8lMawwZMX44uxVX5KJGAhnPc50RUpudQ== X-Received: by 2002:a05:651c:4ca:b0:2f3:d008:a54e with SMTP id 38308e7fff4ca-2f3d008a692mr41139131fa.36.1724084407684; Mon, 19 Aug 2024 09:20:07 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 13/14] fuzz: Implement NWFilter XML fuzzer Date: Mon, 19 Aug 2024 21:39:51 +0530 Message-Id: <20240819160952.351383-14-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: R4KDGVGTBMLKR7BYOIYJEHC47F7QNDDK X-Message-ID-Hash: R4KDGVGTBMLKR7BYOIYJEHC47F7QNDDK 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: 1724085075694116600 Content-Type: text/plain; charset="utf-8" This patch includes the harness for the network filter driver. It fuzzes one or more rules in the definition at a time. NWFilter protobuf definitions are generated separately and linked to this fuzzer. This patch also includes handling of some datatypes to fuzz certain attributes: IPSet, TCPFlag, ARP opcode, state flags, etc. Signed-off-by: Rayhan Faizel --- scripts/relaxng-to-proto.py | 16 +++ tests/fuzz/meson.build | 34 ++++++ tests/fuzz/proto_custom_datatypes.cc | 88 +++++++++++++++ tests/fuzz/proto_header_common.h | 4 + tests/fuzz/protos/meson.build | 9 ++ tests/fuzz/protos/xml_datatypes.proto | 21 ++++ tests/fuzz/protos/xml_nwfilter.proto | 9 ++ tests/fuzz/xml_nwfilter_fuzz.cc | 149 ++++++++++++++++++++++++++ 8 files changed, 330 insertions(+) create mode 100644 tests/fuzz/protos/xml_nwfilter.proto create mode 100644 tests/fuzz/xml_nwfilter_fuzz.cc diff --git a/scripts/relaxng-to-proto.py b/scripts/relaxng-to-proto.py index f13d6f7e40..9c1203ff1b 100644 --- a/scripts/relaxng-to-proto.py +++ b/scripts/relaxng-to-proto.py @@ -51,6 +51,22 @@ custom_ref_table =3D { "irq": {"type": "uint32"}, "iobase": {"type": "uint32"}, "uniMacAddr": {"type": "MacAddr"}, + + # NWFilter types + + "addrIP": {"type": "IPAddr"}, + "addrIPv6": {"type": "IPAddr"}, + "addrMAC": {"type": "MacAddr"}, + "uint16range": {"type": "uint32"}, + "uint32range": {"type": "uint32"}, + "sixbitrange": {"type": "uint32"}, + "stateflags-type": {"type": "StateFlags"}, + "tcpflags-type": {"type": "TCPFlags"}, + "ipset-flags-type": {"type": "IPSetFlags"}, + "arpOpcodeType": {"types": ["uint32", "DummyString"], + "values": ["reply", "request", "repl= y_reverse", "request_reverse", + "DRARP_reply", "DRARP_req= uest", "DRARP_error", "INARP_request", + "ARP_NAK"]}, } =20 net_model_names =3D ["virtio", "virtio-transitional", "virtio-non-transiti= onal", "e1000", "e1000e", "igb", diff --git a/tests/fuzz/meson.build b/tests/fuzz/meson.build index 417b8dc1ef..2e796b5726 100644 --- a/tests/fuzz/meson.build +++ b/tests/fuzz/meson.build @@ -31,6 +31,23 @@ fuzz_autogen_xml_domain_dep =3D declare_dependency( ] ) =20 +fuzz_autogen_xml_nwfilter_lib =3D static_library( + 'fuzz_autogen_xml_nwfilter_lib', + [ + autogen_xml_nwfilter_src, + xml_datatypes_proto_src, + 'proto_custom_datatypes.cc', + ], + dependencies: [ fuzz_dep ], +) + +fuzz_autogen_xml_nwfilter_dep =3D declare_dependency( + link_whole: [ fuzz_autogen_xml_nwfilter_lib ], + include_directories: [ + fuzz_autogen_xml_nwfilter_lib.private_dir_include(), + ] +) + if conf.has('WITH_QEMU') fuzzer_src =3D [ 'qemu_xml_domain_fuzz.cc', @@ -108,6 +125,23 @@ if conf.has('WITH_LIBXL') ] endif =20 +if conf.has('WITH_NWFILTER') + fuzzer_src =3D [ + 'xml_nwfilter_fuzz.cc', + 'proto_to_xml.cc', + ] + + nwfilter_libs =3D [ + test_utils_lib, + libvirt_lib, + nwfilter_driver_impl, + ] + + xml_fuzzers +=3D [ + { 'name': 'xml_nwfilter_fuzz', 'src': [ fuzzer_src, xml_nwfilter_proto= _src ], 'libs': nwfilter_libs, 'macro': '-DXML_NWFILTER', 'deps': [ fuzz_au= togen_xml_nwfilter_dep ] }, + ] +endif + foreach fuzzer: xml_fuzzers xml_domain_fuzz =3D executable(fuzzer['name'], fuzzer['src'], diff --git a/tests/fuzz/proto_custom_datatypes.cc b/tests/fuzz/proto_custom= _datatypes.cc index d89a6d4f59..a4a54c0116 100644 --- a/tests/fuzz/proto_custom_datatypes.cc +++ b/tests/fuzz/proto_custom_datatypes.cc @@ -87,6 +87,29 @@ std::string convertIPAddr(const Message &message) { } =20 =20 +static +std::string convertIPSetFlags(const Message &message) +{ + std::string value =3D ""; + const libvirt::IPSetFlags &ipset_flags =3D (libvirt::IPSetFlags &) mes= sage; + + uint32_t max_count =3D ipset_flags.max_count() % 7; + uint32_t bitmap =3D ipset_flags.bitarray() & 0x1f; + + for (size_t i =3D 0; i < max_count; i++) { + if ((bitmap >> i) & 1) + value +=3D "src,"; + else + value +=3D "dst,"; + } + + if (value !=3D "") + value.pop_back(); + + return value; +} + + static std::string convertMacAddr(const Message &message) { char value[64] =3D {0}; @@ -104,6 +127,34 @@ std::string convertMacAddr(const Message &message) { } =20 =20 +static +std::string convertStateFlags(const Message &message) +{ + std::string value =3D ""; + const libvirt::StateFlags &state_flags =3D (libvirt::StateFlags &) mes= sage; + + if (state_flags.newflag()) + value +=3D "NEW,"; + + if (state_flags.established()) + value +=3D "ESTABLISHED,"; + + if (state_flags.related()) + value +=3D "RELATED,"; + + if (state_flags.invalid()) + value +=3D "INVALID,"; + + if (value =3D=3D "") + return "NONE"; + + /* Remove trailing comma */ + value.pop_back(); + + return value; +} + + static std::string convertDiskTarget(const Message &message) { @@ -118,12 +169,49 @@ std::string convertDiskTarget(const Message &message) } =20 =20 +static +std::string convertTCPFlags(const Message &message) +{ + std::string value =3D ""; + const libvirt::TCPFlags &tcp_flags =3D (libvirt::TCPFlags &) message; + + if (tcp_flags.syn()) + value +=3D "SYN,"; + + if (tcp_flags.ack()) + value +=3D "ACK,"; + + if (tcp_flags.urg()) + value +=3D "URG,"; + + if (tcp_flags.psh()) + value +=3D "PSH,"; + + if (tcp_flags.fin()) + value +=3D "FIN,"; + + if (tcp_flags.rst()) + value +=3D "RST,"; + + if (value =3D=3D "") + return "NONE"; + + /* Remove trailing comma */ + value.pop_back(); + + return value; +} + + std::unordered_map type_handler_table =3D { {"libvirt.CPUSet", convertCPUSet}, {"libvirt.EmulatorString", convertEmulatorString}, {"libvirt.IPAddr", convertIPAddr}, + {"libvirt.IPSetFlags", convertIPSetFlags}, {"libvirt.MacAddr", convertMacAddr}, + {"libvirt.StateFlags", convertStateFlags}, {"libvirt.TargetDev", convertDiskTarget}, + {"libvirt.TCPFlags", convertTCPFlags}, }; =20 =20 diff --git a/tests/fuzz/proto_header_common.h b/tests/fuzz/proto_header_com= mon.h index 3f135c48e1..4e4beb787b 100644 --- a/tests/fuzz/proto_header_common.h +++ b/tests/fuzz/proto_header_common.h @@ -39,6 +39,10 @@ #include "xml_hotplug.pb.h" #endif =20 +#ifdef XML_NWFILTER +#include "xml_nwfilter.pb.h" +#endif + =20 #define FUZZ_COMMON_INIT(...) \ if (virErrorInitialize() < 0) \ diff --git a/tests/fuzz/protos/meson.build b/tests/fuzz/protos/meson.build index 0731ef1eca..df276aee8b 100644 --- a/tests/fuzz/protos/meson.build +++ b/tests/fuzz/protos/meson.build @@ -4,6 +4,7 @@ protos =3D [ 'xml_domain_disk_only.proto', 'xml_domain_interface_only.proto', 'xml_hotplug.proto', + 'xml_nwfilter.proto', ] =20 autogen_proto_xml_domain_proto =3D custom_target('autogen_xml_domain.proto= ', @@ -12,6 +13,12 @@ autogen_proto_xml_domain_proto =3D custom_target('autoge= n_xml_domain.proto', command : [relaxng_to_proto_prog, '@INPUT@', '@OUTPUT@'], ) =20 +autogen_proto_xml_nwfilter_proto =3D custom_target('autogen_xml_nwfilter.p= roto', + output : 'autogen_xml_nwfilter.proto', + input : meson.project_source_root() / 'src' / 'conf' / 'schemas' / 'nwfi= lter.rng', + command : [relaxng_to_proto_prog, '@INPUT@', '@OUTPUT@'], +) + protoc_generator =3D generator(protoc_prog, output: [ '@BASENAME@.pb.cc', @@ -25,10 +32,12 @@ protoc_generator =3D generator(protoc_prog, ], depends: [ autogen_proto_xml_domain_proto, + autogen_proto_xml_nwfilter_proto, ], ) =20 autogen_xml_domain_proto_src =3D protoc_generator.process(autogen_proto_xm= l_domain_proto) +autogen_xml_nwfilter_src =3D protoc_generator.process(autogen_proto_xml_nw= filter_proto) =20 foreach proto: protos proto_src_name =3D proto.split('.')[0].underscorify() diff --git a/tests/fuzz/protos/xml_datatypes.proto b/tests/fuzz/protos/xml_= datatypes.proto index 1229b9810f..7bf19051cd 100644 --- a/tests/fuzz/protos/xml_datatypes.proto +++ b/tests/fuzz/protos/xml_datatypes.proto @@ -70,3 +70,24 @@ message CPUSet { } =20 message EmulatorString {} + +message TCPFlags { + required bool syn =3D 1; + required bool ack =3D 2; + required bool urg =3D 3; + required bool psh =3D 4; + required bool fin =3D 5; + required bool rst =3D 6; +} + +message StateFlags { + required bool newflag =3D 1; + required bool established =3D 2; + required bool related =3D 3; + required bool invalid =3D 4; +} + +message IPSetFlags { + required uint32 max_count =3D 1; + required uint32 bitarray =3D 2; +} diff --git a/tests/fuzz/protos/xml_nwfilter.proto b/tests/fuzz/protos/xml_n= wfilter.proto new file mode 100644 index 0000000000..459a10f840 --- /dev/null +++ b/tests/fuzz/protos/xml_nwfilter.proto @@ -0,0 +1,9 @@ +syntax =3D "proto2"; + +import "autogen_xml_nwfilter.proto"; + +package libvirt; + +message MainObj { + required filterTag T_filter =3D 1; +} diff --git a/tests/fuzz/xml_nwfilter_fuzz.cc b/tests/fuzz/xml_nwfilter_fuzz= .cc new file mode 100644 index 0000000000..a2c25a38eb --- /dev/null +++ b/tests/fuzz/xml_nwfilter_fuzz.cc @@ -0,0 +1,149 @@ +/* + * xml_nwfilter_fuzz.cc: NWFilter 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 + +extern "C" { +#include "testutils.h" +#include "nwfilter/nwfilter_ebiptables_driver.h" +#include "virbuffer.h" + +#define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW +#include "vircommandpriv.h" +} + +#include "port/protobuf.h" +#include "proto_to_xml.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +bool enable_xml_dump =3D false; + +uint64_t parse_pass =3D 0; +uint64_t apply_rules_pass =3D 0; +uint64_t success =3D 0; + +static int +fuzzNWFilterDefToRules(virNWFilterDef *def) +{ + size_t i; + virNWFilterRuleDef *rule; + virNWFilterRuleInst *ruleinst; + + virNWFilterRuleInst **ruleinsts =3D NULL; + size_t nrules =3D 0; + + g_auto(virBuffer) buf =3D VIR_BUFFER_INITIALIZER; + g_autoptr(virCommandDryRunToken) dryRunToken =3D virCommandDryRunToken= New(); + + int ret =3D -1; + + /* This line is needed to avoid actually running iptables/ebtables */ + virCommandSetDryRun(dryRunToken, &buf, true, true, NULL, NULL); + + for (i =3D 0; i < (size_t) def->nentries; i++) { + /* We handle only elements. is ignored */ + if (!(rule =3D def->filterEntries[i]->rule)) + continue; + + ruleinst =3D g_new0(virNWFilterRuleInst, 1); + + ruleinst->chainSuffix =3D def->chainsuffix; + ruleinst->chainPriority =3D def->chainPriority; + ruleinst->def =3D rule; + ruleinst->priority =3D rule->priority; + ruleinst->vars =3D virHashNew(virNWFilterVarValueHashFree); + + VIR_APPEND_ELEMENT(ruleinsts, nrules, ruleinst); + } + + + if (ebiptables_driver.applyNewRules("vnet0", ruleinsts, nrules) < 0) + goto cleanup; + + ret =3D 0; + + cleanup: + for (i =3D 0; i < nrules; i++) { + g_clear_pointer(&ruleinsts[i]->vars, g_hash_table_unref); + g_free(ruleinsts[i]); + ruleinsts[i] =3D NULL; + } + + if (nrules !=3D 0) + g_free(ruleinsts); + + return ret; +} + + +static void +fuzzNWFilterXML(const char *xml) +{ + virNWFilterDef *def =3D NULL; + + parse_pass++; + if (!(def =3D virNWFilterDefParse(xml, NULL, 0))) + goto cleanup; + + apply_rules_pass++; + + if (fuzzNWFilterDefToRules(def) < 0) + goto cleanup; + + success++; + + cleanup: + virNWFilterDefFree(def); +} + + +DEFINE_PROTO_FUZZER(const libvirt::MainObj &message) +{ + static bool initialized =3D false; + static const char *dump_xml_env =3D g_getenv("LPM_XML_DUMP_INPUT"); + + std::string xml =3D ""; + + if (!initialized) { + FUZZ_COMMON_INIT(); + + /* Enable printing of XML to stdout (useful for debugging crashes)= */ + if (dump_xml_env && STREQ(dump_xml_env, "YES")) + enable_xml_dump =3D true; + + initialized =3D true; + } + + convertProtoToXML(message, xml); + + if (enable_xml_dump) + printf("%s\n", xml.c_str()); + + fuzzNWFilterXML(xml.c_str()); + + if (parse_pass % 1000 =3D=3D 0) + printf("[FUZZ METRICS] Parse: %lu, Apply Rules: %lu, Success: %lu\= n", + parse_pass, apply_rules_pass, success); +} --=20 2.34.1 From nobody Sat Nov 23 15:01:47 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 1724085113282285.5113876155575; Mon, 19 Aug 2024 09:31:53 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 996) id 29797982; Mon, 19 Aug 2024 12:31:52 -0400 (EDT) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id 41B0E1702; Mon, 19 Aug 2024 12:20:55 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 996) id E7A9215E0; Mon, 19 Aug 2024 12:20:43 -0400 (EDT) Received: from mail-ej1-f49.google.com (mail-ej1-f49.google.com [209.85.218.49]) (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 D45FD1621 for ; Mon, 19 Aug 2024 12:20:10 -0400 (EDT) Received: by mail-ej1-f49.google.com with SMTP id a640c23a62f3a-a80ea7084e9so228967966b.0 for ; Mon, 19 Aug 2024 09:20:10 -0700 (PDT) Received: from localhost.localdomain ([37.186.51.21]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-5bebbde4964sm5738298a12.24.2024.08.19.09.20.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 19 Aug 2024 09:20:08 -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_H3,RCVD_IN_MSPIKE_WL,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=1724084409; x=1724689209; 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=c1adgYEBZ8y8QHT6z7C1curS3uXRODS27kGMserVOZA=; b=X30vi79yYyvO784J6YN59CbPU06wlkIeRgLxWigzkLunhD154r6c9v0PJWsCd9yDhH +kETO3US6jZTdcRwNwrK07PbOdNymt68cK1hr2WOi9nG60blKeSQM32oIELYZwkwdVng xGfshbvMjoIRd9m/qBJD7a+mED9c4I8sU/E1b2uqMzQM4ZEtxjo3zT3V9mPIXYZ+3R3l TVcYktqAg0ENYct0rP4sPRVEDg9dJ2c4mmzV9vEcxC4n1TaXcGSVZqbpajXdjMr+JNLU +SWgjcVB9ZGqOtoL9/Xr+/fmdmkC5ICmD2cpXtx6Ym5EfVUf4vvHdvjUlE7sMnmsqiZ4 stLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1724084409; x=1724689209; 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=c1adgYEBZ8y8QHT6z7C1curS3uXRODS27kGMserVOZA=; b=B8TMnqVd9v7XQOwvqTQ2kZJNuQACxJXqn6fBfNWh3PzeIoC4SBdxJ+67K75vb2FcfY sPhqNdwDUZqcpAK8o5dX0ycbkjqEVX5IOloxTtlX81Zh79eEQX22g2g7dMLODL8q3PWc ZLmwD3p+ije7gmDlhr3Uqne2z69N7+R70PptF96j50UM4TQR892u2zO4QkXzQPJjNJ2u MXcTfUIl94g5JOEZNZoC4oce/jLBQISt3SkK2oTIMBKwU1dKGT0b+DG+oGF4fUe2EgzG UVCiaCnDeHwQB5ACCpKhYky3xT0/KirS4UmrFaG0OwzgE4rKehz8x12hgRMYssd/lc7O vViA== X-Gm-Message-State: AOJu0Yz/e8C7P6PDxuasuL4jiNKWWsXG7ZYRu/F8gCMv0GqukoLDAc8S X5rqaCcVyAd7qI508rI/PIuYGUoRLn3YOqZevpbGWolyaUoJiJBsuodWCg== X-Google-Smtp-Source: AGHT+IFhn8XS8VtgaBkY+FyYxS4qO4YXBEZBj+iZXA3ArxWsQIwZpYCMNFZ652cB0Fhv1ZYxrJs8lw== X-Received: by 2002:a05:6402:5506:b0:58b:1a5e:c0e7 with SMTP id 4fb4d7f45d1cf-5beca8cd10bmr6522136a12.35.1724084409014; Mon, 19 Aug 2024 09:20:09 -0700 (PDT) From: Rayhan Faizel To: devel@lists.libvirt.org Subject: [PATCH 14/14] docs: Document the fuzzers Date: Mon, 19 Aug 2024 21:39:52 +0530 Message-Id: <20240819160952.351383-15-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: SUYAJMDQ7VEXORWMNPWAN5SI7IUILNWY X-Message-ID-Hash: SUYAJMDQ7VEXORWMNPWAN5SI7IUILNWY 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: 1724085113857116600 Content-Type: text/plain; charset="utf-8" Document the fuzzers in two ways. 1. Explain the high level working of the fuzzers under docs/kbase. 2. Add README to explain general setup of the fuzzer and its usage. Signed-off-by: Rayhan Faizel --- docs/kbase/index.rst | 3 + docs/kbase/internals/meson.build | 1 + docs/kbase/internals/xml-fuzzing.rst | 120 ++++++++++++++++++++++++ tests/fuzz/README.rst | 131 +++++++++++++++++++++++++++ 4 files changed, 255 insertions(+) create mode 100644 docs/kbase/internals/xml-fuzzing.rst create mode 100644 tests/fuzz/README.rst diff --git a/docs/kbase/index.rst b/docs/kbase/index.rst index e51b35cbfc..9cf6268800 100644 --- a/docs/kbase/index.rst +++ b/docs/kbase/index.rst @@ -116,3 +116,6 @@ Internals =20 `QEMU monitor event handling `__ Brief outline how events emitted by qemu on the monitor are handlded. + +`XML Fuzzing `__ + Working of the structure-aware XML fuzzers. diff --git a/docs/kbase/internals/meson.build b/docs/kbase/internals/meson.= build index f1e9122f8f..86b6639419 100644 --- a/docs/kbase/internals/meson.build +++ b/docs/kbase/internals/meson.build @@ -9,6 +9,7 @@ docs_kbase_internals_files =3D [ 'qemu-migration', 'qemu-threads', 'rpc', + 'xml-fuzzing', ] =20 =20 diff --git a/docs/kbase/internals/xml-fuzzing.rst b/docs/kbase/internals/xm= l-fuzzing.rst new file mode 100644 index 0000000000..85f565fda5 --- /dev/null +++ b/docs/kbase/internals/xml-fuzzing.rst @@ -0,0 +1,120 @@ +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +Libvirt XML fuzzing +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +XML fuzzing is done using libFuzzer and libprotobuf-mutator. XML fuzzing +cannot be done with normal fuzzing methods, as XML is a highly structured +format. Structure-aware fuzzing is implemented using libprotobuf-mutator w= hich +mutates and fuzzes protobuf inputs. Protobufs are used as an intermediate +format and serialized to XML. + +Protobuf to XML representation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D + +A protobuf definition written to fuzz libvirt XML formats may resemble the +following. + +:: + + message MainObj { + message SomeTagMessage { + optional uint32 A_number =3D 1; + optional DummyString A_name =3D 2; + + enum typeEnum { + typeA =3D 0; + typeB =3D 1; + typeC =3D 2; + } + + optional typeEnum A_type =3D 3; + + message InnerTagMessage { + optional uint32 A_number =3D 1; + } + + repeated InnerTagMessage T_innertag =3D 4; + + message SecondInnerTagMessage { + optional uint32 V_value =3D 1; + } + optional SecondInnerTagMessage T_secondinner =3D 5; + } + + optional SomeTagMessage T_sometag =3D 1; + } + +* Fields starting with ``T_`` represent XML tags. Their types are protobuf= messages + which may further contain other protobuf-defined XML tags or attributes. + +* Fields starting with ``A_`` represent XML attributes. Most of the time, + it uses one of the primitive datatypes (Eg: ``uint32``, ``bool``, ``enum= ``, etc. ) available in protobuf. + + * If the attribute can take multiple data types, it is encapsulated in a= ``oneof`` statement. + The field name also has a prefix of ``A_OPTXX_`` where ``XX`` is a num= ber between 0 to 99. + * If the attribute name contains special characters, the real name is st= ored in + ``libvirt::real_name`` which is extended by ``FieldOptions``. + * If an enum value contains special characters, the real value is stored= in + ``libvirt::real_value`` which is extended by ``EnumValueOptions``. + +* Fields starting with ``V_`` represent raw text in XML. + + * If ``T_`` and ``V_`` fields are defined in the same message, ``V_`` fi= elds + will be preferred only if it has presence, otherwise it will process t= he + rest of the ``T`` fields as usual. + * ``V_`` fields can take on the same datatypes as ``A_`` fields. + +* ``repeated`` is used to allow multiple XML tags of the same name. + +``A_`` fields must always precede ``V_`` and ``T_`` fields. Likewise, ``V_= `` +fields must precede ``T_`` fields if any. + +On fuzzing the above protobuf definition, one of the possible protobuf to = XML +serializations could be + +:: + + + + + 1241232 + + +Custom Protobuf Datatypes +------------------------- + +Sometimes, primitive data types or enums are not enough to encode the +desired attribute values, especially if they themselves are structured. In= this +case, such fields are represented by a handwritten protobuf message define= d in +``xml_domain_datatypes.proto``. To serialize these messages to XML attribu= te +values, custom handlers are defined in ``proto_custom_datatypes.cc``. + +This is useful for data types such as IP addresses, MAC addresses, target +device names, etc. + +Protobuf generation +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +``proto`` files are automatically generated on compile-time using the scri= pt +``relaxng_to_proto.py``. The script parses relaxng schemas to generate a p= rotobuf +file containing fields and messages representing all the defined XML tags = and +attributes. + +The script tries to figure out the correct datatype of the XML attribute. +However, on its own it can only figure out the general datatype or enum va= lues +of the attribute but not the constraints or regex patterns. Some override = tables +are present to improve upon that. + +Fuzzer Harnesses +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +Driver-specific harnesses in general re-use the existing test driver setup +as well as other existing test utilities under ``tests/``. Harnesses are +available for the following drivers: + +* QEMU XML Domain +* QEMU XML Hotplug +* CH XML Domain +* VMX XML Domain +* libXL XML Domain +* NWFilter XML diff --git a/tests/fuzz/README.rst b/tests/fuzz/README.rst new file mode 100644 index 0000000000..d92cdc94d7 --- /dev/null +++ b/tests/fuzz/README.rst @@ -0,0 +1,131 @@ +=3D=3D=3D=3D=3D=3D=3D +Fuzzing +=3D=3D=3D=3D=3D=3D=3D + +The XML fuzzing project was built as part of Google Summer of Code 2024. +The fuzzing project aims to find edge-case XML configurations that may cra= sh +libvirt during parsing. The libvirt domain XML format is a highly structur= ed +grammar so normal methods of fuzzing will not work. We use a combination +of libFuzzer and libprotobuf-mutator to perform structure-aware fuzzing of +various libvirt XML formats. The XML is represented through an intermediate +protobuf that is mutated by libprotobuf-mutator. This protobuf is automati= cally +generated by a Python script ``relaxng_to_proto.py`` which parses relaxNG +schemas. + +Currently, we fuzz the following: + +* QEMU XML Domain (qemu_xml_domain_fuzz, qemu_xml_domain_fuzz_disk, qemu_x= ml_domain_fuzz_interface) +* QEMU XML Hotplug (qemu_xml_hotplug_fuzz) +* CH XML Domain (ch_xml_domain_fuzz) +* VMX XML Domain (vmx_xml_domain_fuzz) +* LibXL XML Domain (libxl_xml_domain_fuzz) +* NWFilter XML (xml_nwfilter_fuzz) + +libprotobuf-mutator +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +libprotobuf-mutator is the crux of our fuzzing methodology that +allows us to perform grammar-aware fuzzing of the XML format in the first +place. However, its setup is a bit involved. The general build and install +instructions can be followed in +https://github.com/google/libprotobuf-mutator/blob/master/README.md +but we will have to tweak it depending on the distro. One of the biggest +problems is that most distros have very outdated versions of protobuf +which will cause various build and linkage issues with the mutator. + +- If you are on a rolling release distro, the system package can likely be + used as-is. However, you may need to pass ``-std=3Dc++17`` in ``CXXFLAG= S`` + and ``-Wl,--copy-dt-needed-entries`` in ``LDFLAGS``.\ +- For every other distro with old protobuf installations, you can supply + ``-DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=3DON`` during libprotobuf-mutat= or + setup. After this, provide ``-Dexternal_protobuf_dir=3D`` to libvi= rt + meson setup pointing to the ``external.protobuf`` directory generated + during libprotobuf-mutator compilation. +- On some distros like Fedora which predominantly use PIC compiled + libraries, you may need to pass ``-fPIC`` in ``CFLAGS/CXXFLAGS`` or you + will encounter relocation errors during libvirt compilation. + +Setup +=3D=3D=3D=3D=3D + +:: + + env CC=3Dclang CXX=3Dclang++ \ + meson setup build -Dsystem=3Dtrue -Ddriver_qemu=3Denabled -Db_lundef= =3Dfalse \ + -Db_sanitize=3Daddress,undefined -Dfuz= z=3Denabled -Dexternal_protobuf_dir=3D + +- This command line will introduce LLVM SanitizerCoverage across all + object files. +- libFuzzer is supported only on clang/clang++. +- To use an external protobuf dependency, use + ``-Dexternal_protobuf_dir=3D``. If your system has a new enough pro= tobuf + dependency, you can ignore this. +- ``b_sanitize`` is not compulsory but it does improve the odds of the fuz= zer + finding interesting test cases. It is recommended to pass + ``address,undefined`` to enable both ASAN and UBSan. Note that ASAN will + cut your performance by a factor of 2 on average. +- You can set ``b_sanitize`` to ``thread`` to enable TSAN which is useful = for + fuzzing race conditions in the ``qemu_xml_hotplug_fuzz`` fuzzer especial= ly. + +NOTE: This has only been tested on x86_64 and aarch64 Linux, but should wo= rk +identically on other architectures and possibly even other UNIX based OSes +(BSD, macOS, etc.). + +Usage +=3D=3D=3D=3D=3D + +Run ``./tests/fuzz/run_fuzz ``. + +If the fuzzer finds a crashing test case, it will dump a separate file in = your +working directory. Run +``./tests/fuzz/run_fuzz --testcase `` to reproduce the= crash. +More options to configure the fuzzer can be found with the ``-h`` flag. To= save/ +load a corpus, add ``--corpus ``. + +To merge or minimize corpuses, run +:: + ./tests/fuzz/run_fuzz --libfuzzer-options=3D"-merge=3D1 " + +Notable options are listed below. + +- ``--arch``: Set architecture of the domain XML to fuzz. +- ``-j, --jobs``: Run parallel fuzzing workers using either ``jobs`` or + ``fork`` based on ``--parallel-mode``. Eg: + ``./tests/fuzz/run_fuzz qemu_xml_domain_fuzz -j8 --parallel-mode fork``. +- ``--dump-xml``: Print all fuzzed XMLs (useful for debugging reproducers) +- ``--format-xml``: Exercise format function on XML domain fuzzers. +- ``--corpus``: Save or use corpus on-disk. +- ``--libfuzzer-options``: Pass additional libFuzzer flags as documented in + https://llvm.org/docs/LibFuzzer.html#options. + +Coverage Report +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +- libvirt supports instrumenting builds with gcov for coverage data colle= ction + using ``-Dtest_coverage=3Dtrue``. +:: + + ./tests/fuzz/run_fuzz --total_time=3D --corpus=3D + ./tests/fuzz/run_fuzz --corpus=3D --libfuzzer-opt= ions=3D"-runs=3D0" + find -name '*.gcda' -exec llvm-cov gcov {} \; # Run in build directory + gcovr --gcov-executable "llvm-cov gcov" --html-details coverage.html -= r + +- Alternatively, we can use clang profile coverage instrumentation + enabled with ``-Dtest_coverage_clang=3Dtrue``. +:: + + ./tests/fuzz/run_fuzz --total_time=3D --corpus=3D + ./tests/fuzz/run_fuzz --corpus=3D --llvm-profile-= file=3Dcoverage.profraw + llvm-profdata merge coverage.profraw -output coverage.profdata + llvm-cov show --instr-profile coverage.profdata --sources --format html > coverage.html + +Tips +=3D=3D=3D=3D + +- libFuzzer will try to pass comparison checks using its internal TORC + (Table of Recent Comparisons), but this can get easily overwhelmed in t= he + case of libvirt due to its code being quite complex. You can alleviate + this to some extent by passing ``--use-value-profile`` to the fuzzer. +- If you want the fuzzer to proceed even after encountering a crash, + add ``-j --parallel-mode=3Dfork``. Do note that the memory usage will + increase exponentially with each parallel fuzzing worker. --=20 2.34.1