QEMU is not at fault here.  The discrepancy comes from the BIOS.

When I create a disk image as described in the question, then attach it to a QEMU virtual machine and use the `info qtree` command in the monitor, this is the geometry I see reported:

```
            cyls = 248 (0xf8)
            heads = 16 (0x10)
            secs = 63 (0x3f)
```

The same geometry is reported to the guest with the ATA IDENTIFY command.  The reason for the discrepancy is what the BIOS firmware does with that hardware-reported geometry; this can be seen within the source code of SeaBIOS, which is the BIOS implementation used in QEMU.  The [`disk_1308` function][seabios/src/disk.c#disk_1308] contains this code:

```
// read disk drive parameters
static void noinline
disk_1308(struct bregs *regs, struct drive_s *drive_fl)
{
    // Get logical geometry from table
    struct chs_s chs = getLCHS(drive_fl);
    u16 nlc=chs.cylinder, nlh=chs.head, nls=chs.sector;
    nlc--;
    nlh--;
    u8 count;
    if (regs->dl < EXTSTART_HD) {
        // Floppy
        /* [...] */
    } else if (regs->dl < EXTSTART_CD) {
        // Hard drive
        count = GET_BDA(hdcount);
        nlc--;  // last sector reserved
    } else {
        /* [...] */
    }

    /* [...] */

    regs->al = 0;
    regs->ch = nlc & 0xff;
    regs->cl = ((nlc >> 2) & 0xc0) | (nls & 0x3f);
    regs->dh = nlh;
    disk_ret(regs, DISK_RET_SUCCESS);
    regs->dl = count;
}
```

The interrupt 0x13 service 8 handler reads off the logical geometry from an internal data structure.  Since the call is supposed to return the *maximum* CHS coordinates, the numbers of cylinders and heads are decremented by one, as those coordinates are zero-based.  If the block device is a hard disk, the maximum cylinder number is decremented again.  The latter is accompanied by a rather laconic comment: ‘last sector reserved’.  What could that mean?

Ralf Brown’s [entry on that service][rbil/B-1308] warns us that:

> the maximum cylinder number reported in CX is usually two less than
> the total cylinder count reported in the fixed disk parameter table
> (see INT 41h,INT 46h) because early hard disks used the last cylinder
> for testing purposes; however, on some Zenith machines, the maximum
> cylinder number reportedly is three less than the count in the fixed
> disk parameter table.
>
> for BIOSes which reserve the last cylinder for testing purposes, the
> cylinder count is automatically decremented

So apparently there is some historical precedent for this: some old BIOSes would reserve the last cylinder of the drive for their own purposes, and as such, hide its existence from programs querying the drive geometry.  This agrees with SeaBIOS’s behaviour: if we look up the geometry in the FDPT, we can see it contains the true number of cylinders.

But the key word here seems to be ‘usually’, which implies this behaviour is optional, at the BIOS’s discretion.  It does not seem that SeaBIOS actually makes use of that reservation anywhere, which means it is completely spurious, and there is no reason service 8 should not report the true (emulated) geometry.<sup>0</sup>  As such, I would consider this a bug after all.  There might conceivably be software in the wild that blindly attempts to undo the last-cylinder reservation, but I think the probability of such being found is rather small.  (I would expect such software to consult the FDPT instead of asking interrupt 0x13 for the disk geometry.)

----

<sup>0</sup> Compare for example [DOSEMU2, which does not bother to reserve the last cylinder][dosemu2/src/base/misc/disks.c#1308-return].  I am pointing to another emulator since I am having a hard time finding a physical machine I own with a small enough hard drive for it to be CHS-addressable in its entirety.  On the other hand, though, the PC AT BIOS used in PCjs *does* reserve the last cylinder: a type-5 hard drive (940 cylinders × 6 heads × 17 sectors per track) is reported with a maximum cylinder number of 938, i.e. as having 939 cylinders.

[seabios/src/disk.c#disk_1308]: https://review.coreboot.org/plugins/gitiles/seabios/+/54082c81d96028ba8c76fbe6784085cf1df76b20/src/disk.c#273
[rbil/B-1308]: https://fd.lod.bz/rbil/interrup/bios/1308.html
[dosemu2/src/base/misc/disks.c#1308-return]: https://github.com/dosemu2/dosemu2/blob/fe0bd439ce69b3a1c428696bfff29d82dcec5778/src/base/misc/disks.c#L1757-L1771
[seavgasrc/vgasrc/vgabios.c#handle_1000]: https://review.coreboot.org/plugins/gitiles/seabios/+/54082c81d96028ba8c76fbe6784085cf1df76b20/vgasrc/vgabios.c#336
[rbil/V-1000]: https://fd.lod.bz/rbil/interrup/video/1000.html