From 00f426974367a660d5228418b9b5843d969f4ac6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 23 Jan 2019 12:00:13 +0000 Subject: display: ensure qxl log_buf is a nul terminated string MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QXL_IO_LOG command allows the guest to send log messages to the host via a buffer in the QXLRam struct. QEMU prints these to the console if the qxl 'guestdebug' option is set to non-zero. It will also feed them to the trace subsystem if any backends are built-in. In both cases the log_buf data will get treated as being as a nul terminated string, by the printf '%s' format specifier and / or other code reading the buffer. QEMU does nothing to guarantee that the log_buf really is nul terminated, so there is potential for out of bounds array access. This would affect any QEMU which has the log, syslog or ftrace trace backends built into QEMU. It can only be triggered if the 'qxl_io_log' trace event is enabled, however, so they are not vulnerable without specific administrative action to enable this. It would also affect QEMU if the 'guestdebug' parameter is set to a non-zero value, which again is not the default and requires explicit admin opt-in. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé Message-id: 20190123120016.4538-2-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/display/qxl.c | 14 ++++++++++---- hw/display/trace-events | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'hw') diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 8e9a65e..da8fd5a 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -1763,10 +1763,16 @@ async_common: qxl_set_mode(d, val, 0); break; case QXL_IO_LOG: - trace_qxl_io_log(d->id, d->ram->log_buf); - if (d->guestdebug) { - fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), d->ram->log_buf); + if (TRACE_QXL_IO_LOG_ENABLED || d->guestdebug) { + /* We cannot trust the guest to NUL terminate d->ram->log_buf */ + char *log_buf = g_strndup((const char *)d->ram->log_buf, + sizeof(d->ram->log_buf)); + trace_qxl_io_log(d->id, log_buf); + if (d->guestdebug) { + fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), log_buf); + } + g_free(log_buf); } break; case QXL_IO_RESET: diff --git a/hw/display/trace-events b/hw/display/trace-events index 5a48c6c..387c6b8 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -72,7 +72,7 @@ qxl_interface_update_area_complete_rest(int qid, uint32_t num_updated_rects) "%d qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d" qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d" qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s" -qxl_io_log(int qid, const uint8_t *log_buf) "%d %s" +qxl_io_log(int qid, const char *log_buf) "%d %s" qxl_io_read_unexpected(int qid) "%d" qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)" qxl_io_write(int qid, const char *mode, uint64_t addr, const char *aname, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " (%s) val=%"PRIu64" size=%u async=%d" -- cgit v1.1 From 77606363094332415995db7e09ed532b8903fdb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 23 Jan 2019 12:00:14 +0000 Subject: trace: enforce that every trace-events file has a final newline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When generating the trace-events-all file, the build system simply concatenates all the individual trace-events files. If any one of those files does not have a final newline, the printf format string will have the contents of the first line of the next file appended to it, which is usually a '#' comment. Reviewed-by: Eric Blake Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20190123120016.4538-3-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/gpio/trace-events | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'hw') diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events index cb41a89..5d4dd20 100644 --- a/hw/gpio/trace-events +++ b/hw/gpio/trace-events @@ -4,4 +4,4 @@ nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 -nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 \ No newline at end of file +nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 -- cgit v1.1 From 772f1b3721ac138b69c525cb2186b6d72ed200e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 23 Jan 2019 12:00:15 +0000 Subject: trace: forbid use of %m in trace event format strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The '%m' format instructs glibc's printf()/syslog() implementation to insert the contents of strerror(errno). Since this is a glibc extension it should generally be avoided in QEMU due to need for portability to a variety of platforms. Even though vfio is Linux-only code that could otherwise use "%m", it must still be avoided in trace-events files because several of the backends do not use the format string and so this error information is invisible to them. The errno string value should be given as an explicit trace argument instead, making it accessible to all backends. This also allows it to work correctly with future patches that use the format string with systemtap's simple printf code. Reviewed-by: Eric Blake Signed-off-by: Daniel P. Berrangé Message-id: 20190123120016.4538-4-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/vfio/pci.c | 2 +- hw/vfio/trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'hw') diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index c0cb1ec..dd12f36 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2581,7 +2581,7 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); if (ret) { /* This can fail for an old kernel or legacy PCI dev */ - trace_vfio_populate_device_get_irq_info_failure(); + trace_vfio_populate_device_get_irq_info_failure(strerror(errno)); } else if (irq_info.count == 1) { vdev->pci_aer = true; } else { diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index a85e866..f41ca96 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -37,7 +37,7 @@ vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent de vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device %s config:\n size: 0x%lx, offset: 0x%lx, flags: 0x%lx" -vfio_populate_device_get_irq_info_failure(void) "VFIO_DEVICE_GET_IRQ_INFO failure: %m" +vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" vfio_realize(const char *name, int group_id) " (%s) group %d" vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" -- cgit v1.1