id = 1628 title = "windows 10 display scale will cause an exception" state = "opened" created_at = "2023-04-28T06:30:53.137Z" closed_at = "n/a" labels = ["GUI", "guest: Windows", "target: i386", "workflow::Patch available"] url = "https://gitlab.com/qemu-project/qemu/-/issues/1628" host-os = "centos" host-arch = "x86" qemu-version = "7.2.0" guest-os = "Windows 10 21H2" guest-arch = "x86" description = """windows dispaly sacle 150% or higher, windows system will exception""" reproduce = """1. windows dispaly sacle 150%""" additional = """- code in: qemu/hw/display/qxl-render.c static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,                               QXLDataChunk *chunk, uint32_t group_id) {     uint32_t max_chunks = 32;     size_t offset = 0;     size_t bytes;     for (;;) {         bytes = MIN(size - offset, chunk->data_size);         memcpy(dest + offset, chunk->data, bytes);         offset += bytes;         if (offset == size) {             return;         }         chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id,                               sizeof(QXLDataChunk) + chunk->data_size); **// get next chunk, but the chunk size use current chunk's data size, not next chunk's data size!!!!** **// if next chunk alloc size < current chunk's data size, there will be exception **         if (!chunk) {             return;         }         max_chunks--;         if (max_chunks == 0) {             return;         }     } } - code in: qxl_wddm_dod/QXLDod.cpp exist next chunk alloc size < current chunk's data size NTSTATUS QxlDevice::SetPointerShape(_In_ CONST DXGKARG_SETPOINTERSHAPE* pSetPointerShape) { ..... res = (Resource *)AllocMem(MSPACE_TYPE_VRAM, CURSOR_ALLOC_SIZE, TRUE); // here we all the first QXLDataChunk , and alloc_size = (CURSOR_ALLOC_SIZE - sizeof(Resource) - sizeof(InternalCursor)) = 8118 ..... for (; src != src_end; src += pSetPointerShape->Pitch) { if (!PutBytesAlign(&chunk, &now, &end, src, line_size, PAGE_SIZE - PAGE_SIZE % line_size, NULL)) { // in this function ,we will alloc next QXLDataChunk .......... break; } } } BOOLEAN QxlDevice::PutBytesAlign(QXLDataChunk **chunk_ptr, UINT8 **now_ptr, UINT8 **end_ptr, UINT8 *src, int size, size_t alloc_size, PLIST_ENTRY pDelayed) { ..... size_t maxAllocSize = BITS_BUF_MAX - BITS_BUF_MAX % size; alloc_size = MIN(alloc_size, maxAllocSize); void *ptr = AllocMem(MSPACE_TYPE_VRAM, alloc_size + sizeof(QXLDataChunk), bForced); *** //here will alloc next QXLDataChunk and alloc_size = (PAGE_SIZE - PAGE_SIZE % line_size) = 3876 **** } eg: dispaly sacle 150% ,mouse size will bu change to 57* 55 ,rgba data size = 12540, we need three QXLDataChunk QXLDataChunk* first; first->data_size = 8118; first->prev_chunk = 0; first->next_chunk=second; first->data = [alloc_size(8118), data_size(8118)] QXLDataChunk* second; second->data_size = 3876; second->prev_chunk = first; second->next_chunk=third; second->data = [alloc_size(3876), data_size(3876)] QXLDataChunk* third; third->data_size = 546; third->prev_chunk =second; third->next_chunk=0; third->data = [alloc_size(3876), data_size(546)] chunk = first; qxl_phys2virt(qxl, second, group_id, sizeof(QXLDataChunk) + 8118) this size [sizeof(QXLDataChunk) + 8118] > second QXLDataChunk's alloc size , will cause qxl_get_check_slot_offset check fail for second QXLDataChunk, we actual alloc size is (sizeof(QXLDataChunk) + 3876), but we assign (8118 + sizeof(QXLDataChunk)) will cause an exception suggest code : static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl,                               QXLDataChunk *chunk, uint32_t group_id) {     uint32_t max_chunks = 32;     size_t offset = 0;     size_t bytes;     QXLPHYSICAL next_chunk_phys = 0;     for (;;) {         bytes = MIN(size - offset, chunk->data_size);         memcpy(dest + offset, chunk->data, bytes);         offset += bytes;         if (offset == size) {             return;         }         next_chunk_phys = chunk->next_chunk;         chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,                               sizeof(QXLDataChunk));  // fist time, only get the next chunk's data size;         if (!chunk) {             return;         }         chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id,                               sizeof(QXLDataChunk) + chunk->data_size); // second time, check data size and get data;         if (!chunk) {             return;         }         max_chunks--;         if (max_chunks == 0) {             return;         }     } }"""