From 0a9893121187c0c3f9807e9164366e1f6977619c Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Thu, 5 Dec 2019 10:08:51 -0600 Subject: [PATCH] qemu: don't hold a monitor and agent job for reboot We have to assume that the guest agent may be malicious so we don't want to allow any agent queries to block any other libvirt API. By holding a monitor job while we're querying the agent, we open ourselves up to a DoS. Split the function so that we only hold the appropriate type of job while rebooting. Signed-off-by: Jonathon Jongsma Signed-off-by: Michal Privoznik Reviewed-by: Michal Privoznik --- src/qemu/qemu_driver.c | 112 +++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 42 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c11009e0d6..2891faffe7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2055,6 +2055,72 @@ static int qemuDomainShutdown(virDomainPtr dom) } +static int +qemuDomainRebootAgent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + bool isReboot, + bool agentForced) +{ + qemuAgentPtr agent; + int ret = -1; + int agentFlag = QEMU_AGENT_SHUTDOWN_REBOOT; + + if (!isReboot) + agentFlag = QEMU_AGENT_SHUTDOWN_POWERDOWN; + + if (qemuDomainObjBeginAgentJob(driver, vm, + QEMU_AGENT_JOB_MODIFY) < 0) + return -1; + + if (!qemuDomainAgentAvailable(vm, agentForced)) + goto endjob; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + + qemuDomainSetFakeReboot(driver, vm, false); + agent = qemuDomainObjEnterAgent(vm); + ret = qemuAgentShutdown(agent, agentFlag); + qemuDomainObjExitAgent(vm, agent); + + endjob: + qemuDomainObjEndAgentJob(vm); + return ret; +} + + +static int +qemuDomainRebootMonitor(virQEMUDriverPtr driver, + virDomainObjPtr vm, + bool isReboot) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + + if (qemuDomainObjBeginJob(driver, vm, + QEMU_JOB_MODIFY) < 0) + return -1; + + if (virDomainObjCheckActive(vm) < 0) + goto endjob; + +#if !WITH_YAJL + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("ACPI reboot is not supported without the JSON monitor")); + goto endjob; +#endif + qemuDomainSetFakeReboot(driver, vm, isReboot); + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorSystemPowerdown(priv->mon); + if (qemuDomainObjExitMonitor(driver, vm) < 0) + ret = -1; + + endjob: + qemuDomainObjEndJob(driver, vm); + return ret; +} + + static int qemuDomainReboot(virDomainPtr dom, unsigned int flags) { @@ -2065,8 +2131,6 @@ qemuDomainReboot(virDomainPtr dom, unsigned int flags) bool useAgent = false, agentRequested, acpiRequested; bool isReboot = true; bool agentForced; - qemuDomainAgentJob agentJob = QEMU_AGENT_JOB_NONE; - int agentFlag = QEMU_AGENT_SHUTDOWN_REBOOT; virCheckFlags(VIR_DOMAIN_REBOOT_ACPI_POWER_BTN | VIR_DOMAIN_REBOOT_GUEST_AGENT, -1); @@ -2076,7 +2140,6 @@ qemuDomainReboot(virDomainPtr dom, unsigned int flags) if (vm->def->onReboot == VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY || vm->def->onReboot == VIR_DOMAIN_LIFECYCLE_ACTION_PRESERVE) { - agentFlag = QEMU_AGENT_SHUTDOWN_POWERDOWN; isReboot = false; VIR_INFO("Domain on_reboot setting overridden, shutting down"); } @@ -2092,56 +2155,21 @@ qemuDomainReboot(virDomainPtr dom, unsigned int flags) if (virDomainRebootEnsureACL(dom->conn, vm->def, flags) < 0) goto cleanup; + agentForced = agentRequested && !acpiRequested; if (useAgent) - agentJob = QEMU_AGENT_JOB_MODIFY; + ret = qemuDomainRebootAgent(driver, vm, isReboot, agentForced); - if (qemuDomainObjBeginJobWithAgent(driver, vm, - QEMU_JOB_MODIFY, - agentJob) < 0) + if (ret < 0 && agentForced) goto cleanup; - agentForced = agentRequested && !acpiRequested; - if (!qemuDomainAgentAvailable(vm, agentForced)) { - if (agentForced) - goto endjob; - useAgent = false; - } - - if (virDomainObjCheckActive(vm) < 0) - goto endjob; - - if (useAgent) { - qemuAgentPtr agent; - - qemuDomainSetFakeReboot(driver, vm, false); - agent = qemuDomainObjEnterAgent(vm); - ret = qemuAgentShutdown(agent, agentFlag); - qemuDomainObjExitAgent(vm, agent); - } - /* If we are not enforced to use just an agent, try ACPI * shutdown as well in case agent did not succeed. */ if ((!useAgent) || (ret < 0 && (acpiRequested || !flags))) { -#if !WITH_YAJL - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("ACPI reboot is not supported without the JSON monitor")); - goto endjob; -#endif - qemuDomainSetFakeReboot(driver, vm, isReboot); - qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorSystemPowerdown(priv->mon); - if (qemuDomainObjExitMonitor(driver, vm) < 0) - ret = -1; + ret = qemuDomainRebootMonitor(driver, vm, isReboot); } - endjob: - if (agentJob) - qemuDomainObjEndJobWithAgent(driver, vm); - else - qemuDomainObjEndJob(driver, vm); - cleanup: virDomainObjEndAPI(&vm); return ret; -- 2.47.2