Commit 2ac2cf0483 for qemu.org

commit 2ac2cf0483d090f2f2d795ed06a94cc865b972fe
Author: Peter Maydell <peter.maydell@linaro.org>
Date:   Mon Jul 13 12:34:17 2026 +0100

    hw/display/sm501: Don't allow guest to set ram size larger than it is

    The SM501 DRAM_CONTROL register has a 7 bit Size field which allows
    the guest to change the local memory size.  We use the local memory
    size in bounds checks calculations for 2D operations. Currently we
    have no check on the validity of the value the guest programs to
    this field, which means that the guest can:
     - set it to a reserved value (6 or 7) which will cause
       get_local_mem_size() to read outside sm501_mem_local_size[]
     - set it to a value corresponding to more RAM than the card
       was created with, so that the 2D bounds check will let 2D
       operations access off the end of the memory region

    Fix this by decoupling the value the guest reads and writes to this
    field from the internal size we consider the local memory to have.
    We validate changes and ignore them except for readback if they would
    be reserved values or values for more memory than the card has.

    Cc: qemu-stable@nongnu.org
    Reported-by: Heechan Kang
    Tested-by: BALATON Zoltan <balaton@eik.bme.hu>
    Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu>
    Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
    Message-id: 20260707150933.1410507-4-peter.maydell@linaro.org
    Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3811
    Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

diff --git a/hw/display/sm501.c b/hw/display/sm501.c
index d5e165daef..cacb6c87ec 100644
--- a/hw/display/sm501.c
+++ b/hw/display/sm501.c
@@ -571,6 +571,25 @@ static uint32_t get_local_mem_size_index(uint32_t size)
     return index;
 }

+static void set_new_local_mem_size_index(SM501State *s, uint32_t idx)
+{
+    /*
+     * Update local_mem_size_index on guest write. We don't allow this
+     * to be set to larger than the actual RAM size. (The guest will
+     * still read back the SYSTEM_CONTROL.Size bits that it wrote.)
+     */
+    if (idx < ARRAY_SIZE(sm501_mem_local_size) &&
+        sm501_mem_local_size[idx] <= memory_region_size(&s->local_mem_region)) {
+        s->local_mem_size_index = idx;
+        return;
+    }
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "sm501: Guest set DRAM_CONTROL.Size to 0x%x but "
+                  "local memory is not that large\n",
+                  idx);
+    /* Don't change the effective size, leave it as whatever it was */
+}
+
 static ram_addr_t get_fb_addr(SM501State *s, int crt)
 {
     return (crt ? s->dc_crt_fb_addr : s->dc_panel_fb_addr) & 0x3FFFFF0;
@@ -990,7 +1009,7 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr,
         ret = 0x050100A0;
         break;
     case SM501_DRAM_CONTROL:
-        ret = (s->dram_control & 0x07F107C0) | s->local_mem_size_index << 13;
+        ret = (s->dram_control & 0x07F1E7C0);
         break;
     case SM501_ARBTRTN_CONTROL:
         ret = s->arbitration_control;
@@ -1049,8 +1068,7 @@ static void sm501_system_config_write(void *opaque, hwaddr addr,
         s->gpio_63_32_control = value & 0xFF80FFFF;
         break;
     case SM501_DRAM_CONTROL:
-        s->local_mem_size_index = (value >> 13) & 0x7;
-        /* TODO : check validity of size change */
+        set_new_local_mem_size_index(s, (value >> 13) & 0x7);
         s->dram_control &= 0x80000000;
         s->dram_control |= value & 0x7FFFFFC3;
         break;