Commit 6c712a86f6 for qemu.org

commit 6c712a86f6c8d8bdd8a3ba6b39cbcd9550668c36
Author: Denis V. Lunev <den@openvz.org>
Date:   Fri Aug 14 15:35:29 2026 +0200

    hw/ide: reject an unsupported CHS translation

    ide_set_sector() divides by (s->heads * s->sectors) when the drive is
    addressed in CHS mode. Both come from the guest via INITIALIZE DEVICE
    PARAMETERS, and cmd_specify() stored them without any check, so a guest
    asking for zero sectors per logical track killed QEMU with SIGFPE on the
    completion of the first CHS read or write. s->heads is safe, as the
    command passes a heads-1 value.

    The count has an upper bound as well. The legacy sector count register is
    eight bits wide, but handle_cmd() takes the count from a 16 bit field of
    the register FIS, so an AHCI guest can ask for up to 65535 sectors per
    track, and the CHS branch of ide_get_sector() then overflows the int it
    multiplies cylinder, heads and sectors in.

    ATA-5 6.2 numbers CHS sectors from one and ATA-2 D.2.8 limits IDENTIFY
    DEVICE word 56 to 1 through 255, so neither end is a translation a device
    may accept. ATA-5 8.16.6 requires an unsupported one to be reported as an
    aborted command: do that, leave the translation in effect alone, and
    refuse the value rather than checking it at every use.

    Cc: John Snow <jsnow@redhat.com>
    Cc: Peter Maydell <peter.maydell@linaro.org>
    Cc: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>
    Cc: qemu-stable@nongnu.org
    Fixes: 176e4961bb33 ("hw/ide/core.c: Implement ATA INITIALIZE_DEVICE_PARAMETERS command")
    Reported-by: Zheyu Ma <zheyuma97@gmail.com>
    Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2399
    Signed-off-by: Denis V. Lunev <den@openvz.org>
    Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com>

diff --git a/hw/ide/core.c b/hw/ide/core.c
index fb9bf11b45..747fa71677 100644
--- a/hw/ide/core.c
+++ b/hw/ide/core.c
@@ -1655,14 +1655,21 @@ static bool cmd_check_power_mode(IDEState *s, uint8_t cmd)
 /* INITIALIZE DEVICE PARAMETERS */
 static bool cmd_specify(IDEState *s, uint8_t cmd)
 {
-    if (s->blk && s->drive_kind != IDE_CD) {
-        s->heads = (s->select & (ATA_DEV_HS)) + 1;
-        s->sectors = s->nsector;
-        ide_bus_set_irq(s->bus);
-    } else {
+    if (!s->blk || s->drive_kind == IDE_CD) {
+        ide_abort_command(s);
+        return true;
+    }
+
+    /* ATA-2 D.2.8 limits IDENTIFY DEVICE word 56, and the count, to 1..255 */
+    if (s->nsector == 0 || s->nsector > 255) {
         ide_abort_command(s);
+        return true;
     }

+    s->heads = (s->select & (ATA_DEV_HS)) + 1;
+    s->sectors = s->nsector;
+    ide_bus_set_irq(s->bus);
+
     return true;
 }