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; >> } >> -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list