From fef28891aa401e8f9d048c65f32067f51d695f4e Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 24 Jul 2019 16:31:03 +0200 Subject: loader: Handle memory-mapped ELFs This patch allows handling an ELF memory-mapped, taking care the reference count of the GMappedFile* passed through rom_add_elf_program(). In this case, the 'data' pointer is not heap-allocated, so we cannot free it. Suggested-by: Paolo Bonzini Signed-off-by: Stefano Garzarella Message-Id: <20190724143105.307042-2-sgarzare@redhat.com> Signed-off-by: Paolo Bonzini --- hw/core/loader.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'hw') diff --git a/hw/core/loader.c b/hw/core/loader.c index 84e4f3e..de00f56 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -838,6 +838,7 @@ struct Rom { int isrom; char *fw_dir; char *fw_file; + GMappedFile *mapped_file; bool committed; @@ -848,10 +849,25 @@ struct Rom { static FWCfgState *fw_cfg; static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); -/* rom->data must be heap-allocated (do not use with rom_add_elf_program()) */ +/* + * rom->data can be heap-allocated or memory-mapped (e.g. when added with + * rom_add_elf_program()) + */ +static void rom_free_data(Rom *rom) +{ + if (rom->mapped_file) { + g_mapped_file_unref(rom->mapped_file); + rom->mapped_file = NULL; + } else { + g_free(rom->data); + } + + rom->data = NULL; +} + static void rom_free(Rom *rom) { - g_free(rom->data); + rom_free_data(rom); g_free(rom->path); g_free(rom->name); g_free(rom->fw_dir); @@ -1058,11 +1074,12 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, /* This function is specific for elf program because we don't need to allocate * all the rom. We just allocate the first part and the rest is just zeros. This - * is why romsize and datasize are different. Also, this function seize the - * memory ownership of "data", so we don't have to allocate and copy the buffer. + * is why romsize and datasize are different. Also, this function takes its own + * reference to "mapped_file", so we don't have to allocate and copy the buffer. */ -int rom_add_elf_program(const char *name, void *data, size_t datasize, - size_t romsize, hwaddr addr, AddressSpace *as) +int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, + size_t datasize, size_t romsize, hwaddr addr, + AddressSpace *as) { Rom *rom; @@ -1073,6 +1090,12 @@ int rom_add_elf_program(const char *name, void *data, size_t datasize, rom->romsize = romsize; rom->data = data; rom->as = as; + + if (mapped_file && data) { + g_mapped_file_ref(mapped_file); + rom->mapped_file = mapped_file; + } + rom_insert(rom); return 0; } @@ -1107,8 +1130,7 @@ static void rom_reset(void *unused) } if (rom->isrom) { /* rom needs to be written only once */ - g_free(rom->data); - rom->data = NULL; + rom_free_data(rom); } /* * The rom loader is really on the same level as firmware in the guest -- cgit v1.1 From e502fe96ac4343a3f4a3c13f28eea03ae7b11c3f Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Wed, 24 Jul 2019 16:31:05 +0200 Subject: hw/i386/pc: Map into memory the initrd In order to reduce the memory footprint we map into memory the initrd using g_mapped_file_new() instead of reading it. In this way we can share the initrd pages between multiple instances of QEMU. Suggested-by: Paolo Bonzini Signed-off-by: Stefano Garzarella Message-Id: <20190724143105.307042-4-sgarzare@redhat.com> Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'hw') diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 3ab4bcb..6b4f32e 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1244,17 +1244,21 @@ static void load_linux(PCMachineState *pcms, /* load initrd */ if (initrd_filename) { + GMappedFile *mapped_file; gsize initrd_size; gchar *initrd_data; GError *gerr = NULL; - if (!g_file_get_contents(initrd_filename, &initrd_data, - &initrd_size, &gerr)) { + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { fprintf(stderr, "qemu: error reading initrd %s: %s\n", initrd_filename, gerr->message); exit(1); } + pcms->initrd_mapped_file = mapped_file; + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; if (initrd_size >= initrd_max) { fprintf(stderr, "qemu: initrd is too large, cannot support." @@ -1381,6 +1385,7 @@ static void load_linux(PCMachineState *pcms, /* load initrd */ if (initrd_filename) { + GMappedFile *mapped_file; gsize initrd_size; gchar *initrd_data; GError *gerr = NULL; @@ -1390,12 +1395,16 @@ static void load_linux(PCMachineState *pcms, exit(1); } - if (!g_file_get_contents(initrd_filename, &initrd_data, - &initrd_size, &gerr)) { + mapped_file = g_mapped_file_new(initrd_filename, false, &gerr); + if (!mapped_file) { fprintf(stderr, "qemu: error reading initrd %s: %s\n", initrd_filename, gerr->message); exit(1); } + pcms->initrd_mapped_file = mapped_file; + + initrd_data = g_mapped_file_get_contents(mapped_file); + initrd_size = g_mapped_file_get_length(mapped_file); if (initrd_size >= initrd_max) { fprintf(stderr, "qemu: initrd is too large, cannot support." "(max: %"PRIu32", need %"PRId64")\n", -- cgit v1.1 From 98387d58024e3fb1b88d5a57912e4514d7f39c7c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 25 Jul 2019 12:03:30 +0200 Subject: 9p: simplify source file selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Express the complex conditions in Kconfig rather than Makefiles, since Kconfig is better suited at expressing dependencies and detecting contradictions. Cc: Marc-André Lureau Signed-off-by: Paolo Bonzini --- hw/9pfs/Kconfig | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'hw') diff --git a/hw/9pfs/Kconfig b/hw/9pfs/Kconfig index 8c5032c..3ae5749 100644 --- a/hw/9pfs/Kconfig +++ b/hw/9pfs/Kconfig @@ -1,4 +1,9 @@ +config FSDEV_9P + bool + depends on VIRTFS + config VIRTIO_9P bool default y depends on VIRTFS && VIRTIO + select FSDEV_9P -- cgit v1.1 From 8ff72af557dcae1e1d441983f9057f8c99cad26f Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Wed, 24 Jul 2019 12:58:20 +0100 Subject: mc146818rtc: Remove reset notifiers The reset notifiers are unreliable and recalculating the offsets after boot causes problems with migration in cases where explicit base times are set on the destination. Signed-off-by: Dr. David Alan Gilbert Message-Id: <20190724115823.4199-2-dgilbert@redhat.com> Signed-off-by: Paolo Bonzini --- hw/timer/mc146818rtc.c | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'hw') diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 2661884..6cb3787 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -96,7 +96,6 @@ typedef struct RTCState { uint32_t irq_coalesced; uint32_t period; QEMUTimer *coalesced_timer; - Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; QLIST_ENTRY(RTCState) link; @@ -889,20 +888,6 @@ static const VMStateDescription vmstate_rtc = { } }; -static void rtc_notify_clock_reset(Notifier *notifier, void *data) -{ - RTCState *s = container_of(notifier, RTCState, clock_reset_notifier); - int64_t now = *(int64_t *)data; - - rtc_set_date_from_host(ISA_DEVICE(s)); - periodic_timer_update(s, now, 0); - check_update_timer(s); - - if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { - rtc_coalesced_timer_update(s); - } -} - /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE) BIOS will read it and start S3 resume at POST Entry */ static void rtc_notify_suspend(Notifier *notifier, void *data) @@ -988,10 +973,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) s->update_timer = timer_new_ns(rtc_clock, rtc_update_timer, s); check_update_timer(s); - s->clock_reset_notifier.notify = rtc_notify_clock_reset; - qemu_clock_register_reset_notifier(rtc_clock, - &s->clock_reset_notifier); - s->suspend_notifier.notify = rtc_notify_suspend; qemu_register_suspend_notifier(&s->suspend_notifier); -- cgit v1.1 From 355477f8c73e9c6b60704c57472c71393ff39bca Mon Sep 17 00:00:00 2001 From: Catherine Ho Date: Mon, 8 Apr 2019 04:42:13 -0400 Subject: migration: do not rom_reset() during incoming migration Commit 18269069c310 ("migration: Introduce ignore-shared capability") addes ignore-shared capability to bypass the shared ramblock (e,g, membackend + numa node). It does good to live migration. As told by Yury,this commit expectes that QEMU doesn't write to guest RAM until VM starts, but it does on aarch64 qemu: Backtrace: 1 0x000055f4a296dd84 in address_space_write_rom_internal () at exec.c:3458 2 0x000055f4a296de3a in address_space_write_rom () at exec.c:3479 3 0x000055f4a2d519ff in rom_reset () at hw/core/loader.c:1101 4 0x000055f4a2d475ec in qemu_devices_reset () at hw/core/reset.c:69 5 0x000055f4a2c90a28 in qemu_system_reset () at vl.c:1675 6 0x000055f4a2c9851d in main () at vl.c:4552 Actually, on arm64 virt marchine, ramblock "dtb" will be filled into ram druing rom_reset. In ignore-shared incoming case, this rom filling is not required since all the data has been stored in memory backend file. Further more, as suggested by Peter Xu, if we do rom_reset() now with these ROMs then the RAM data should be re-filled again too with the migration stream coming in. Fixes: commit 18269069c310 ("migration: Introduce ignore-shared capability") Suggested-by: Yury Kotov Suggested-by: Peter Xu Signed-off-by: Catherine Ho Acked-by: Peter Xu Signed-off-by: Paolo Bonzini --- hw/core/loader.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'hw') diff --git a/hw/core/loader.c b/hw/core/loader.c index de00f56..32f7cc7 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -58,6 +58,7 @@ #include "exec/address-spaces.h" #include "hw/boards.h" #include "qemu/cutils.h" +#include "sysemu/runstate.h" #include @@ -1114,6 +1115,15 @@ static void rom_reset(void *unused) { Rom *rom; + /* + * We don't need to fill in the RAM with ROM data because we'll fill + * the data in during the next incoming migration in all cases. Note + * that some of those RAMs can actually be modified by the guest on ARM + * so this is probably the only right thing to do here. + */ + if (runstate_check(RUN_STATE_INMIGRATE)) + return; + QTAILQ_FOREACH(rom, &roms, next) { if (rom->fw_file) { continue; -- cgit v1.1 From de594e47659029316bbf9391efb79da0a1a08e08 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 14 Aug 2019 17:35:21 +0530 Subject: scsi: lsi: exit infinite loop while executing script (CVE-2019-12068) When executing script in lsi_execute_script(), the LSI scsi adapter emulator advances 's->dsp' index to read next opcode. This can lead to an infinite loop if the next opcode is empty. Move the existing loop exit after 10k iterations so that it covers no-op opcodes as well. Reported-by: Bugs SysSec Signed-off-by: Paolo Bonzini Signed-off-by: Prasad J Pandit Signed-off-by: Paolo Bonzini --- hw/scsi/lsi53c895a.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) (limited to 'hw') diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 222a286..ec53b14 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -186,6 +186,9 @@ static const char *names[] = { /* Flag set if this is a tagged command. */ #define LSI_TAG_VALID (1 << 16) +/* Maximum instructions to process. */ +#define LSI_MAX_INSN 10000 + typedef struct lsi_request { SCSIRequest *req; uint32_t tag; @@ -1133,7 +1136,21 @@ static void lsi_execute_script(LSIState *s) s->istat1 |= LSI_ISTAT1_SRUN; again: - insn_processed++; + if (++insn_processed > LSI_MAX_INSN) { + /* Some windows drivers make the device spin waiting for a memory + location to change. If we have been executed a lot of code then + assume this is the case and force an unexpected device disconnect. + This is apparently sufficient to beat the drivers into submission. + */ + if (!(s->sien0 & LSI_SIST0_UDC)) { + qemu_log_mask(LOG_GUEST_ERROR, + "lsi_scsi: inf. loop with UDC masked"); + } + lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); + lsi_disconnect(s); + trace_lsi_execute_script_stop(); + return; + } insn = read_dword(s, s->dsp); if (!insn) { /* If we receive an empty opcode increment the DSP by 4 bytes @@ -1570,19 +1587,7 @@ again: } } } - if (insn_processed > 10000 && s->waiting == LSI_NOWAIT) { - /* Some windows drivers make the device spin waiting for a memory - location to change. If we have been executed a lot of code then - assume this is the case and force an unexpected device disconnect. - This is apparently sufficient to beat the drivers into submission. - */ - if (!(s->sien0 & LSI_SIST0_UDC)) { - qemu_log_mask(LOG_GUEST_ERROR, - "lsi_scsi: inf. loop with UDC masked"); - } - lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); - lsi_disconnect(s); - } else if (s->istat1 & LSI_ISTAT1_SRUN && s->waiting == LSI_NOWAIT) { + if (s->istat1 & LSI_ISTAT1_SRUN && s->waiting == LSI_NOWAIT) { if (s->dcntl & LSI_DCNTL_SSM) { lsi_script_dma_interrupt(s, LSI_DSTAT_SSI); } else { @@ -1970,6 +1975,10 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) case 0x2f: /* DSP[24:31] */ s->dsp &= 0x00ffffff; s->dsp |= val << 24; + /* + * FIXME: if s->waiting != LSI_NOWAIT, this will only execute one + * instruction. Is this correct? + */ if ((s->dmode & LSI_DMODE_MAN) == 0 && (s->istat1 & LSI_ISTAT1_SRUN) == 0) lsi_execute_script(s); @@ -1988,6 +1997,10 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x3b: /* DCNTL */ s->dcntl = val & ~(LSI_DCNTL_PFF | LSI_DCNTL_STD); + /* + * FIXME: if s->waiting != LSI_NOWAIT, this will only execute one + * instruction. Is this correct? + */ if ((val & LSI_DCNTL_STD) && (s->istat1 & LSI_ISTAT1_SRUN) == 0) lsi_execute_script(s); break; -- cgit v1.1