ati_host_data_flush() resets host_data.next only on its success path.
When it returns early (unsupported bpp, direction, src_source, or
src_datatype), next remains stale at >= 4. The next HOST_DATA write
then stores a guest-controlled dword at acc[4+], overflowing the
4-element accumulator array.

Fix this by resetting next unconditionally in the write handler after
calling ati_host_data_flush() or ati_host_data_finish(), and removing
the reset from inside ati_host_data_flush(). This ensures the write
handler owns the full lifecycle of the accumulator index regardless of
flush success or failure.

Reported-by: Feifan Qian <[email protected]>
Resolves: 
https://lore.kernel.org/qemu-devel/Czyl6yVfL6sHl_o1kRk8N_LpwXMMRVhO9vgz1qCVJFagn9D4nHSKuiux39iOLty0Q3acxQq_FeovPhTQvSKus2htwjI9lTajLZmqovr0Wxs=@proton.me/
Cc: [email protected]
Signed-off-by: Junjie Cao <[email protected]>
---
 hw/display/ati.c    | 2 ++
 hw/display/ati_2d.c | 1 -
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/hw/display/ati.c b/hw/display/ati.c
index d77589df67..db7e08a462 100644
--- a/hw/display/ati.c
+++ b/hw/display/ati.c
@@ -1034,8 +1034,10 @@ static void ati_mm_write(void *opaque, hwaddr addr,
         s->host_data.acc[s->host_data.next++] = data;
         if (addr == HOST_DATA_LAST) {
             ati_host_data_finish(s);
+            s->host_data.next = 0;
         } else if (s->host_data.next >= 4) {
             ati_host_data_flush(s);
+            s->host_data.next = 0;
         }
         break;
     default:
diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c
index 48498677c7..8ef82bb87f 100644
--- a/hw/display/ati_2d.c
+++ b/hw/display/ati_2d.c
@@ -452,7 +452,6 @@ bool ati_host_data_flush(ATIVGAState *s)
     }
 
     /* Track state of the overall blit for use by the next flush */
-    s->host_data.next = 0;
     s->host_data.row = row;
     s->host_data.col = col;
     if (s->host_data.row >= ctx.dst.height) {
-- 
2.43.0


Reply via email to