[libvirt] [PATCH] Introduce virConnectCrashDaemon API

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

On 04/01/14 09:34, Ján Tomko wrote:
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(-)
...
+ +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;
The random crash method is not accessible here.
+ + virConnectCrashDaemon(ctl->conn, flags); + return true; +} + const vshCmdDef hostAndHypervisorCmds[] = { {.name = "capabilities", .handler = cmdCapabilities,
I really like this API, this will allow us to decrease load on the libvirt-security list and avoid us having to go through the tedious CVE process for every little crasher. Additionally it will help attackers to avoid having to look through complex code paths to crash the daemon by presenting them with a very userfriendly API! ACK if you support the random crash method too ;) Peter

On Tue, Apr 01, 2014 at 09:44:16AM +0200, Peter Krempa wrote:
On 04/01/14 09:34, Ján Tomko wrote:
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(-)
...
+ +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;
The random crash method is not accessible here.
+ + virConnectCrashDaemon(ctl->conn, flags); + return true; +} + const vshCmdDef hostAndHypervisorCmds[] = { {.name = "capabilities", .handler = cmdCapabilities,
I really like this API, this will allow us to decrease load on the libvirt-security list and avoid us having to go through the tedious CVE process for every little crasher.
Additionally it will help attackers to avoid having to look through complex code paths to crash the daemon by presenting them with a very userfriendly API!
Unfortunately, this is not true if the daemon does not have a qemu driver since it is implemented only in there. This should rather be a method in daemon/remote.c which would make it available in the daemon without any particular driver. Also, this should have @priority: high, not specifying the priority makes it default to low which you really don't want to since it would not be available if no priority workers are available. Looking forward to v2 (in one year, is it?) :-) Martin
ACK if you support the random crash method too ;)
Peter
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list
participants (3)
-
Ján Tomko
-
Martin Kletzander
-
Peter Krempa