hw/display/cirrus_vga: negative shift exponent in 24bpp transparent pattern color-expand path
## Host environment - Operating system: Linux - OS/kernel version: `Linux dell-PowerEdge-R740 4.15.0-142-generic #146-Ubuntu SMP Tue Apr 13 01:11:19 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux` - Architecture: x86_64 - QEMU flavor: qemu-system-i386 - QEMU version: `QEMU emulator version 10.2.91 (v11.0.0-rc1-25-g3c46691f16-dirty)` ## Description of problem A guest-triggerable undefined-behavior issue exists in QEMU's Cirrus VGA blitter emulation and can crash the host QEMU process in sanitizer-enabled builds. The issue is in the 24bpp transparent pattern color-expand path. In `hw/display/cirrus_vga.c`, writing `CIRRUS_MMIO_BLTSTATUS` (offset `0x40`) reaches the BLT start logic through: ```c case CIRRUS_MMIO_BLTSTATUS: cirrus_vga_write_gr(s, 0x31, value); break; ``` `gr[0x31]` is handled by `cirrus_write_bitblt()`, which invokes `cirrus_bitblt_start()` when the `CIRRUS_BLT_START` bit changes from 0 to 1: ```c static void cirrus_write_bitblt(CirrusVGAState * s, unsigned reg_value) { unsigned old_value; old_value = s->vga.gr[0x31]; s->vga.gr[0x31] = reg_value; if (((old_value & CIRRUS_BLT_RESET) != 0) && ((reg_value & CIRRUS_BLT_RESET) == 0)) { cirrus_bitblt_reset(s); } else if (((old_value & CIRRUS_BLT_START) == 0) && ((reg_value & CIRRUS_BLT_START) != 0)) { cirrus_bitblt_start(s); } } ``` Inside `cirrus_bitblt_start()`, when the guest programs BLT state for `PATTERNCOPY | COLOREXPAND | TRANSPARENTCOMP` with 24bpp, the code selects the 24bpp transparent pattern color-expand path: ```c s->cirrus_rop = cirrus_colorexpand_pattern_transp[rop_to_index[blt_rop]] [s->cirrus_blt_pixelwidth - 1]; ``` In `hw/display/cirrus_vga_rop2.h`, the 24bpp implementation derives a shift count directly from guest-controlled BLT state: ```c #if DEPTH == 24 int dstskipleft = s->vga.gr[0x2f] & 0x1f; int srcskipleft = dstskipleft / 3; #else int srcskipleft = s->vga.gr[0x2f] & 0x07; int dstskipleft = srcskipleft * (DEPTH / 8); #endif ... bitpos = 7 - srcskipleft; ... if ((bits >> bitpos) & 1) { PUTPIXEL(s, addr, col); } ``` With `gr[0x2f] = 0x1e`, `dstskipleft = 30`, `srcskipleft = 10`, and `bitpos = -3`. The subsequent right shift uses a negative exponent and triggers UBSan: ```text runtime error: shift exponent -3 is negative ``` From a security perspective, guest-controlled device state reaches an unsafe host-side shift operation without validating the resulting shift count. In sanitizer-enabled builds this is a guest-triggerable host crash/DoS. In non-sanitized builds it still indicates insufficient hardening in a guest-reachable device emulation path. We initially discovered it through fuzzing in version 10.2.50, and it appears to still exist in version 10.2.91. Affected source paths: - `hw/display/cirrus_vga.c` - `hw/display/cirrus_vga_rop2.h` ## Steps to reproduce 1. Build QEMU with UBSan enabled. 2. Start QEMU with: ```bash ./qemu-system-i386 \ -machine q35,accel=qtest \ -nodefaults \ -display none \ -serial none \ -monitor none \ -device cirrus-vga,addr=03.0,blitter=on \ -qtest stdio <<'EOF' outl 0xcf8 0x80001814 outl 0xcfc 0x10000000 outl 0xcf8 0x80001804 outl 0xcfc 0x00000007 outb 0x3c4 0x07 outb 0x3c5 0x05 writeb 0x10000108 0x1f writeb 0x10000109 0x00 writeb 0x1000010a 0x00 writeb 0x1000010b 0x00 writeb 0x1000010c 0x20 writeb 0x1000010d 0x00 writeb 0x1000010e 0x20 writeb 0x1000010f 0x00 writeb 0x10000110 0x00 writeb 0x10000111 0x01 writeb 0x10000112 0x00 writeb 0x10000114 0x00 writeb 0x10000115 0x00 writeb 0x10000116 0x00 writeb 0x10000117 0x1e writeb 0x10000118 0xe8 writeb 0x1000011a 0x00 writeb 0x1000011b 0x00 writeb 0x10000140 0x00 writeb 0x10000140 0x02 EOF ``` 3. Observe that QEMU reaches the Cirrus BLT start path and triggers UBSan with a negative shift exponent in `hw/display/cirrus_vga_rop2.h`. ## Additional information Simplified fuzzing log: ```text ../../third_party/qemu/hw/display/cirrus_vga_rop2.h:216:23: runtime error: shift exponent -1 is negative #0 cirrus_colorexpand_pattern_transp_0_24 #1 cirrus_bitblt_common_patterncopy #2 cirrus_bitblt_videotovideo_patterncopy #3 cirrus_bitblt_videotovideo #4 cirrus_bitblt_start #5 cirrus_vga_write_gr #6 cirrus_mmio_blt_write #7 memory_region_write_accessor #8 access_with_adjusted_size #9 memory_region_dispatch_write ``` The core issue is that the shift count is derived from guest-controlled BLT register state (`gr[0x2f]`) without checking that it is valid for the 24bpp transparent pattern color-expand path before executing the shift. <!--The line below ensures that proper tags are added to the issue. Please do not remove it.-->
issue