From c81fe5e55c8ca0ba9eb8f01a2185b11af86a29ca Mon Sep 17 00:00:00 2001 From: shenhy123 Date: Wed, 1 Sep 2021 09:53:39 +0800 Subject: [PATCH] Add vhostmd --- ...ax-virtio-requirement-in-config-file.patch | 42 ++ ...bmetrics-Set-pointer-NULL-after-free.patch | 58 +++ ...ialize-local-variable-ret-to-failure.patch | 125 +++++ ...trics-Check-return-value-of-asprintf.patch | 67 +++ ...Remove-unsafe-XML_PARSE_NOENT-option.patch | 41 ++ ...e-libmetrics-mutex-is-unlocked-in-er.patch | 53 +++ ...libmetrics-Fix-potential-memory-leak.patch | 49 ++ ...roper-conversion-specifier-when-call.patch | 40 ++ ...s-Fix-potential-leak-of-FILE-pointer.patch | 75 +++ 0010-util-Add-missing-call-to-va_end.patch | 55 +++ 0011-util-Fix-potential-memory-leak.patch | 40 ++ 0012-util-Check-return-value-of-strstr.patch | 38 ++ 0013-Check-return-value-of-asprintf.patch | 48 ++ ...-Fix-memory-leak-in-parse_transports.patch | 36 ++ ...Remove-unsafe-XML_PARSE_NOENT-option.patch | 36 ++ ...Check-return-value-of-file-functions.patch | 94 ++++ ...r-valide-file-handle-before-calling-.patch | 38 ++ ...ostmd-Fix-memory-leak-in-vhostmd_run.patch | 46 ++ ...-virtio-Fix-strncpy-length-parameter.patch | 36 ++ vhostmd-1.1.tar.gz | Bin 0 -> 57218 bytes vhostmd.conf | 431 ++++++++++++++++++ vhostmd.spec | 123 +++++ 22 files changed, 1571 insertions(+) create mode 100644 0001-Relax-virtio-requirement-in-config-file.patch create mode 100644 0002-libmetrics-Set-pointer-NULL-after-free.patch create mode 100644 0003-libmetrics-Initialize-local-variable-ret-to-failure.patch create mode 100644 0004-libmetrics-Check-return-value-of-asprintf.patch create mode 100644 0005-libmetrics-Remove-unsafe-XML_PARSE_NOENT-option.patch create mode 100644 0006-libmetrics-Ensure-libmetrics-mutex-is-unlocked-in-er.patch create mode 100644 0007-libmetrics-Fix-potential-memory-leak.patch create mode 100644 0008-libmetrics-Use-proper-conversion-specifier-when-call.patch create mode 100644 0009-libmetrics-Fix-potential-leak-of-FILE-pointer.patch create mode 100644 0010-util-Add-missing-call-to-va_end.patch create mode 100644 0011-util-Fix-potential-memory-leak.patch create mode 100644 0012-util-Check-return-value-of-strstr.patch create mode 100644 0013-Check-return-value-of-asprintf.patch create mode 100644 0014-vhostmd-Fix-memory-leak-in-parse_transports.patch create mode 100644 0015-vhostmd-Remove-unsafe-XML_PARSE_NOENT-option.patch create mode 100644 0016-vhostmd-Check-return-value-of-file-functions.patch create mode 100644 0017-vhostmd-Check-for-valide-file-handle-before-calling-.patch create mode 100644 0018-vhostmd-Fix-memory-leak-in-vhostmd_run.patch create mode 100644 0019-virtio-Fix-strncpy-length-parameter.patch create mode 100644 vhostmd-1.1.tar.gz create mode 100755 vhostmd.conf create mode 100644 vhostmd.spec diff --git a/0001-Relax-virtio-requirement-in-config-file.patch b/0001-Relax-virtio-requirement-in-config-file.patch new file mode 100644 index 0000000..f14d062 --- /dev/null +++ b/0001-Relax-virtio-requirement-in-config-file.patch @@ -0,0 +1,42 @@ +From 83cc269f6892852be94467cea771b3ad1da8a369 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 8 Oct 2019 20:56:18 -0600 +Subject: [PATCH 01/19] Relax virtio requirement in config file + +When the virtio transport was introduced the schema was changed to +require a transport in vhostmd.conf. When updating existing +deployments without a virtio transport specified in vhostmd.conf, +vhostmd fails to start + +/usr/sbin/vhostmd -d +/etc/vhostmd/vhostmd.conf:41: element globals: validity error : Element +globals content does not follow the DTD, expecting (disk , virtio , +update_period , path , transport+), got (disk update_period path transport ) +validate_config_file(): Failed to validate :/etc/vhostmd/vhostmd.conf +Config file: /etc/vhostmd/vhostmd.conf, fails DTD validation + +Relax the requirement for virtio transport in the schema. With the +introduction of multiple transports perhaps the others shoud be optional +as well, but requiring virtio is clearly a regression. + +Signed-off-by: Jim Fehlig +--- + vhostmd.dtd | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/vhostmd.dtd b/vhostmd.dtd +index db417fd..888270e 100644 +--- a/vhostmd.dtd ++++ b/vhostmd.dtd +@@ -9,7 +9,7 @@ Virtual Host Metrics Daemon (vhostmd). Configuration file DTD + --> + + +- ++ + + + +-- +2.32.0 + diff --git a/0002-libmetrics-Set-pointer-NULL-after-free.patch b/0002-libmetrics-Set-pointer-NULL-after-free.patch new file mode 100644 index 0000000..5a1297a --- /dev/null +++ b/0002-libmetrics-Set-pointer-NULL-after-free.patch @@ -0,0 +1,58 @@ +From 06e73264b2338d20aa6e3f17b9820be3768439bf Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 16:14:18 -0700 +Subject: [PATCH 02/19] libmetrics: Set pointer NULL after free + +From a coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c:185:10: warning: Attempt to free released memory + free(mdisk->buffer); + +Some error conditions will call mdisk_content_free(), which could result +in a double-free when the entire mdisk is freed and mdisk_content_free() +is called again. Protect agains these potential double-frees by setting +the mdisk contents NULL after freeing them. + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 24 ++++++++++++++++-------- + 1 file changed, 16 insertions(+), 8 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 6c80681..a5582e7 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -177,14 +177,22 @@ static metric_disk * mdisk_alloc() + static void mdisk_content_free() + { + if (mdisk) { +- if (mdisk->doc) +- xmlFreeDoc(mdisk->doc); +- if (mdisk->pctxt) +- xmlFreeParserCtxt(mdisk->pctxt); +- if (mdisk->buffer) +- free(mdisk->buffer); +- if (mdisk->disk_name) +- free(mdisk->disk_name); ++ if (mdisk->doc) { ++ xmlFreeDoc(mdisk->doc); ++ mdisk->doc = NULL; ++ } ++ if (mdisk->pctxt) { ++ xmlFreeParserCtxt(mdisk->pctxt); ++ mdisk->pctxt = NULL; ++ } ++ if (mdisk->buffer) { ++ free(mdisk->buffer); ++ mdisk->buffer = NULL; ++ } ++ if (mdisk->disk_name) { ++ free(mdisk->disk_name); ++ mdisk->disk_name = NULL; ++ } + } + } + +-- +2.32.0 + diff --git a/0003-libmetrics-Initialize-local-variable-ret-to-failure.patch b/0003-libmetrics-Initialize-local-variable-ret-to-failure.patch new file mode 100644 index 0000000..290ead0 --- /dev/null +++ b/0003-libmetrics-Initialize-local-variable-ret-to-failure.patch @@ -0,0 +1,125 @@ +From 9db959c9e3f83cb2a4fc07534462e769990d9631 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 16:33:24 -0700 +Subject: [PATCH 03/19] libmetrics: Initialize local variable 'ret' to failure + +The get_mdef() and dump_xenstore_metrics() functions have local variables +named 'ret' that are initialized to zero, meaning success. In failure paths +'ret' is set to -1 before jumping to the 'out' label. Invert this logic by +initializing 'ret' to -1 and only setting it to success after the function +bodies have successfully executed. + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 16 ++++------------ + 1 file changed, 4 insertions(+), 12 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index a5582e7..49b38ea 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -228,7 +228,7 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + xmlNodePtr node; + char *str; + char *xpath; +- int ret = 0; ++ int ret = -1; + + ctxt = xmlXPathNewContext(mdisk->doc); + if (!ctxt) { +@@ -242,19 +242,16 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + if ((obj == NULL) || (obj->type != XPATH_NODESET)) { + libmsg("%s(): No metrics found that matches %s in context:%s or malformed definition\n", + __func__, pmdef->name, pmdef->context); +- ret = -1; + goto out; + } + if (xmlXPathNodeSetGetLength(obj->nodesetval) != 1) { + libmsg("%s(): No metrics found that matches %s in context:%s or malformed definition\n", + __func__, pmdef->name, pmdef->context); +- ret = -1; + goto out; + } + node = obj->nodesetval->nodeTab[0]; + if ((str = (char *)xmlGetProp(node, BAD_CAST "type")) == NULL) { + libmsg("%s(): Metric type not specified\n", __func__); +- ret = -1; + goto out; + } + metric_type_from_str((char *)str, &(pmdef->type)); +@@ -267,7 +264,6 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + free(xpath); + if ((obj == NULL) || (obj->type != XPATH_NODESET)) { + libmsg("%s(): No metrics value found!\n", __func__); +- ret = -1; + goto out; + } + +@@ -276,6 +272,7 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + str = (char *)xmlNodeListGetString(mdisk->doc, node, 1); + pmdef->value = strdup(str); + free(str); ++ ret = 0; + + out: + if (obj) +@@ -707,7 +704,7 @@ int dump_xenstore_metrics(const char *dest_file) + char *buf = NULL, *path = NULL, *metrics = NULL; + struct xs_handle *xsh = NULL; + unsigned int len; +- int ret = 0; ++ int ret = -1; + xmlParserCtxtPtr pctxt = NULL; + xmlDocPtr doc = NULL; + int domid; +@@ -726,35 +723,30 @@ int dump_xenstore_metrics(const char *dest_file) + + if ((domid = get_dom_id()) == -1) { + libmsg("Unable to derive domID.\n" ); +- ret = -1; + goto out; + } + + xsh = xs_domain_open(); + if (xsh == NULL) { + libmsg("xs_domain_open() error. errno: %d.\n", errno); +- ret = -1; + goto out; + } + + path = xs_get_domain_path(xsh, domid); + if (path == NULL) { + libmsg("xs_get_domain_path() error. domid %d.\n", 0); +- ret = -1; + goto out; + } + asprintf(&buf, "%s/metrics", path); + metrics = xs_read(xsh, XBT_NULL, buf, &len); + if (metrics == NULL) { + libmsg("xs_read(): uuid get error. %s.\n", buf); +- ret = -1; + goto out; + } + + pctxt = xmlNewParserCtxt(); + if (!pctxt || !pctxt->sax) { + libmsg("%s(): failed to create parser \n", __func__); +- ret = -1; + goto out; + } + +@@ -764,10 +756,10 @@ int dump_xenstore_metrics(const char *dest_file) + XML_PARSE_NOWARNING); + if (!doc) { + libmsg("%s(): libxml failed to xenstore metrics attribute\n", __func__); +- ret = -1; + goto out; + } + xmlDocFormatDump(fp, doc, 1); ++ ret = 0; + + out: + if (fp && fp != stdout) +-- +2.32.0 + diff --git a/0004-libmetrics-Check-return-value-of-asprintf.patch b/0004-libmetrics-Check-return-value-of-asprintf.patch new file mode 100644 index 0000000..967b012 --- /dev/null +++ b/0004-libmetrics-Check-return-value-of-asprintf.patch @@ -0,0 +1,67 @@ +From f659ec774221532cc5452a07418e2ab1385f162c Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 16:43:21 -0700 +Subject: [PATCH 04/19] libmetrics: Check return value of asprintf + +Exmaple from coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c: scope_hint: In function 'get_mdef' +vhostmd-1.1/libmetrics/libmetrics.c:231:4: warning: ignoring return value of 'asprintf', declared with attribute warn_unused_result [-Wunused-result] + asprintf(&xpath, "//metrics/metric[name='%s'][@context='%s']", pmdef->name, pmdef->context); + ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 49b38ea..4b2369a 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -236,7 +236,9 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + } + + /* Get the matching metric node type */ +- asprintf(&xpath, "//metrics/metric[name='%s'][@context='%s']", pmdef->name, pmdef->context); ++ if (asprintf(&xpath, "//metrics/metric[name='%s'][@context='%s']", pmdef->name, pmdef->context) < 0) ++ goto out; ++ + obj = xmlXPathEval(BAD_CAST xpath, ctxt); + free(xpath); + if ((obj == NULL) || (obj->type != XPATH_NODESET)) { +@@ -259,7 +261,9 @@ static int get_mdef(metric_disk *mdisk, private_metric *pmdef) + xmlXPathFreeObject(obj); + + /* Get the matching metric node value */ +- asprintf(&xpath, "//metrics/metric[name='%s'][@context='%s']/value/text()", pmdef->name, pmdef->context); ++ if (asprintf(&xpath, "//metrics/metric[name='%s'][@context='%s']/value/text()", pmdef->name, pmdef->context) < 0) ++ goto out; ++ + obj = xmlXPathEval( BAD_CAST xpath, ctxt); /* worked but no nodes */ + free(xpath); + if ((obj == NULL) || (obj->type != XPATH_NODESET)) { +@@ -349,7 +353,8 @@ retry: + strcmp(entry->d_name, "..") == 0) + continue; + +- asprintf(&path, "/dev/%s", entry->d_name); ++ if (asprintf(&path, "/dev/%s", entry->d_name) < 0) ++ goto error; + #else + path = strdup("/dev/shm/vhostmd0"); + #endif +@@ -737,7 +742,9 @@ int dump_xenstore_metrics(const char *dest_file) + libmsg("xs_get_domain_path() error. domid %d.\n", 0); + goto out; + } +- asprintf(&buf, "%s/metrics", path); ++ if (asprintf(&buf, "%s/metrics", path) , 0) ++ goto out; ++ + metrics = xs_read(xsh, XBT_NULL, buf, &len); + if (metrics == NULL) { + libmsg("xs_read(): uuid get error. %s.\n", buf); +-- +2.32.0 + diff --git a/0005-libmetrics-Remove-unsafe-XML_PARSE_NOENT-option.patch b/0005-libmetrics-Remove-unsafe-XML_PARSE_NOENT-option.patch new file mode 100644 index 0000000..8b64921 --- /dev/null +++ b/0005-libmetrics-Remove-unsafe-XML_PARSE_NOENT-option.patch @@ -0,0 +1,41 @@ +From 3d4f3acdfc9f937bea946bb1c7dfad1f3516a6ce Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 17:42:39 -0700 +Subject: [PATCH 05/19] libmetrics: Remove unsafe XML_PARSE_NOENT option + +From coverity scan + +Error: UNSAFE_XML_PARSE_CONFIG: +vhostmd-1.1/libmetrics/libmetrics.c:412: unsafe_xml_parse_config: XML parse option should not have flag "XML_PARSE_NOENT" set, which is vulnerable to XML external entity attack. + 410| mdisk->doc = xmlCtxtReadMemory(mdisk->pctxt, mdisk->buffer, + 411| mdisk->length, "mdisk.xml", NULL, + 412|-> XML_PARSE_NOENT | XML_PARSE_NONET | + 413| XML_PARSE_NOWARNING); + 414| if (!mdisk->doc) { + +It should be safe to remove the option. + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 4b2369a..2819f80 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -418,9 +418,8 @@ retry: + } + + mdisk->doc = xmlCtxtReadMemory(mdisk->pctxt, mdisk->buffer, +- mdisk->length, "mdisk.xml", NULL, +- XML_PARSE_NOENT | XML_PARSE_NONET | +- XML_PARSE_NOWARNING); ++ mdisk->length, "mdisk.xml", NULL, ++ XML_PARSE_NONET | XML_PARSE_NOWARNING); + if (!mdisk->doc) { + libmsg("%s(): libxml failed to parse mdisk.xml buffer\n", __func__); + goto error; +-- +2.32.0 + diff --git a/0006-libmetrics-Ensure-libmetrics-mutex-is-unlocked-in-er.patch b/0006-libmetrics-Ensure-libmetrics-mutex-is-unlocked-in-er.patch new file mode 100644 index 0000000..f23d4af --- /dev/null +++ b/0006-libmetrics-Ensure-libmetrics-mutex-is-unlocked-in-er.patch @@ -0,0 +1,53 @@ +From 855326a8c9ec1a599354f743fb5391a2766a2a9c Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 17:47:10 -0700 +Subject: [PATCH 06/19] libmetrics: Ensure libmetrics mutex is unlocked in + error paths + +From coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c:595: missing_unlock: Returning without unlocking "libmetrics_mutex". + 593| if (mdisk_alloc() == NULL) { + 594| errno = ENOMEM; + 595|-> return -1; + 596| } + 597| read_mdisk(mdisk); + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 2819f80..756645c 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -601,7 +601,7 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + mdisk_free(); + if (mdisk_alloc() == NULL) { + errno = ENOMEM; +- return -1; ++ goto out; + } + read_mdisk(mdisk); + } +@@ -620,7 +620,7 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + + if ((lmdef = metric_alloc_padded(extra_len)) == NULL) { + errno = ENOMEM; +- return -1; ++ goto out; + } + + lmdef->type = pmdef.type; +@@ -635,6 +635,7 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + if (pmdef.context) + free(pmdef.context); + ++out: + /* unlock library data */ + pthread_mutex_unlock(&libmetrics_mutex); + return ret; +-- +2.32.0 + diff --git a/0007-libmetrics-Fix-potential-memory-leak.patch b/0007-libmetrics-Fix-potential-memory-leak.patch new file mode 100644 index 0000000..4f20b0b --- /dev/null +++ b/0007-libmetrics-Fix-potential-memory-leak.patch @@ -0,0 +1,49 @@ +From 2cbce5fcb652226bf159f9cd9649f5869d76f6b7 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 18:40:33 -0700 +Subject: [PATCH 07/19] libmetrics: Fix potential memory leak + +From coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c:613:18: warning: Potential leak of memory pointed to by 'pmdef.context' + errno = ENOMEM; + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 756645c..6817df9 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -586,7 +586,8 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + int ret = -1; + + *mdef = NULL; +- ++ memset(&pmdef, 0, sizeof(private_metric)); ++ + if (mdisk == NULL) { + errno = ENODEV; + return -1; +@@ -628,6 +629,8 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + *mdef = lmdef; + ret = 0; + } ++ ++out: + if (pmdef.name) + free(pmdef.name); + if (pmdef.value) +@@ -635,7 +638,6 @@ int get_metric(const char *metric_name, metric **mdef, metric_context context) + if (pmdef.context) + free(pmdef.context); + +-out: + /* unlock library data */ + pthread_mutex_unlock(&libmetrics_mutex); + return ret; +-- +2.32.0 + diff --git a/0008-libmetrics-Use-proper-conversion-specifier-when-call.patch b/0008-libmetrics-Use-proper-conversion-specifier-when-call.patch new file mode 100644 index 0000000..5200180 --- /dev/null +++ b/0008-libmetrics-Use-proper-conversion-specifier-when-call.patch @@ -0,0 +1,40 @@ +From bc5da0b99699bbeb653b86398a7112bc0885c31e Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 18:51:40 -0700 +Subject: [PATCH 08/19] libmetrics: Use proper conversion specifier when + calling log function + +From coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c:817: invalid_type: Argument "5L" to format specifier "%u" was expected to have type "unsigned int" but has type "long". + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 6817df9..0f4cf70 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -823,7 +823,7 @@ static char *get_virtio_metrics(void) + usleep(10000); + if (time(NULL) > end_time) { + libmsg("%s(): Unable to send metrics request" +- " - timeout after %us\n", __func__, timeout); ++ " - timeout after %lis\n", __func__, timeout); + goto error; + } + } +@@ -856,7 +856,7 @@ static char *get_virtio_metrics(void) + usleep(10000); + if (time(NULL) > end_time) { + libmsg("%s(): Unable to read metrics" +- " - timeout after %us\n", __func__, timeout); ++ " - timeout after %lis\n", __func__, timeout); + goto error; + } + } else +-- +2.32.0 + diff --git a/0009-libmetrics-Fix-potential-leak-of-FILE-pointer.patch b/0009-libmetrics-Fix-potential-leak-of-FILE-pointer.patch new file mode 100644 index 0000000..8206fd2 --- /dev/null +++ b/0009-libmetrics-Fix-potential-leak-of-FILE-pointer.patch @@ -0,0 +1,75 @@ +From cba4dddebc56886034038f907085da3c6b50baab Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Mon, 6 Jan 2020 18:59:18 -0700 +Subject: [PATCH 09/19] libmetrics: Fix potential leak of FILE pointer + +From coverity scan + +vhostmd-1.1/libmetrics/libmetrics.c:892: alloc_fn: Storage is returned from allocation function "fopen". +vhostmd-1.1/libmetrics/libmetrics.c:892: var_assign: Assigning: "fp" = storage returned from "fopen(dest_file, "w")". +vhostmd-1.1/libmetrics/libmetrics.c:900: noescape: Resource "fp" is not freed or pointed-to in "fwrite". +vhostmd-1.1/libmetrics/libmetrics.c:909: leaked_storage: Variable "fp" going out of scope leaks the storage it points to. + 907| free(response); + 908| + 909|-> return 0; + 910| + 911| error: + +Signed-off-by: Jim Fehlig +--- + libmetrics/libmetrics.c | 16 +++++++--------- + 1 file changed, 7 insertions(+), 9 deletions(-) + +diff --git a/libmetrics/libmetrics.c b/libmetrics/libmetrics.c +index 0f4cf70..8819074 100644 +--- a/libmetrics/libmetrics.c ++++ b/libmetrics/libmetrics.c +@@ -890,10 +890,11 @@ int dump_virtio_metrics(const char *dest_file) + FILE *fp = stdout; + char *response = NULL; + size_t len; ++ int ret = -1; + + response = get_virtio_metrics(); + if (response == NULL) +- goto error; ++ return -1; + + len = strlen(response); + +@@ -902,27 +903,24 @@ int dump_virtio_metrics(const char *dest_file) + if (fp == NULL) { + libmsg("%s(), unable to dump metrics: fopen(%s) %s\n", + __func__, dest_file, strerror(errno)); +- goto error; ++ goto out; + } + } + + if (fwrite(response, 1UL, len, fp) != len) { + libmsg("%s(), unable to export metrics to file:%s %s\n", + __func__, dest_file ? dest_file : "stdout", strerror(errno)); +- goto error; ++ goto out; + } + +- if (response) +- free(response); ++ ret = 0; + +- return 0; +- +- error: ++out: + if (dest_file && fp) + fclose(fp); + + if (response) + free(response); + +- return -1; ++ return ret; + } +-- +2.32.0 + diff --git a/0010-util-Add-missing-call-to-va_end.patch b/0010-util-Add-missing-call-to-va_end.patch new file mode 100644 index 0000000..dfe25fd --- /dev/null +++ b/0010-util-Add-missing-call-to-va_end.patch @@ -0,0 +1,55 @@ +From 69fcc2075d7cb1f16eb7d27ae3559fa3c77f5514 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 7 Jan 2020 11:36:56 -0700 +Subject: [PATCH 10/19] util: Add missing call to va_end + +From coverity scan + +Error: VARARGS (CWE-237): +vhostmd-1.1/vhostmd/util.c:209: va_init: Initializing va_list "argptr". +vhostmd-1.1/vhostmd/util.c:218: missing_va_end: va_end was not called for "argptr". + 216| grow_size = (count > 1000) ? count : 1000; + 217| if (buffer_grow(buf, grow_size) < 0) + 218|-> return; + 219| + 220| size = buf->size - buf->use - 1; + +Error: VARARGS (CWE-237): +vhostmd-1.1/vhostmd/util.c:209: va_init: Initializing va_list "argptr". +vhostmd-1.1/vhostmd/util.c:226: missing_va_end: va_end was not called for "argptr". + 224| buf->use += count; + 225| buf->content[buf->use] = '\0'; + 226|-> } + 227| + 228| /* + +Signed-off-by: Jim Fehlig +--- + vhostmd/util.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/vhostmd/util.c b/vhostmd/util.c +index 5747f16..1f9545b 100644 +--- a/vhostmd/util.c ++++ b/vhostmd/util.c +@@ -214,13 +214,16 @@ void vu_buffer_vsprintf(vu_buffer *buf, const char *format, ...) + va_end(locarg); + + grow_size = (count > 1000) ? count : 1000; +- if (buffer_grow(buf, grow_size) < 0) ++ if (buffer_grow(buf, grow_size) < 0) { ++ va_end(argptr); + return; ++ } + + size = buf->size - buf->use - 1; + va_copy(locarg, argptr); + } + va_end(locarg); ++ va_end(argptr); + buf->use += count; + buf->content[buf->use] = '\0'; + } +-- +2.32.0 + diff --git a/0011-util-Fix-potential-memory-leak.patch b/0011-util-Fix-potential-memory-leak.patch new file mode 100644 index 0000000..418b678 --- /dev/null +++ b/0011-util-Fix-potential-memory-leak.patch @@ -0,0 +1,40 @@ +From b39f7cc778903e9cda8aa46d170b9efe80efda89 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 7 Jan 2020 11:41:11 -0700 +Subject: [PATCH 11/19] util: Fix potential memory leak + +From coverity scan + +vhostmd-1.1/vhostmd/util.c:415:14: warning: Potential leak of memory pointed to by 'cp' + return(NULL); + +Signed-off-by: Jim Fehlig +--- + vhostmd/util.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/vhostmd/util.c b/vhostmd/util.c +index 1f9545b..599c5c7 100644 +--- a/vhostmd/util.c ++++ b/vhostmd/util.c +@@ -415,7 +415,7 @@ char *vu_str_replace(const char *haystack, const char *origstr, const char *news + + dest = malloc(strlen(haystack) - (origlen * cnt) + (newlen * cnt) + 1); + if (dest == NULL) { +- return(NULL); ++ goto out; + } + *dest = '\0'; + +@@ -428,6 +428,8 @@ char *vu_str_replace(const char *haystack, const char *origstr, const char *news + cp = p + origlen; + } + strcat(dest, cp); ++ ++out: + free(tempstr); + + return dest; +-- +2.32.0 + diff --git a/0012-util-Check-return-value-of-strstr.patch b/0012-util-Check-return-value-of-strstr.patch new file mode 100644 index 0000000..0f4219a --- /dev/null +++ b/0012-util-Check-return-value-of-strstr.patch @@ -0,0 +1,38 @@ +From 4350fcf7a5c4cb7d803db1a0df2b757230b90e25 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 7 Jan 2020 11:47:33 -0700 +Subject: [PATCH 12/19] util: Check return value of strstr + +From coverity scan + +vhostmd-1.1/vhostmd/util.c:421: returned_null: "strstr" returns "NULL" (checked 6 out of 7 times). + +Signed-off-by: Jim Fehlig +--- + vhostmd/util.c | 10 ++++++---- + 1 file changed, 6 insertions(+), 4 deletions(-) + +diff --git a/vhostmd/util.c b/vhostmd/util.c +index 599c5c7..317dbc6 100644 +--- a/vhostmd/util.c ++++ b/vhostmd/util.c +@@ -422,10 +422,12 @@ char *vu_str_replace(const char *haystack, const char *origstr, const char *news + cp = tempstr; + for (i=0; i +Date: Tue, 7 Jan 2020 11:52:23 -0700 +Subject: [PATCH 13/19] Check return value of asprintf + +Example from coverity scan + +vhostmd-1.1/vhostmd/util.c: scope_hint: In function 'vu_append_string' +vhostmd-1.1/vhostmd/util.c:484:7: warning: ignoring return value of 'asprintf', declared with attribute warn_unused_result [-Wunused-result] + asprintf(&cp, "%s,%s", *dest, str); + +Signed-off-by: Jim Fehlig +--- + vhostmd/util.c | 3 ++- + vhostmd/vhostmd.c | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/vhostmd/util.c b/vhostmd/util.c +index 317dbc6..d7ce3fc 100644 +--- a/vhostmd/util.c ++++ b/vhostmd/util.c +@@ -488,7 +488,8 @@ int vu_append_string(char **dest, xmlChar * str) + char *cp; + + if (*dest) { +- asprintf(&cp, "%s,%s", *dest, str); ++ if (asprintf(&cp, "%s,%s", *dest, str) < 0) ++ return -1; + free(*dest); + *dest = cp; + } +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 7e29e6f..7374ec9 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -259,7 +259,8 @@ static int parse_group_metric(xmlDocPtr xml ATTRIBUTE_UNUSED, + vu_log(VHOSTMD_WARN, "parse_group_metric: node path not found"); + return -1; + } +- asprintf(&cp, "%s/variable", path); ++ if (asprintf(&cp, "%s/variable", path) < 0) ++ goto error; + + obj = xmlXPathEval( BAD_CAST cp, ctxt); + if ((obj == NULL) || (obj->type != XPATH_NODESET)) { +-- +2.32.0 + diff --git a/0014-vhostmd-Fix-memory-leak-in-parse_transports.patch b/0014-vhostmd-Fix-memory-leak-in-parse_transports.patch new file mode 100644 index 0000000..3b838e5 --- /dev/null +++ b/0014-vhostmd-Fix-memory-leak-in-parse_transports.patch @@ -0,0 +1,36 @@ +From 71a94a18c470ebbd870253b8aedbb7b3b24a274b Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 15:08:54 -0700 +Subject: [PATCH 14/19] vhostmd: Fix memory leak in parse_transports + +From coverity scan + +vhostmd-1.1/vhostmd/vhostmd.c:455: alloc_fn: Storage is returned from allocation function "xmlXPathEval". +vhostmd-1.1/vhostmd/vhostmd.c:455: var_assign: Assigning: "obj" = storage returned from "xmlXPathEval((xmlChar *)"//vhostmd/globals/transport", ctxt)". +vhostmd-1.1/vhostmd/vhostmd.c:474: leaked_storage: Variable "obj" going out of scope leaks the storage it points to. + 472| #else + 473| vu_log (VHOSTMD_ERR, "No support for xenstore transport in this vhostmd"); + 474|-> return -1; + 475| #endif + 476| } + +Signed-off-by: Jim Fehlig +--- + vhostmd/vhostmd.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 7374ec9..3d1d53e 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -472,6 +472,7 @@ static int parse_transports(xmlDocPtr xml, + transports |= XENSTORE; + #else + vu_log (VHOSTMD_ERR, "No support for xenstore transport in this vhostmd"); ++ xmlXPathFreeObject(obj); + return -1; + #endif + } +-- +2.32.0 + diff --git a/0015-vhostmd-Remove-unsafe-XML_PARSE_NOENT-option.patch b/0015-vhostmd-Remove-unsafe-XML_PARSE_NOENT-option.patch new file mode 100644 index 0000000..9872d7d --- /dev/null +++ b/0015-vhostmd-Remove-unsafe-XML_PARSE_NOENT-option.patch @@ -0,0 +1,36 @@ +From d9eeede678521776d327784d0307de6c98920bb8 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 15:12:28 -0700 +Subject: [PATCH 15/19] vhostmd: Remove unsafe XML_PARSE_NOENT option + +From coverity scan + +vhostmd-1.1/vhostmd/vhostmd.c:553: unsafe_xml_parse_config: XML parse option should not have flag "XML_PARSE_NOENT" set, which is vulnerable to XML external entty attack. + 551| + 552| xml = xmlCtxtReadFile(pctxt, filename, NULL, + 553|-> XML_PARSE_NOENT | XML_PARSE_NONET | + 554| XML_PARSE_NOWARNING); + 555| if (!xml) { + +Signed-off-by: Jim Fehlig +--- + vhostmd/vhostmd.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 3d1d53e..4d04989 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -552,8 +552,7 @@ static int parse_config_file(const char *filename) + goto out; + + xml = xmlCtxtReadFile(pctxt, filename, NULL, +- XML_PARSE_NOENT | XML_PARSE_NONET | +- XML_PARSE_NOWARNING); ++ XML_PARSE_NONET | XML_PARSE_NOWARNING); + if (!xml) { + vu_log(VHOSTMD_ERR, "libxml failed to parse config file %s", + filename); +-- +2.32.0 + diff --git a/0016-vhostmd-Check-return-value-of-file-functions.patch b/0016-vhostmd-Check-return-value-of-file-functions.patch new file mode 100644 index 0000000..2597209 --- /dev/null +++ b/0016-vhostmd-Check-return-value-of-file-functions.patch @@ -0,0 +1,94 @@ +From 4f7b23e19c88c92d834d5f975c846b47eaa03c79 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 15:33:39 -0700 +Subject: [PATCH 16/19] vhostmd: Check return value of file functions + +Check return value of ftruncate, lseek, and write functions as +reported by coverity. Example from coverity scan + +vhostmd-1.1/vhostmd/vhostmd.c: scope_hint: In function 'metrics_disk_create' +vhostmd-1.1/vhostmd/vhostmd.c:821:4: warning: ignoring return value of 'ftruncate', declared with attribute warn_unused_result [-Wunused-result] + ftruncate(fd, mdisk_size); + ^~~~~~~~~~~~~~~~~~~~~~~~~ + 819| + 820| /* truncate to a possible new size */ + 821|-> ftruncate(fd, mdisk_size); + 822| + 823| /* zero fill metrics data */ + +Signed-off-by: Jim Fehlig +--- + vhostmd/vhostmd.c | 33 +++++++++++++++++++++++++-------- + 1 file changed, 25 insertions(+), 8 deletions(-) + +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 4d04989..1600a87 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -675,8 +675,12 @@ static int metrics_disk_busy(int fd, int busy) + { + md_header.busy = (uint32_t)(htonl(busy)); + +- lseek(fd, offsetof(mdisk_header, busy), SEEK_SET); +- write(fd, &(md_header.busy), sizeof(uint32_t)); ++ if (lseek(fd, offsetof(mdisk_header, busy), SEEK_SET) == -1) ++ return -1; ++ ++ if (write(fd, &(md_header.busy), sizeof(uint32_t)) == -1) ++ return -1; ++ + return 0; + } + +@@ -724,6 +728,8 @@ error: + + static int metrics_disk_update(int fd, vu_buffer *buf) + { ++ int ret = -1; ++ + if (buf->use > MDISK_SIZE) { + vu_log(VHOSTMD_ERR, "Metrics data is larger than metrics disk"); + return -1; +@@ -731,11 +737,17 @@ static int metrics_disk_update(int fd, vu_buffer *buf) + + metrics_disk_busy(fd, 1); + metrics_disk_header_update(fd, buf); +- lseek(fd, MDISK_HEADER_SIZE, SEEK_SET); +- write(fd, buf->content, buf->use); ++ if (lseek(fd, MDISK_HEADER_SIZE, SEEK_SET) == -1) ++ goto out; ++ ++ if (write(fd, buf->content, buf->use) == -1) ++ goto out; ++ ++ ret = 0; ++ ++out: + metrics_disk_busy(fd, 0); +- +- return 0; ++ return ret; + } + + static int metrics_free() +@@ -819,10 +831,15 @@ static int metrics_disk_create(void) + } + + /* truncate to a possible new size */ +- ftruncate(fd, mdisk_size); ++ if (ftruncate(fd, mdisk_size) == -1){ ++ vu_log(VHOSTMD_ERR, "Failed to truncate metrics disk: %s", ++ strerror(errno)); ++ goto error; ++ } + + /* zero fill metrics data */ +- lseek(fd, MDISK_HEADER_SIZE, SEEK_SET); ++ if (lseek(fd, MDISK_HEADER_SIZE, SEEK_SET) == -1) ++ goto error; + for (i = 0; i < size / MDISK_SIZE_MIN; i++) + if (write(fd, buf, MDISK_SIZE_MIN) != MDISK_SIZE_MIN) { + vu_log(VHOSTMD_ERR, "Error creating disk of requested " +-- +2.32.0 + diff --git a/0017-vhostmd-Check-for-valide-file-handle-before-calling-.patch b/0017-vhostmd-Check-for-valide-file-handle-before-calling-.patch new file mode 100644 index 0000000..5222522 --- /dev/null +++ b/0017-vhostmd-Check-for-valide-file-handle-before-calling-.patch @@ -0,0 +1,38 @@ +From d86c51d98ce8b891f3948f8aa54fc9634e6a8c67 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 16:04:09 -0700 +Subject: [PATCH 17/19] vhostmd: Check for valide file handle before calling + close + +From coverity scan + +vhostmd-1.1/vhostmd/vhostmd.c:778: var_tested_neg: Assigning: "fd" = a negative value. +vhostmd-1.1/vhostmd/vhostmd.c:845: negative_returns: "fd" is passed to a parameter that cannot be negative. + 843| free(dir); + 844| free(buf); + 845|-> close(fd); + 846| return -1; + 847| } + +Signed-off-by: Jim Fehlig +--- + vhostmd/vhostmd.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 1600a87..1395bc5 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -860,7 +860,8 @@ static int metrics_disk_create(void) + error: + free(dir); + free(buf); +- close(fd); ++ if (fd != -1) ++ close(fd); + return -1; + } + +-- +2.32.0 + diff --git a/0018-vhostmd-Fix-memory-leak-in-vhostmd_run.patch b/0018-vhostmd-Fix-memory-leak-in-vhostmd_run.patch new file mode 100644 index 0000000..1480307 --- /dev/null +++ b/0018-vhostmd-Fix-memory-leak-in-vhostmd_run.patch @@ -0,0 +1,46 @@ +From ff6959fd9203c667f6b4c95fa812621cc91dc42e Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 16:08:42 -0700 +Subject: [PATCH 18/19] vhostmd: Fix memory leak in vhostmd_run + +Example from coverity scan + +vhostmd-1.1/vhostmd/vhostmd.c:940: alloc_arg: "vu_buffer_create" allocates memory that is stored into "buf". +vhostmd-1.1/vhostmd/vhostmd.c:959: leaked_storage: Variable "buf" going out of scope leaks the storage it points to. + 957| vu_log(VHOSTMD_ERR, "Failed to start virtio thread '%s'\n", + 958| strerror(rc)); + 959|-> return -1; + 960| } + 961| } + +Signed-off-by: Jim Fehlig +--- + vhostmd/vhostmd.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/vhostmd/vhostmd.c b/vhostmd/vhostmd.c +index 1395bc5..6f3709b 100644 +--- a/vhostmd/vhostmd.c ++++ b/vhostmd/vhostmd.c +@@ -967,14 +967,17 @@ static int vhostmd_run(int diskfd) + if (virtio_expiration_time < (update_period * 3)) + virtio_expiration_time = update_period * 3; + +- if (virtio_init(virtio_max_channels, virtio_expiration_time)) ++ if (virtio_init(virtio_max_channels, virtio_expiration_time)) { ++ vu_buffer_delete(buf); + return -1; ++ } + + rc = pthread_create(&virtio_tid, NULL, virtio_run, NULL); + + if (rc != 0) { + vu_log(VHOSTMD_ERR, "Failed to start virtio thread '%s'\n", + strerror(rc)); ++ vu_buffer_delete(buf); + return -1; + } + } +-- +2.32.0 + diff --git a/0019-virtio-Fix-strncpy-length-parameter.patch b/0019-virtio-Fix-strncpy-length-parameter.patch new file mode 100644 index 0000000..97c6a57 --- /dev/null +++ b/0019-virtio-Fix-strncpy-length-parameter.patch @@ -0,0 +1,36 @@ +From 99995e4ba138f43b277620bd43a096c72f354548 Mon Sep 17 00:00:00 2001 +From: Jim Fehlig +Date: Tue, 14 Jan 2020 16:22:48 -0700 +Subject: [PATCH 19/19] virtio: Fix strncpy length parameter + +Leave an extra byte for null-terminator in call to strncpy. From +coverity scan + +vhostmd-1.1/vhostmd/virtio.c:194: buffer_size_warning: Calling "strncpy" with a maximum size argument of 108 bytes on destination array "address.sun_path" of size 108 bytes might leave the destination string unterminated. + 192| address.sun_family = AF_LOCAL; + 193| + 194|-> strncpy(address.sun_path, c->uds_name, SUN_PATH_LEN); + 195| + 196| if ((c->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) + +Signed-off-by: Jim Fehlig +--- + vhostmd/virtio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/vhostmd/virtio.c b/vhostmd/virtio.c +index f227b45..a6c2515 100644 +--- a/vhostmd/virtio.c ++++ b/vhostmd/virtio.c +@@ -191,7 +191,7 @@ static int vio_channel_open(channel_t * c) + bzero(&address, sizeof(address)); + address.sun_family = AF_LOCAL; + +- strncpy(address.sun_path, c->uds_name, SUN_PATH_LEN); ++ strncpy(address.sun_path, c->uds_name, SUN_PATH_LEN - 1); + + if ((c->fd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) + goto error; +-- +2.32.0 + diff --git a/vhostmd-1.1.tar.gz b/vhostmd-1.1.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..0ec264e2995423ff5241babf5736bcad10c28234 GIT binary patch literal 57218 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 literal 0 HcmV?d00001 diff --git a/vhostmd.conf b/vhostmd.conf new file mode 100755 index 0000000..955dd2f --- /dev/null +++ b/vhostmd.conf @@ -0,0 +1,431 @@ + + + + + + + + + + + + + + + + + host-metrics-disk + /dev/shm/vhostmd0 + + 256 + + + 1024 + 15 + + 60 + /bin:/sbin:/usr/bin:/usr/sbin:/usr/share/vhostmd/scripts + vbd + + virtio + + + + HostName + hostname + + + Time + date +%s + + + VirtualizationVendor + + rpm -q --queryformat "%{VENDOR}\n" libvirt | sort -u + + + + + VirtProductInfo + + virsh -r CONNECT version \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "USING_API" { print $2; }' + + + + + HostSystemInfo + hostname -s + + + + NumberOfPhysicalCPUs + + + + + + + + virsh -r CONNECT nodeinfo \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "CPU(S)" { print $2; }' + + + + + MemoryAllocatedToVirtualServers + + + + + free|egrep -i '^[[:space:]]*(Mem:)' \ + | awk 'BEGIN { sum = 0; } + { sum += $3; } + END { printf "%d\n", sum/1024; }' + + + + + FreePhysicalMemory + + free|egrep -i '^[[:space:]]*(Mem:)' \ + | awk 'BEGIN { sum = 0; } + { sum += $4; } + END { printf "%d\n", sum/1024; }' + + + + + FreeVirtualMemory + + free|egrep -i '^[[:space:]]*(Mem:|Swap:)' \ + | awk 'BEGIN { sum = 0; } + { sum += $4; } + END { printf "%d\n", sum/1024; }' + + + + + UsedVirtualMemory + + free|egrep -i '^[[:space:]]*(Mem:|Swap:)' \ + | awk 'BEGIN { sum = 0; } + { sum += $3; } + END { printf "%d\n", sum/1024; }' + + + + + PagedInMemory + + vmstat -s | awk 'BEGIN { + cmd = "getconf PAGESIZE"; + cmd | getline pagesize; + close(cmd); + } + /pages swapped in/ { + printf "%d\n", $1 / 1024 * pagesize / 1024; + }' + + + + + PagedOutMemory + + vmstat -s | awk 'BEGIN { + cmd = "getconf PAGESIZE"; + cmd | getline pagesize; + close(cmd); + } + /pages swapped out/ { + printf "%d\n", $1 / 1024 * pagesize / 1024; + }' + + + + + TotalCPUTime + + awk ' + function user_hz( hz) + { + cmd = "getconf CLK_TCK"; + cmd | getline; + hz = $1; + close(cmd); + + return hz; + } + + BEGIN { + USER_HZ = user_hz(); + TotalCPUTime = 0; + + while ( 0 < ( getline < "/proc/stat" ) ) + { + if ( "cpu" == $1 ) + { + TotalCPUTime = $2 + $3 + $4; + + break; + } + } + close("/proc/stat"); + + #printf "USER_HZ = %d\n", USER_HZ | "cat 1>&2"; + TotalCPUTime /= USER_HZ; + printf "%f\n", TotalCPUTime; + + #close("cat 1>&2"); + }' + + + + + TotalCPUTime + + virsh -r CONNECT dominfo NAME \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "CPU_TIME" { print $2; }' + + + + + ResourceProcessorLimit + + virsh -r CONNECT dominfo NAME \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "CPU(S)" { print $2; }' + + + + + ResourceMemoryLimit + + virsh -r CONNECT dominfo NAME \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "MAX_MEMORY" { print int($2/1024); }' + + + + + PhysicalMemoryAllocatedToVirtualSystem + + virsh -r CONNECT dominfo NAME \ + |awk -F ':' ' + function mkvarnam(s) { # UPPER_CASE_UNDERSCORE + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + gsub("[[:space:]]+", "_", s); s = toupper(s); + return s; + } + function filt_phys(s, sep, num, unit) { # 42.0 KM + sub("(^[[:space:]]+|[[:space:]]+$)", "", s); # trim + if ( s ~ /^[0-9]*\.?[0-9]+[[:space:]]*[[:alpha:]]+$/ ) + { + num = s; unit = s; + sub("[[:space:]]*[[:alpha:]]+$", "", num); + sub("^[0-9]*[.]?[0-9]+[[:space:]]*", "", unit); + return num sep toupper(unit); + } + else + { + return s; + } + } + /:/ { + d1 = substr($0, 1, index($0, ":") - 1); + rest = substr($0, index($0, ":") + 1); + printf("%s:%s\n", mkvarnam(d1), filt_phys(rest, ":")); + }' \ + | awk -F: '$1 == "USED_MEMORY" { print int($2/1024); }' + + + + + + diff --git a/vhostmd.spec b/vhostmd.spec new file mode 100644 index 0000000..9595045 --- /dev/null +++ b/vhostmd.spec @@ -0,0 +1,123 @@ +%global have_xen 0 +Summary: Virtualization host metrics daemon +Name: vhostmd +Version: 1.1 +Release: 1 +License: GPLv2+ +URL: https://github.com/vhostmd/vhostmd +Source0: https://github.com/vhostmd/vhostmd/archive/v%{version}/%{name}-%{version}.tar.gz +Source1: vhostmd.conf +Patch0001: 0001-Relax-virtio-requirement-in-config-file.patch +Patch0002: 0002-libmetrics-Set-pointer-NULL-after-free.patch +Patch0003: 0003-libmetrics-Initialize-local-variable-ret-to-failure.patch +Patch0004: 0004-libmetrics-Check-return-value-of-asprintf.patch +Patch0005: 0005-libmetrics-Remove-unsafe-XML_PARSE_NOENT-option.patch +Patch0006: 0006-libmetrics-Ensure-libmetrics-mutex-is-unlocked-in-er.patch +Patch0007: 0007-libmetrics-Fix-potential-memory-leak.patch +Patch0008: 0008-libmetrics-Use-proper-conversion-specifier-when-call.patch +Patch0009: 0009-libmetrics-Fix-potential-leak-of-FILE-pointer.patch +Patch0010: 0010-util-Add-missing-call-to-va_end.patch +Patch0011: 0011-util-Fix-potential-memory-leak.patch +Patch0012: 0012-util-Check-return-value-of-strstr.patch +Patch0013: 0013-Check-return-value-of-asprintf.patch +Patch0014: 0014-vhostmd-Fix-memory-leak-in-parse_transports.patch +Patch0015: 0015-vhostmd-Remove-unsafe-XML_PARSE_NOENT-option.patch +Patch0016: 0016-vhostmd-Check-return-value-of-file-functions.patch +Patch0017: 0017-vhostmd-Check-for-valide-file-handle-before-calling-.patch +Patch0018: 0018-vhostmd-Fix-memory-leak-in-vhostmd_run.patch +Patch0019: 0019-virtio-Fix-strncpy-length-parameter.patch +BuildRequires: make +BuildRequires: gcc +BuildRequires: chrpath +BuildRequires: perl-generators +BuildRequires: pkgconfig +BuildRequires: libxml2-devel +BuildRequires: libvirt-devel +BuildRequires: autoconf, automake, libtool +BuildRequires: git +%{?systemd_requires} +BuildRequires: systemd +%if %{have_xen} +BuildRequires: xen-devel +%endif +Requires: libvirt +%description +vhostmd provides a "metrics communication channel" between a host and +its hosted virtual machines, allowing limited introspection of host +resource usage from within virtual machines. +%package -n vm-dump-metrics +Summary: Virtualization host metrics dump +%description -n vm-dump-metrics +Executable to dump all available virtualization host metrics to stdout +or a file. +%package -n vm-dump-metrics-devel +Summary: Virtualization host metrics dump development +Requires: vm-dump-metrics = %{version}-%{release} +Requires: pkgconfig +%description -n vm-dump-metrics-devel +Header and libraries necessary for metrics gathering development +%prep +%autosetup -S git +%build +autoreconf -i +%configure \ +%if %{have_xen} == 0 + --without-xenstore \ +%endif + --with-init-script=systemd \ + --enable-shared --disable-static +make %{_smp_mflags} +%install +rm -rf $RPM_BUILD_ROOT +make DESTDIR=$RPM_BUILD_ROOT install +rm $RPM_BUILD_ROOT%{_libdir}/libmetrics.la +chrpath --delete $RPM_BUILD_ROOT%{_sbindir}/vm-dump-metrics +# Remove docdir - we'll make a proper one ourselves. +rm -r $RPM_BUILD_ROOT%{_docdir}/vhostmd +# Remove metric.dtd from /etc. +rm $RPM_BUILD_ROOT%{_sysconfdir}/vhostmd/metric.dtd + +# The default configuration file is great for Xen, not so great +# for anyone else. Replace it with one which is better for libvirt +# users. +rm $RPM_BUILD_ROOT%{_sysconfdir}/vhostmd/vhostmd.conf +cp %{SOURCE1} $RPM_BUILD_ROOT%{_sysconfdir}/vhostmd/vhostmd.conf +%pre +getent group vhostmd >/dev/null || groupadd -g 112 -r vhostmd +getent passwd vhostmd >/dev/null || \ +useradd -u 112 -r -g vhostmd -d %{_datadir}/vhostmd -s /sbin/nologin \ +-c "Virtual Host Metrics Daemon" vhostmd +exit 0 +%post +%systemd_post vhostmd.service +%preun +%systemd_preun vhostmd.service +%postun +%systemd_postun_with_restart vhostmd.service +%files +%doc AUTHORS ChangeLog COPYING README +%doc mdisk.xml metric.dtd vhostmd.dtd vhostmd.xml +%{_sbindir}/vhostmd +%dir %{_sysconfdir}/vhostmd +%config(noreplace) %{_sysconfdir}/vhostmd/vhostmd.conf +%config %{_sysconfdir}/vhostmd/vhostmd.dtd +%{_unitdir}/vhostmd.service +%dir %{_datadir}/vhostmd +%dir %{_datadir}/vhostmd/scripts +%{_datadir}/vhostmd/scripts/pagerate.pl +%{_mandir}/man8/vhostmd.8.gz +%files -n vm-dump-metrics +%doc COPYING +%{_sbindir}/vm-dump-metrics +%{_libdir}/libmetrics.so.0 +%{_libdir}/libmetrics.so.0.0.0 +%{_mandir}/man1/vm-dump-metrics.1.gz + +%files -n vm-dump-metrics-devel +%doc README +%{_libdir}/libmetrics.so +%dir %{_includedir}/vhostmd +%{_includedir}/vhostmd/libmetrics.h +%changelog +* Tue Agu 31 2021 shenhongyi - 1.1-1 +- Package init