Message ID | 1343043213-9997-1-git-send-email-lersek@redhat.com |
---|---|
State | New |
Headers | show |
On 23 July 2012 12:33, Laszlo Ersek <lersek@redhat.com> wrote: > > Signed-off-by: Laszlo Ersek <lersek@redhat.com> I think it would be much nicer to just rewrite qdev_get_fw_dev_path so we weren't trying to fill the path into a fixed string buffer at all. Here is an entirely untested implementation: char *qdev_get_fw_dev_path(DeviceState *dev) { char *path; char **strarray; int depth = 0; DeviceState *d = dev; for (d = dev; d && d->parent_bus; d = d->parent_bus->parent) { depth++; } depth++; strarray = g_new(char*, depth); for (d = dev; d && d->parent_bus; d = d->parent_bus->parent) { depth--; strarray[depth] = bus_get_fw_dev_path(dev->parent_bus, dev); if (!strarray[depth]) { strarray[depth] = g_strdup(object_get_typename(OBJECT(dev))); } } strarray[0] = g_strdup(""); path = g_strjoinv("/", strarray); g_strfreev(strarray); return path; } Bonus extra patch: check that all the implementations of get_fw_dev_path() are returning a g_malloc'd string rather than a plain malloc'd one (both this code and the current implementation assume so but at least the scsi bus implementation doesn't use the glib functions.) -- PMM
On 23 July 2012 13:34, Peter Maydell <peter.maydell@linaro.org> wrote: > On 23 July 2012 12:33, Laszlo Ersek <lersek@redhat.com> wrote: >> >> Signed-off-by: Laszlo Ersek <lersek@redhat.com> > > I think it would be much nicer to just rewrite qdev_get_fw_dev_path > so we weren't trying to fill the path into a fixed string buffer > at all. Here is an entirely untested implementation: > > char *qdev_get_fw_dev_path(DeviceState *dev) > { > char *path; > char **strarray; > int depth = 0; > DeviceState *d = dev; > for (d = dev; d && d->parent_bus; d = d->parent_bus->parent) { > depth++; > } > depth++; > strarray = g_new(char*, depth); > for (d = dev; d && d->parent_bus; d = d->parent_bus->parent) { > depth--; > strarray[depth] = bus_get_fw_dev_path(dev->parent_bus, dev); "d" not "dev" here and in the line below, obviously. I said it was untested :-) > if (!strarray[depth]) { > strarray[depth] = g_strdup(object_get_typename(OBJECT(dev))); > } > } > strarray[0] = g_strdup(""); > path = g_strjoinv("/", strarray); > g_strfreev(strarray); > return path; > } -- PMM
Laszlo Ersek <lersek@redhat.com> writes: > Signed-off-by: Laszlo Ersek <lersek@redhat.com> > --- > hw/qdev.c | 14 +++++++++++++- > vl.c | 7 ++++++- > 2 files changed, 19 insertions(+), 2 deletions(-) > > diff --git a/hw/qdev.c b/hw/qdev.c > index af54467..f1e83a4 100644 > --- a/hw/qdev.c > +++ b/hw/qdev.c > @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) > if (dev && dev->parent_bus) { > char *d; > l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); > + if (l >= size) { > + return l; > + } > + > d = bus_get_fw_dev_path(dev->parent_bus, dev); > if (d) { > l += snprintf(p + l, size - l, "%s", d); > @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) > } else { > l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); > } > + > + if (l >= size) { > + return l; > + } > } > l += snprintf(p + l , size - l, "/"); > If the return value is less than the size argument, it's the length of the string written into p[]. Else, it means p[] has insufficient space. > @@ -520,8 +528,12 @@ char* qdev_get_fw_dev_path(DeviceState *dev) > char path[128]; > int l; > > - l = qdev_get_fw_dev_path_helper(dev, path, 128); > + l = qdev_get_fw_dev_path_helper(dev, path, sizeof(path)); > > + assert(l > 0); > + if (l >= sizeof(path)) { > + return NULL; > + } Failure mode could be avoided with the common technique: make qdev_get_fw_dev_path_helper() return the true length. If it fit into path[], return strdup(path). Else, allocate a suitable buffer and try again. What do you think? > path[l-1] = '\0'; > > return strdup(path); > diff --git a/vl.c b/vl.c > index 8904db1..78dcc93 100644 > --- a/vl.c > +++ b/vl.c > @@ -913,7 +913,12 @@ char *get_boot_devices_list(uint32_t *size) > > if (i->dev) { > devpath = qdev_get_fw_dev_path(i->dev); > - assert(devpath); > + if (devpath == NULL) { > + fprintf(stderr, > + "OpenFirmware Device Path too long (boot index %d)\n", > + i->bootindex); > + exit(1); > + } > } > > if (i->suffix && devpath) {
On 07/23/12 14:46, Markus Armbruster wrote: > Laszlo Ersek <lersek@redhat.com> writes: > >> Signed-off-by: Laszlo Ersek <lersek@redhat.com> >> --- >> hw/qdev.c | 14 +++++++++++++- >> vl.c | 7 ++++++- >> 2 files changed, 19 insertions(+), 2 deletions(-) >> >> diff --git a/hw/qdev.c b/hw/qdev.c >> index af54467..f1e83a4 100644 >> --- a/hw/qdev.c >> +++ b/hw/qdev.c >> @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >> if (dev && dev->parent_bus) { >> char *d; >> l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); >> + if (l >= size) { >> + return l; >> + } >> + >> d = bus_get_fw_dev_path(dev->parent_bus, dev); >> if (d) { >> l += snprintf(p + l, size - l, "%s", d); >> @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >> } else { >> l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); >> } >> + >> + if (l >= size) { >> + return l; >> + } >> } >> l += snprintf(p + l , size - l, "/"); >> > > If the return value is less than the size argument, it's the length of > the string written into p[]. Else, it means p[] has insufficient > space. Yes. (snprintf() returns the number of bytes it would store, excluding the terminating NUL, had there been enough room. <http://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04>) Did I make a mistake? Supposing snprintf() encounters no error, it returns a positive value P in the above. P = snprintf(..., size - l0, ...) l1 = l0 + P; l1 >= size <-> l0 + P >= size <-> P >= size - l0 The return value of qdev_get_fw_dev_path_helper() comes from another invocation of itself, or from the trailing snprintf(), so it behaves like snprintf. Or what do you have in mind? > >> @@ -520,8 +528,12 @@ char* qdev_get_fw_dev_path(DeviceState *dev) >> char path[128]; >> int l; >> >> - l = qdev_get_fw_dev_path_helper(dev, path, 128); >> + l = qdev_get_fw_dev_path_helper(dev, path, sizeof(path)); >> >> + assert(l > 0); >> + if (l >= sizeof(path)) { >> + return NULL; >> + } > > Failure mode could be avoided with the common technique: make > qdev_get_fw_dev_path_helper() return the true length. If it fit into > path[], return strdup(path). Else, allocate a suitable buffer and try > again. > > What do you think? You're right of course, but I didn't have (or wanted to spend) the time to change the code that much (and to test it...), especially because it would have been fixed already if it had caused actual problems. I think the patch could work as a "last resort", small improvement, but I don't mind if someone posts a better fix :) Laszlo
Laszlo Ersek <lersek@redhat.com> writes: > On 07/23/12 14:46, Markus Armbruster wrote: >> Laszlo Ersek <lersek@redhat.com> writes: >> >>> Signed-off-by: Laszlo Ersek <lersek@redhat.com> >>> --- >>> hw/qdev.c | 14 +++++++++++++- >>> vl.c | 7 ++++++- >>> 2 files changed, 19 insertions(+), 2 deletions(-) >>> >>> diff --git a/hw/qdev.c b/hw/qdev.c >>> index af54467..f1e83a4 100644 >>> --- a/hw/qdev.c >>> +++ b/hw/qdev.c >>> @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>> if (dev && dev->parent_bus) { >>> char *d; >>> l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); >>> + if (l >= size) { >>> + return l; >>> + } >>> + >>> d = bus_get_fw_dev_path(dev->parent_bus, dev); >>> if (d) { >>> l += snprintf(p + l, size - l, "%s", d); >>> @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>> } else { >>> l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); >>> } >>> + >>> + if (l >= size) { >>> + return l; >>> + } >>> } >>> l += snprintf(p + l , size - l, "/"); >>> >> >> If the return value is less than the size argument, it's the length of >> the string written into p[]. Else, it means p[] has insufficient >> space. > > Yes. (snprintf() returns the number of bytes it would store, excluding > the terminating NUL, had there been enough room. > <http://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04>) > > Did I make a mistake? > > Supposing snprintf() encounters no error, it returns a positive value P > in the above. > > P = snprintf(..., size - l0, ...) > l1 = l0 + P; > > l1 >= size > <-> l0 + P >= size > <-> P >= size - l0 > > > The return value of qdev_get_fw_dev_path_helper() comes from another > invocation of itself, or from the trailing snprintf(), so it behaves > like snprintf. > > Or what do you have in mind? If I read your code correctly, qdev_get_fw_dev_path_helper() bails out after the first snprintf() that goes beyond the buffer size. When that happens before the job's done, the return value is less than the length of the full path. static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) { int l = 0; if (dev && dev->parent_bus) { char *d; l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); + if (l >= size) { + return l; <--- bail out early + } + if (dev->parent_bus->info->get_fw_dev_path) { d = dev->parent_bus->info->get_fw_dev_path(dev); l += snprintf(p + l, size - l, "%s", d); qemu_free(d); } else { l += snprintf(p + l, size - l, "%s", dev->info->name); } + + if (l >= size) { + return l; <--- bail out early + } } l += snprintf(p + l , size - l, "/"); return l; } >>> @@ -520,8 +528,12 @@ char* qdev_get_fw_dev_path(DeviceState *dev) >>> char path[128]; >>> int l; >>> >>> - l = qdev_get_fw_dev_path_helper(dev, path, 128); >>> + l = qdev_get_fw_dev_path_helper(dev, path, sizeof(path)); >>> >>> + assert(l > 0); >>> + if (l >= sizeof(path)) { >>> + return NULL; >>> + } >> >> Failure mode could be avoided with the common technique: make >> qdev_get_fw_dev_path_helper() return the true length. If it fit into >> path[], return strdup(path). Else, allocate a suitable buffer and try >> again. >> >> What do you think? > > You're right of course, but I didn't have (or wanted to spend) the time > to change the code that much (and to test it...), especially because it > would have been fixed already if it had caused actual problems. I think > the patch could work as a "last resort", small improvement, but I don't > mind if someone posts a better fix :) Only marginally harder than handling the failure :)
On 07/23/12 17:01, Markus Armbruster wrote: > Laszlo Ersek <lersek@redhat.com> writes: > >> On 07/23/12 14:46, Markus Armbruster wrote: >>> Laszlo Ersek <lersek@redhat.com> writes: >>> >>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com> >>>> --- >>>> hw/qdev.c | 14 +++++++++++++- >>>> vl.c | 7 ++++++- >>>> 2 files changed, 19 insertions(+), 2 deletions(-) >>>> >>>> diff --git a/hw/qdev.c b/hw/qdev.c >>>> index af54467..f1e83a4 100644 >>>> --- a/hw/qdev.c >>>> +++ b/hw/qdev.c >>>> @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>>> if (dev && dev->parent_bus) { >>>> char *d; >>>> l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); >>>> + if (l >= size) { >>>> + return l; >>>> + } >>>> + >>>> d = bus_get_fw_dev_path(dev->parent_bus, dev); >>>> if (d) { >>>> l += snprintf(p + l, size - l, "%s", d); >>>> @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>>> } else { >>>> l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); >>>> } >>>> + >>>> + if (l >= size) { >>>> + return l; >>>> + } >>>> } >>>> l += snprintf(p + l , size - l, "/"); >>>> >>> >>> If the return value is less than the size argument, it's the length of >>> the string written into p[]. Else, it means p[] has insufficient >>> space. >> >> Yes. (snprintf() returns the number of bytes it would store, excluding >> the terminating NUL, had there been enough room. >> <http://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04>) >> >> Did I make a mistake? >> >> Supposing snprintf() encounters no error, it returns a positive value P >> in the above. >> >> P = snprintf(..., size - l0, ...) >> l1 = l0 + P; >> >> l1 >= size >> <-> l0 + P >= size >> <-> P >= size - l0 >> >> >> The return value of qdev_get_fw_dev_path_helper() comes from another >> invocation of itself, or from the trailing snprintf(), so it behaves >> like snprintf. >> >> Or what do you have in mind? > > If I read your code correctly, qdev_get_fw_dev_path_helper() bails out > after the first snprintf() that goes beyond the buffer size. When that > happens before the job's done, the return value is less than the length > of the full path. Yes, but still greater than what would fit in the buffer. Is it a problem? ... Oh, did you mean the first comment in connection with the second? Ie. we should continue to format (just for length counting's sake) and then retry? IOW the early return isn't problematic in itself, but it doesn't support the reallocation? Thanks, Laszlo
Laszlo Ersek <lersek@redhat.com> writes: > On 07/23/12 17:01, Markus Armbruster wrote: >> Laszlo Ersek <lersek@redhat.com> writes: >> >>> On 07/23/12 14:46, Markus Armbruster wrote: >>>> Laszlo Ersek <lersek@redhat.com> writes: >>>> >>>>> Signed-off-by: Laszlo Ersek <lersek@redhat.com> >>>>> --- >>>>> hw/qdev.c | 14 +++++++++++++- >>>>> vl.c | 7 ++++++- >>>>> 2 files changed, 19 insertions(+), 2 deletions(-) >>>>> >>>>> diff --git a/hw/qdev.c b/hw/qdev.c >>>>> index af54467..f1e83a4 100644 >>>>> --- a/hw/qdev.c >>>>> +++ b/hw/qdev.c >>>>> @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>>>> if (dev && dev->parent_bus) { >>>>> char *d; >>>>> l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); >>>>> + if (l >= size) { >>>>> + return l; >>>>> + } >>>>> + >>>>> d = bus_get_fw_dev_path(dev->parent_bus, dev); >>>>> if (d) { >>>>> l += snprintf(p + l, size - l, "%s", d); >>>>> @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) >>>>> } else { >>>>> l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); >>>>> } >>>>> + >>>>> + if (l >= size) { >>>>> + return l; >>>>> + } >>>>> } >>>>> l += snprintf(p + l , size - l, "/"); >>>>> >>>> >>>> If the return value is less than the size argument, it's the length of >>>> the string written into p[]. Else, it means p[] has insufficient >>>> space. >>> >>> Yes. (snprintf() returns the number of bytes it would store, excluding >>> the terminating NUL, had there been enough room. >>> <http://pubs.opengroup.org/onlinepubs/9699919799/functions/snprintf.html#tag_16_159_04>) >>> >>> Did I make a mistake? >>> >>> Supposing snprintf() encounters no error, it returns a positive value P >>> in the above. >>> >>> P = snprintf(..., size - l0, ...) >>> l1 = l0 + P; >>> >>> l1 >= size >>> <-> l0 + P >= size >>> <-> P >= size - l0 >>> >>> >>> The return value of qdev_get_fw_dev_path_helper() comes from another >>> invocation of itself, or from the trailing snprintf(), so it behaves >>> like snprintf. >>> >>> Or what do you have in mind? >> >> If I read your code correctly, qdev_get_fw_dev_path_helper() bails out >> after the first snprintf() that goes beyond the buffer size. When that >> happens before the job's done, the return value is less than the length >> of the full path. > > Yes, but still greater than what would fit in the buffer. Is it a problem? > > ... Oh, did you mean the first comment in connection with the second? > Ie. we should continue to format (just for length counting's sake) and > then retry? IOW the early return isn't problematic in itself, but it > doesn't support the reallocation? Yes. Your code lets the caller detect "need more space" reliably. How much more it can only guess, and need to be prepared for the retry to fail again. If we return the required size, like snprintf() does, the caller knows, and the retry will succeed.
As long as "two" qualifies as "assorted". v1->v2: - abandon original "idea", allocate sufficient memory for OFW device path formatting [Markus] - all bus formatters should rely on glib for dynamic allocation [Peter] Tested with an OVMF debug patch that grabs and logs the "bootorder" fw_cfg file: /pci@i0cf8/ide@1,1/drive@0/disk@0 /pci@i0cf8/ide@1,1/drive@1/disk@0 /pci@i0cf8/isa@1/fdc@03f0/floppy@0 /pci@i0cf8/ethernet@3/ethernet-phy@0 Laszlo Ersek (2): accomodate OpenFirmware device paths in sufficient storage get_fw_dev_path() impls should allocate memory with glib functions hw/ide/qdev.c | 2 +- hw/isa-bus.c | 2 +- hw/pci.c | 2 +- hw/qdev.c | 54 +++++++++++++++++++++++++++++++++++++++++++----------- hw/scsi-bus.c | 2 +- hw/sysbus.c | 2 +- 6 files changed, 48 insertions(+), 16 deletions(-)
diff --git a/hw/qdev.c b/hw/qdev.c index af54467..f1e83a4 100644 --- a/hw/qdev.c +++ b/hw/qdev.c @@ -502,6 +502,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) if (dev && dev->parent_bus) { char *d; l = qdev_get_fw_dev_path_helper(dev->parent_bus->parent, p, size); + if (l >= size) { + return l; + } + d = bus_get_fw_dev_path(dev->parent_bus, dev); if (d) { l += snprintf(p + l, size - l, "%s", d); @@ -509,6 +513,10 @@ static int qdev_get_fw_dev_path_helper(DeviceState *dev, char *p, int size) } else { l += snprintf(p + l, size - l, "%s", object_get_typename(OBJECT(dev))); } + + if (l >= size) { + return l; + } } l += snprintf(p + l , size - l, "/"); @@ -520,8 +528,12 @@ char* qdev_get_fw_dev_path(DeviceState *dev) char path[128]; int l; - l = qdev_get_fw_dev_path_helper(dev, path, 128); + l = qdev_get_fw_dev_path_helper(dev, path, sizeof(path)); + assert(l > 0); + if (l >= sizeof(path)) { + return NULL; + } path[l-1] = '\0'; return strdup(path); diff --git a/vl.c b/vl.c index 8904db1..78dcc93 100644 --- a/vl.c +++ b/vl.c @@ -913,7 +913,12 @@ char *get_boot_devices_list(uint32_t *size) if (i->dev) { devpath = qdev_get_fw_dev_path(i->dev); - assert(devpath); + if (devpath == NULL) { + fprintf(stderr, + "OpenFirmware Device Path too long (boot index %d)\n", + i->bootindex); + exit(1); + } } if (i->suffix && devpath) {
Signed-off-by: Laszlo Ersek <lersek@redhat.com> --- hw/qdev.c | 14 +++++++++++++- vl.c | 7 ++++++- 2 files changed, 19 insertions(+), 2 deletions(-)