On 08.12.2016 19:40, John Ferlan wrote:
On 11/24/2016 04:19 AM, Nikolay Shirokovskiy wrote:
> Current call to qemuAgentGetFSInfo in qemuDomainGetFSInfo is
> unsafe. Domain lock is dropped and we use vm->def. To fix that
> let's fill in intermediate qemuAgentFsInfo structure in
> qemuAgentGetFSInfo and use vm->def to convert result later when
> lock is hold.
> ---
> src/qemu/qemu_agent.c | 52 +++++++++++--------
> src/qemu/qemu_agent.h | 25 ++++++++-
> src/qemu/qemu_driver.c | 88 +++++++++++++++++++++++++++++++-
> tests/Makefile.am | 1 -
> tests/qemuagentdata/qemuagent-fsinfo.xml | 39 --------------
> tests/qemuagenttest.c | 47 +++++++----------
> 6 files changed, 159 insertions(+), 93 deletions(-)
> delete mode 100644 tests/qemuagentdata/qemuagent-fsinfo.xml
>
This failed to compile for me as 'num' wasn't initialized in
qemuDomainGetFSInfo
Well I'll just set it to zero then...
> diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
> index c5cf403..5230cbc 100644
> --- a/src/qemu/qemu_agent.c
> +++ b/src/qemu/qemu_agent.c
> @@ -1835,18 +1835,15 @@ qemuAgentSetTime(qemuAgentPtr mon,
>
>
> int
> -qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
> - virDomainDefPtr vmdef)
> +qemuAgentGetFSInfo(qemuAgentPtr mon, qemuAgentFsInfoPtr **info)
> {
> size_t i, j, k;
> int ret = -1;
> ssize_t ndata = 0, ndisk;
> - char **alias;
> virJSONValuePtr cmd;
> virJSONValuePtr reply = NULL;
> virJSONValuePtr data;
> - virDomainFSInfoPtr *info_ret = NULL;
> - virPCIDeviceAddress pci_address;
> + qemuAgentFsInfoPtr *info_ret = NULL;
>
> cmd = qemuAgentMakeCommand("guest-get-fsinfo", NULL);
> if (!cmd)
> @@ -1879,6 +1876,8 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr
**info,
> goto cleanup;
>
> for (i = 0; i < ndata; i++) {
> + qemuAgentFsDiskAliasPtr alias;
> +
> /* Reverse the order to arrange in mount order */
> virJSONValuePtr entry = virJSONValueArrayGet(data, ndata - 1 - i);
>
> @@ -1941,7 +1940,6 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr
**info,
> int diskaddr[3], pciaddr[4];
> const char *diskaddr_comp[] = {"bus", "target",
"unit"};
> const char *pciaddr_comp[] = {"domain", "bus",
"slot", "function"};
> - virDomainDiskDefPtr diskDef;
>
> if (!disk) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> @@ -1967,6 +1965,11 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr
**info,
> goto cleanup;
> }
> }
> +
> + alias->bus = diskaddr[0];
> + alias->target = diskaddr[1];
> + alias->unit = diskaddr[2];
> +
> for (k = 0; k < 4; k++) {
> if (virJSONValueObjectGetNumberInt(
> pci, pciaddr_comp[k], &pciaddr[k]) < 0) {
> @@ -1977,22 +1980,13 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr
**info,
> }
> }
>
> - pci_address.domain = pciaddr[0];
> - pci_address.bus = pciaddr[1];
> - pci_address.slot = pciaddr[2];
> - pci_address.function = pciaddr[3];
> - if (!(diskDef = virDomainDiskByAddress(
> - vmdef, &pci_address,
> - diskaddr[0], diskaddr[1], diskaddr[2])))
> - continue;
> + alias->address.domain = pciaddr[0];
> + alias->address.bus = pciaddr[1];
> + alias->address.slot = pciaddr[2];
> + alias->address.function = pciaddr[3];
>
> - if (VIR_STRDUP(*alias, diskDef->dst) < 0)
> - goto cleanup;
> -
> - if (*alias) {
> - alias++;
> - info_ret[i]->ndevAlias++;
> - }
> + alias++;
> + info_ret[i]->ndevAlias++;
> }
> }
>
> @@ -2003,7 +1997,7 @@ qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr
**info,
> cleanup:
> if (info_ret) {
> for (i = 0; i < ndata; i++)
> - virDomainFSInfoFree(info_ret[i]);
> + qemuAgentFsInfoFree(info_ret[i]);
> VIR_FREE(info_ret);
> }
> virJSONValueFree(cmd);
> @@ -2242,3 +2236,17 @@ qemuAgentSetUserPassword(qemuAgentPtr mon,
> VIR_FREE(password64);
> return ret;
> }
> +
> +void
> +qemuAgentFsInfoFree(qemuAgentFsInfoPtr info)
> +{
> + if (!info)
> + return;
> +
> + VIR_FREE(info->mountpoint);
> + VIR_FREE(info->name);
> + VIR_FREE(info->fstype);
> + VIR_FREE(info->devAlias);
You'd have to free each 'ndevAlias' here.
Why? _qemuAgentFsDiskAlias does not have anything to be freed.
and devAlias itself is an array of elements not array of pointers.
> +
> + VIR_FREE(info);
> +}
> diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
> index 6dd9c70..4b2277c 100644
> --- a/src/qemu/qemu_agent.h
> +++ b/src/qemu/qemu_agent.h
> @@ -75,8 +75,29 @@ int qemuAgentShutdown(qemuAgentPtr mon,
> int qemuAgentFSFreeze(qemuAgentPtr mon,
> const char **mountpoints, unsigned int nmountpoints);
> int qemuAgentFSThaw(qemuAgentPtr mon);
> -int qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
> - virDomainDefPtr vmdef);
> +
> +typedef struct _qemuAgentFsDiskAlias qemuAgentFsDiskAlias;
> +typedef qemuAgentFsDiskAlias *qemuAgentFsDiskAliasPtr;
> +struct _qemuAgentFsDiskAlias {
> + virPCIDeviceAddress address;
> + unsigned int bus;
> + unsigned int target;
> + unsigned int unit;
> +};
> +
> +typedef struct _qemuAgentFsInfo qemuAgentFsInfo;
> +typedef qemuAgentFsInfo *qemuAgentFsInfoPtr;
> +struct _qemuAgentFsInfo {
> + char *mountpoint; /* path to mount point */
> + char *name; /* device name in the guest (e.g. "sda1") */
> + char *fstype; /* filesystem type */
> + size_t ndevAlias; /* number of elements in devAlias */
> + qemuAgentFsDiskAliasPtr devAlias; /* array of disk device aliases */
> +};
> +
> +void qemuAgentFsInfoFree(qemuAgentFsInfoPtr info);
> +
> +int qemuAgentGetFSInfo(qemuAgentPtr mon, qemuAgentFsInfoPtr **info);
>
> int qemuAgentSuspend(qemuAgentPtr mon,
> unsigned int target);
> diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
> index fdfe912..1bc5dc6 100644
> --- a/src/qemu/qemu_driver.c
> +++ b/src/qemu/qemu_driver.c
> @@ -19672,14 +19672,89 @@ qemuNodeAllocPages(virConnectPtr conn,
>
>
> static int
> +qemuDomainConvertFsInfo(qemuAgentFsInfoPtr *agent_info, int num,
> + virDomainDefPtr def,
> + virDomainFSInfoPtr **info)
qemuDomainFsInfoConvert
> +{
> + int ret = -1;
> + size_t i;
> + virDomainFSInfoPtr *info_ret = NULL;
> +
> + if (num == 0) {
> + *info = NULL;
> + return 0;
> + }
> +
> + if (VIR_ALLOC_N(info_ret, num) < 0)
> + return -1;
> +
> + for (i = 0; i < num; i++) {
> + size_t j;
> + int devnum;
> +
> + if (VIR_ALLOC(info_ret[i]) < 0)
> + goto cleanup;
> +
> + if (VIR_STRDUP(info_ret[i]->mountpoint, agent_info[i]->mountpoint)
< 0 ||
> + VIR_STRDUP(info_ret[i]->name, agent_info[i]->name) < 0 ||
> + VIR_STRDUP(info_ret[i]->fstype, agent_info[i]->fstype) < 0)
> + goto cleanup;
I think you could certainly make use of VIR_STEAL_PTR here
Well it saves a few cpu cycles. But we definetely need to change function name then.
Like qemuDomainFsInfoSteal or something to reflect the disruptive changes to the
arguments.
> +
> + if (agent_info[i]->ndevAlias == 0)
> + continue;
> +
> + if (VIR_EXPAND_N(info_ret[i]->devAlias,
> + info_ret[i]->ndevAlias,
> + agent_info[i]->ndevAlias) < 0)
> + goto cleanup;
Not sure EXPAND/SHRINK will be necessary
Well yes, expand does not give here much of its benefits. I can just
use alloc, but what wrong with shrink? The loop below can skip elements
so to keep memory tight shrink is useful.
> +
> + devnum = 0;
> + for (j = 0; j < agent_info[i]->ndevAlias; j++) {
> + virDomainDiskDefPtr diskDef;
> + qemuAgentFsDiskAliasPtr alias = &agent_info[i]->devAlias[j];
> +
> + if (!(diskDef = virDomainDiskByAddress(def, &alias->address,
> + alias->bus, alias->target,
> + alias->unit)))
> + continue;
> +
> + if (VIR_STRDUP(info_ret[i]->devAlias[devnum++], diskDef->dst) <
0)
> + goto cleanup;
Would it be possible to use something like VIR_APPEND_ELEMENT of a
VIR_STEAL_PTR on diskDef->dst; ?
VIR_APPEND_ELEMENT reallocates on every append...
Well I don't think stealing from domain config is a good idea))
> + }
> +
> + if (devnum < info_ret[i]->ndevAlias)
> + VIR_SHRINK_N(info_ret[i]->devAlias,
> + info_ret[i]->ndevAlias,
> + info_ret[i]->ndevAlias - devnum);
> + }
> +
> + *info = info_ret;
> + info_ret = NULL;
> + ret = num;
> +
> + cleanup:
> + if (info_ret) {
> + for (i = 0; i < num; i++)
> + virDomainFSInfoFree(info_ret[i]);
> + VIR_FREE(info_ret);
> + }
> +
> + return ret;
> +}
> +
> +
> +static int
> qemuDomainGetFSInfo(virDomainPtr dom,
> virDomainFSInfoPtr **info,
> unsigned int flags)
> {
> virQEMUDriverPtr driver = dom->conn->privateData;
> + qemuAgentFsInfoPtr *agent_info = NULL;
> virDomainObjPtr vm;
> qemuAgentPtr agent;
> int ret = -1;
> + int num;
num needs to be initialized since we can jump to cleanup without setting it.
> + size_t i;
>
> virCheckFlags(0, ret);
>
> @@ -19702,13 +19777,24 @@ qemuDomainGetFSInfo(virDomainPtr dom,
> goto endjob;
>
> agent = qemuDomainObjEnterAgent(vm);
> - ret = qemuAgentGetFSInfo(agent, info, vm->def);
> + num = qemuAgentGetFSInfo(agent, &agent_info);
> qemuDomainObjExitAgent(vm, agent);
>
Wouldn't it be possible to pass a copy (eg virDomainDefCopy prior to
dropping the vm lock) of the vm->def that virDomainDiskByAddress is
going to need?
In the long run all it's using the data for is a frozen view in time.
I have no objections in general to such approach and it would take much
less changes too. But I guess qemuAgentGetFSInfo is the only function in monitor
that takes domain configuration into account and odd for this reason.
I mean look at the tests - there is no more stub configurations to test monitor
except for this function.
> + if (num < 0)
> + goto endjob;
> +
> + ret = qemuDomainConvertFsInfo(agent_info, num, vm->def, info);
> +
> endjob:
> qemuDomainObjEndJob(driver, vm);
>
> cleanup:
> + if (agent_info) {
> + for (i = 0; i < num; i++)
> + qemuAgentFsInfoFree(agent_info[i]);
> + VIR_FREE(agent_info);
> + }
> +
> virDomainObjEndAPI(&vm);
> return ret;
> }
Depending on the above, could mean changes below, so I'll leave it for
now...
John
> diff --git a/tests/Makefile.am b/tests/Makefile.am
> index 924029a..d34cc95 100644
> --- a/tests/Makefile.am
> +++ b/tests/Makefile.am
> @@ -116,7 +116,6 @@ EXTRA_DIST = \
> nwfilterxml2xmlin \
> nwfilterxml2xmlout \
> oomtrace.pl \
> - qemuagentdata \
> qemuargv2xmldata \
> qemucapabilitiesdata \
> qemucaps2xmldata \
> diff --git a/tests/qemuagentdata/qemuagent-fsinfo.xml
b/tests/qemuagentdata/qemuagent-fsinfo.xml
> deleted file mode 100644
> index 9638feb..0000000
> --- a/tests/qemuagentdata/qemuagent-fsinfo.xml
> +++ /dev/null
> @@ -1,39 +0,0 @@
> -<domain type='qemu'>
> - <name>QEMUGuest1</name>
> - <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
> - <memory unit='KiB'>219136</memory>
> - <currentMemory unit='KiB'>219136</currentMemory>
> - <vcpu placement='static'>1</vcpu>
> - <os>
> - <type arch='i686' machine='pc'>hvm</type>
> - <boot dev='hd'/>
> - </os>
> - <clock offset='utc'/>
> - <on_poweroff>destroy</on_poweroff>
> - <on_reboot>restart</on_reboot>
> - <on_crash>destroy</on_crash>
> - <devices>
> - <emulator>/usr/bin/qemu</emulator>
> - <disk type='file' device='disk'>
> - <source file='/tmp/idedisk.img'/>
> - <target dev='hdc' bus='ide'/>
> - <address type='drive' controller='0' bus='1'
target='0' unit='0'/>
> - </disk>
> - <disk type='file' device='disk'>
> - <driver name='qemu' type='qcow2'/>
> - <source file='/tmp/virtio-blk1.qcow2'/>
> - <target dev='vda' bus='virtio'/>
> - <address type='pci' domain='0x0000' bus='0x00'
slot='0x06' function='0x0'/>
> - </disk>
> - <disk type='file' device='disk'>
> - <driver name='qemu' type='qcow2'/>
> - <source file='/tmp/virtio-blk2.qcow2'/>
> - <target dev='vdb' bus='virtio'/>
> - <address type='pci' domain='0x0000' bus='0x00'
slot='0x07' function='0x0'/>
> - </disk>
> - <controller type='ide' index='0'>
> - <address type='pci' domain='0x0000' bus='0x00'
slot='0x01' function='0x1'/>
> - </controller>
> - <memballoon model='virtio'/>
> - </devices>
> -</domain>
> diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c
> index 5dfa58e..97f340c 100644
> --- a/tests/qemuagenttest.c
> +++ b/tests/qemuagenttest.c
> @@ -169,22 +169,16 @@ testQemuAgentGetFSInfo(const void *data)
> virDomainXMLOptionPtr xmlopt = (virDomainXMLOptionPtr)data;
> virCapsPtr caps = testQemuCapsInit();
> qemuMonitorTestPtr test = qemuMonitorTestNewAgent(xmlopt);
> - char *domain_filename = NULL;
> - virDomainDefPtr def = NULL;
> - virDomainFSInfoPtr *info = NULL;
> + qemuAgentFsInfoPtr *info = NULL;
> int ret = -1, ninfo = 0, i;
>
> + qemuAgentFsDiskAlias alias_sda1 = { { 0, 0, 1, 1, 0 }, 1, 0, 0 };
> + qemuAgentFsDiskAlias alias_vda = { { 0, 0, 6, 0, 0 }, 0, 0, 0 };
> + qemuAgentFsDiskAlias alias_vdb = { { 0, 0, 7, 0, 0 }, 0, 0, 0 };
> +
> if (!test)
> return -1;
>
> - if (virAsprintf(&domain_filename,
"%s/qemuagentdata/qemuagent-fsinfo.xml",
> - abs_srcdir) < 0)
> - goto cleanup;
> -
> - if (!(def = virDomainDefParseFile(domain_filename, caps, xmlopt,
> - NULL, VIR_DOMAIN_DEF_PARSE_INACTIVE)))
> - goto cleanup;
> -
> if (qemuMonitorTestAddAgentSyncResponse(test) < 0)
> goto cleanup;
>
> @@ -220,8 +214,7 @@ testQemuAgentGetFSInfo(const void *data)
> " \"disk\": [], \"type\":
\"xfs\"}]}") < 0)
> goto cleanup;
>
> - if ((ninfo = qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test),
> - &info, def)) < 0)
> + if ((ninfo = qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test), &info)) <
0)
> goto cleanup;
>
> if (ninfo != 3) {
> @@ -234,11 +227,11 @@ testQemuAgentGetFSInfo(const void *data)
> STRNEQ(info[2]->mountpoint, "/") ||
> STRNEQ(info[2]->fstype, "ext4") ||
> info[2]->ndevAlias != 1 ||
> - !info[2]->devAlias || !info[2]->devAlias[0] ||
> - STRNEQ(info[2]->devAlias[0], "hdc")) {
> + !info[2]->devAlias ||
> + memcmp(&info[2]->devAlias[0], &alias_sda1, sizeof(alias_sda1)) !=
0) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> - "unexpected filesystems information returned for sda1
(%s,%s)",
> - info[2]->name, info[2]->devAlias ? info[2]->devAlias[0] :
"null");
> + "unexpected filesystems information returned for sda1 (%s)",
> + info[2]->name);
> ret = -1;
> goto cleanup;
> }
> @@ -246,12 +239,12 @@ testQemuAgentGetFSInfo(const void *data)
> STRNEQ(info[1]->mountpoint, "/opt") ||
> STRNEQ(info[1]->fstype, "vfat") ||
> info[1]->ndevAlias != 2 ||
> - !info[1]->devAlias || !info[1]->devAlias[0] ||
!info[1]->devAlias[1] ||
> - STRNEQ(info[1]->devAlias[0], "vda") ||
> - STRNEQ(info[1]->devAlias[1], "vdb")) {
> + !info[1]->devAlias ||
> + memcmp(&info[1]->devAlias[0], &alias_vda, sizeof(alias_vda)) != 0
||
> + memcmp(&info[1]->devAlias[1], &alias_vdb, sizeof(alias_vdb)) !=
0) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> - "unexpected filesystems information returned for dm-1
(%s,%s)",
> - info[0]->name, info[0]->devAlias ? info[0]->devAlias[0] :
"null");
> + "unexpected filesystems information returned for dm-1 (%s)",
> + info[0]->name);
> ret = -1;
> goto cleanup;
> }
> @@ -260,8 +253,8 @@ testQemuAgentGetFSInfo(const void *data)
> STRNEQ(info[0]->fstype, "xfs") ||
> info[0]->ndevAlias != 0 || info[0]->devAlias) {
> virReportError(VIR_ERR_INTERNAL_ERROR,
> - "unexpected filesystems information returned for sdb1
(%s,%s)",
> - info[0]->name, info[0]->devAlias ? info[0]->devAlias[0] :
"null");
> + "unexpected filesystems information returned for sdb1 (%s)",
> + info[0]->name);
> ret = -1;
> goto cleanup;
> }
> @@ -280,7 +273,7 @@ testQemuAgentGetFSInfo(const void *data)
> "}") < 0)
> goto cleanup;
>
> - if (qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test), &info, def) != -1) {
> + if (qemuAgentGetFSInfo(qemuMonitorTestGetAgent(test), &info) != -1) {
> virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
> "agent get-fsinfo command should have failed");
> goto cleanup;
> @@ -290,11 +283,9 @@ testQemuAgentGetFSInfo(const void *data)
>
> cleanup:
> for (i = 0; i < ninfo; i++)
> - virDomainFSInfoFree(info[i]);
> + qemuAgentFsInfoFree(info[i]);
> VIR_FREE(info);
> - VIR_FREE(domain_filename);
> virObjectUnref(caps);
> - virDomainDefFree(def);
> qemuMonitorTestFree(test);
> return ret;
> }
>