This reduces the affect of an unexpected DoS vulnerablity in libvirtd.
---
include/libvirt/libvirt.h.in | 13 +++++++++++++
src/driver.h | 5 +++++
src/libvirt.c | 32 +++++++++++++++++++++++++++++++
src/libvirt_private.syms | 1 +
src/libvirt_public.syms | 4 ++++
src/qemu/qemu_driver.c | 16 ++++++++++++++++
src/remote/remote_driver.c | 1 +
src/remote/remote_protocol.x | 15 ++++++++++++++-
src/util/virutil.c | 23 ++++++++++++++++++++++
src/util/virutil.h | 1 +
tools/virsh-host.c | 45 ++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 930b7e8..d0db483 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1540,6 +1540,19 @@ char * virConnectGetURI (virConnectPtr
conn);
char * virConnectGetSysinfo (virConnectPtr conn,
unsigned int flags);
+typedef enum {
+ VIR_CONNECT_CRASH_RANDOM = 0, /* Randomly choose one crash method */
+ VIR_CONNECT_CRASH_NULL_PTR = 1, /* Null pointer access */
+ VIR_CONNECT_CRASH_DOUBLE_FREE = 2, /* Double free */
+
+# ifdef VIR_ENUM_SENTINELS
+ VIR_CONNECT_CRASH_LAST
+# endif
+} virConnectCrashFlags;
+
+int virConnectCrashDaemon (virConnectPtr conn,
+ unsigned int flags);
+
int virConnectSetKeepAlive(virConnectPtr conn,
int interval,
unsigned int count);
diff --git a/src/driver.h b/src/driver.h
index e66fc7a..b899a4f 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -1149,6 +1149,10 @@ typedef int
unsigned int flags,
int cancelled);
+typedef int
+(*virDrvConnectCrashDaemon)(virConnectPtr conn,
+ unsigned int flags);
+
typedef struct _virDriver virDriver;
typedef virDriver *virDriverPtr;
@@ -1363,6 +1367,7 @@ struct _virDriver {
virDrvDomainMigrateFinish3Params domainMigrateFinish3Params;
virDrvDomainMigrateConfirm3Params domainMigrateConfirm3Params;
virDrvConnectGetCPUModelNames connectGetCPUModelNames;
+ virDrvConnectCrashDaemon connectCrashDaemon;
};
diff --git a/src/libvirt.c b/src/libvirt.c
index 4454829..739c747 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -1717,6 +1717,38 @@ virConnectGetSysinfo(virConnectPtr conn, unsigned int flags)
/**
+ * virConnectCrashDaemon:
+ * @conn: pointer to a hypervisor connection
+ * @flags: one of virConnectCrashDaemonFlags
+ *
+ * Crashes the daemon by the method selected via @flags.
+ *
+ * Returns -1 on error or doesn't return at all in case of success.
+ */
+int
+virConnectCrashDaemon(virConnectPtr conn, unsigned int flags)
+{
+ VIR_DEBUG("conn=%p, flags=%x", conn, flags);
+
+ virResetLastError();
+
+ virCheckConnectReturn(conn, -1);
+
+ if (conn->driver->connectCrashDaemon) {
+ if (conn->driver->connectCrashDaemon(conn, flags) < 0)
+ goto error;
+ return 0;
+ }
+
+ virReportUnsupportedError();
+
+error:
+ virDispatchError(conn);
+ return -1;
+}
+
+
+/**
* virConnectGetMaxVcpus:
* @conn: pointer to the hypervisor connection
* @type: value of the 'type' attribute in the <domain> element
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 38fbf63..18f91b2 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1983,6 +1983,7 @@ virUSBDeviceSetUsedBy;
# util/virutil.h
virCompareLimitUlong;
+virCrashDaemon;
virDoubleToStr;
virEnumFromString;
virEnumToString;
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 9ab0c92..378d256 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -650,5 +650,9 @@ LIBVIRT_1.2.3 {
virDomainCoreDumpWithFormat;
} LIBVIRT_1.2.1;
+LIBVIRT_1.2.4 {
+ global:
+ virConnectCrashDaemon;
+} LIBVIRT_1.2.3;
# .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index b032441..cd258b7 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -16661,6 +16661,21 @@ qemuConnectGetCPUModelNames(virConnectPtr conn,
}
+static int
+qemuConnectCrashDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
+ unsigned int flags)
+{
+ if (flags >= VIR_CONNECT_CRASH_LAST) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("Unsupported crash method %u"),
+ flags);
+ return -1;
+ }
+
+ return virCrashDaemon(flags);
+}
+
+
static virDriver qemuDriver = {
.no = VIR_DRV_QEMU,
.name = QEMU_DRIVER_NAME,
@@ -16851,6 +16866,7 @@ static virDriver qemuDriver = {
.domainMigrateFinish3Params = qemuDomainMigrateFinish3Params, /* 1.1.0 */
.domainMigrateConfirm3Params = qemuDomainMigrateConfirm3Params, /* 1.1.0 */
.connectGetCPUModelNames = qemuConnectGetCPUModelNames, /* 1.1.3 */
+ .connectCrashDaemon = qemuConnectCrashDaemon, /* 1.2.4 */
};
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index ed7dde6..b01dd5b 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -7800,6 +7800,7 @@ static virDriver remote_driver = {
.domainMigrateFinish3Params = remoteDomainMigrateFinish3Params, /* 1.1.0 */
.domainMigrateConfirm3Params = remoteDomainMigrateConfirm3Params, /* 1.1.0 */
.connectGetCPUModelNames = remoteConnectGetCPUModelNames, /* 1.1.3 */
+ .connectCrashDaemon = remoteConnectCrashDaemon, /* 1.2.4 */
};
static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 6c445cc..7eb7f83 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -442,6 +442,13 @@ struct remote_connect_get_sysinfo_ret {
remote_nonnull_string sysinfo;
};
+struct remote_connect_crash_daemon_args {
+ unsigned int flags;
+};
+
+struct remote_connect_crash_daemon_ret {
+ int retval;
+};
struct remote_connect_get_uri_ret {
remote_nonnull_string uri;
};
@@ -5275,5 +5282,11 @@ enum remote_procedure {
* @generate: both
* @acl: domain:core_dump
*/
- REMOTE_PROC_DOMAIN_CORE_DUMP_WITH_FORMAT = 334
+ REMOTE_PROC_DOMAIN_CORE_DUMP_WITH_FORMAT = 334,
+
+ /**
+ * @generate: both
+ * @acl: none
+ */
+ REMOTE_PROC_CONNECT_CRASH_DAEMON = 335
};
diff --git a/src/util/virutil.c b/src/util/virutil.c
index 9be1590..97215d0 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -78,6 +78,7 @@
#include "vircommand.h"
#include "nonblocking.h"
#include "virprocess.h"
+#include "virrandom.h"
#include "virstring.h"
#include "virutil.h"
@@ -2204,3 +2205,25 @@ void virUpdateSelfLastChanged(const char *path)
selfLastChanged = sb.st_ctime;
}
}
+
+int virCrashDaemon(unsigned int flags)
+{
+ char **bla = NULL;
+ char **tmp;
+
+ if (flags == VIR_CONNECT_CRASH_RANDOM)
+ flags = virRandomInt(VIR_CONNECT_CRASH_LAST) + 1;
+
+ switch (flags) {
+ case VIR_CONNECT_CRASH_NULL_PTR:
+ bla[1] = bla[0];
+ /* fallthrough */
+ case VIR_CONNECT_CRASH_DOUBLE_FREE:
+ if (VIR_ALLOC_N(bla, 2) < 0)
+ return -1;
+ tmp = bla;
+ VIR_FREE(tmp);
+ VIR_FREE(bla);
+ }
+ return -1;
+}
diff --git a/src/util/virutil.h b/src/util/virutil.h
index 1f2efd5..aaf1bef 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -202,4 +202,5 @@ bool virIsSUID(void);
time_t virGetSelfLastChanged(void);
void virUpdateSelfLastChanged(const char *path);
+int virCrashDaemon(unsigned int flags);
#endif /* __VIR_UTIL_H__ */
diff --git a/tools/virsh-host.c b/tools/virsh-host.c
index cac6086..28e5e3b 100644
--- a/tools/virsh-host.c
+++ b/tools/virsh-host.c
@@ -939,6 +939,45 @@ cmdNodeMemoryTune(vshControl *ctl, const vshCmd *cmd)
goto cleanup;
}
+/*
+ * "crash" command
+ */
+static const vshCmdInfo info_crash[] = {
+ {.name = "help",
+ .data = N_("crash the daemon")
+ },
+ {.name = "desc",
+ .data = N_("Crash the libvirt daemon using the selected method.")
+ },
+ {.name = NULL}
+};
+
+static const vshCmdOptDef opts_crash[] = {
+ {.name = "null",
+ .type = VSH_OT_BOOL,
+ .help = N_("Crash by null pointer access")
+ },
+ {.name = "doublefree",
+ .type = VSH_OT_BOOL,
+ .help = N_("Crash by double free")
+ },
+ {.name = NULL}
+};
+
+static bool
+cmdCrash(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED)
+{
+ unsigned int flags = VIR_CONNECT_CRASH_RANDOM;
+
+ if (vshCommandOptBool(cmd, "null"))
+ flags = VIR_CONNECT_CRASH_NULL_PTR;
+ if (vshCommandOptBool(cmd, "doublefree"))
+ flags = VIR_CONNECT_CRASH_DOUBLE_FREE;
+
+ virConnectCrashDaemon(ctl->conn, flags);
+ return true;
+}
+
const vshCmdDef hostAndHypervisorCmds[] = {
{.name = "capabilities",
.handler = cmdCapabilities,
@@ -1024,5 +1063,11 @@ const vshCmdDef hostAndHypervisorCmds[] = {
.info = info_version,
.flags = 0
},
+ {.name = "crash",
+ .handler = cmdCrash,
+ .opts = opts_crash,
+ .info = info_crash,
+ .flags = 0
+ },
{.name = NULL}
};
--
1.8.3.2