[PATCH] ata: libata-scsi: reject non-PIO protocols for READ/WRITE MULTIPLE

From: Zhao Gongyi

Date: Wed Oct 28 2026 - 10:06:39 EST


From: Zhao Gongyi <zhaogongyi@xxxxxxxxxxxxx>

The commands recognized by is_multi_taskfile() are ATA READ/WRITE
MULTIPLE variants, which transfer data using PIO. ata_scsi_pass_thru()
validates the MULTIPLE_COUNT field for these commands, but accepts any
protocol selected by the ATA PASS-THROUGH CDB.

This allows userspace, for example, to combine WRITE MULTIPLE with the
DMA protocol. With a QEMU PIIX disk, the device enters its PIO data phase
and waits with DRQ asserted while the host waits for DMA completion. The
request remains active until the user-selected command timeout. Since
the disk has a single tag, filesystem writeback and later SG_IO requests
block behind it.

Require the protocol to map to ATA_PROT_PIO when is_multi_taskfile()
recognizes the command. Reject any other protocol as an invalid CDB
field before issuing the taskfile. Keep the check local to the existing
READ/WRITE MULTIPLE handling so pass-through behavior for other ATA
commands is unchanged.

Fixes: 1dce589c38c3 ("libata passthru: support PIO multi commands")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Zhao Gongyi <zhaogongyi@xxxxxxxxxxxxx>
---
Tested on a QEMU PIIX disk with 16-sector transfers and a multiple count
of 16:

- WRITE MULTIPLE with DMA was rejected with ILLEGAL REQUEST/INVALID
FIELD IN CDB and a field pointer of 1.
- READ MULTIPLE with DMA was rejected with the same sense data.
- WRITE MULTIPLE with PIO data-out completed successfully.
- READ MULTIPLE with PIO data-in completed successfully and returned the
8192 bytes written by the preceding command.

Both invalid commands completed in less than 1 ms. No ATA timeout or
hung-task warning was reported, and subsequent I/O remained responsive.
The modified libata-scsi.o also builds successfully.

drivers/ata/libata-scsi.c | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 115b75a313..31d822d78e 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3507,6 +3507,11 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc)
if (is_multi_taskfile(tf)) {
unsigned int multi_count = 1 << (cdb[1] >> 5);

+ if (tf->protocol != ATA_PROT_PIO) {
+ fp = 1 + cdb_offset;
+ goto invalid_fld;
+ }
+
if (!dev->multi_count) {
ata_scsi_set_sense(dev, scmd, ABORTED_COMMAND, 0, 0);
return 1;

base-commit: 69be770908ae57354a41d7e1cc99ab46278d2e86
--
2.39.5 (Apple Git-154)