summaryrefslogtreecommitdiff
path: root/drivers/ata
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2019-08-07 12:20:52 -0600
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-11-25 15:54:26 +0100
commit5d784a2d4a0e302f51f6d96f44f5c21de47d6f19 (patch)
tree850951f4fd0a63820a71d7ad163e8b525c23d27a /drivers/ata
parent642fa50e555d2cb7812cdc470257b0251a3947af (diff)
libata: have ata_scsi_rw_xlat() fail invalid passthrough requests
commit 2d7271501720038381d45fb3dcbe4831228fc8cc upstream. For passthrough requests, libata-scsi takes what the user passes in as gospel. This can be problematic if the user fills in the CDB incorrectly. One example of that is in request sizes. For read/write commands, the CDB contains fields describing the transfer length of the request. These should match with the SG_IO header fields, but libata-scsi currently does no validation of that. Check that the number of blocks in the CDB for passthrough requests matches what was mapped into the request. If the CDB asks for more data then the validated SG_IO header fields, error it. Reported-by: Krishna Ram Prakash R <krp@gtux.in> Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/libata-scsi.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 4a267347a6d9..a44aeda57109 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -1654,6 +1654,21 @@ nothing_to_do:
return 1;
}
+static bool ata_check_nblocks(struct scsi_cmnd *scmd, u32 n_blocks)
+{
+ struct request *rq = scmd->request;
+ u32 req_blocks;
+
+ if (!blk_rq_is_passthrough(rq))
+ return true;
+
+ req_blocks = blk_rq_bytes(rq) / scmd->device->sector_size;
+ if (n_blocks > req_blocks)
+ return false;
+
+ return true;
+}
+
/**
* ata_scsi_rw_xlat - Translate SCSI r/w command into an ATA one
* @qc: Storage for translated ATA taskfile
@@ -1693,6 +1708,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
scsi_10_lba_len(cdb, &block, &n_block);
if (cdb[1] & (1 << 3))
tf_flags |= ATA_TFLAG_FUA;
+ if (!ata_check_nblocks(scmd, n_block))
+ goto invalid_fld;
break;
case READ_6:
case WRITE_6:
@@ -1705,6 +1722,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
*/
if (!n_block)
n_block = 256;
+ if (!ata_check_nblocks(scmd, n_block))
+ goto invalid_fld;
break;
case READ_16:
case WRITE_16:
@@ -1713,6 +1732,8 @@ static unsigned int ata_scsi_rw_xlat(struct ata_queued_cmd *qc)
scsi_16_lba_len(cdb, &block, &n_block);
if (cdb[1] & (1 << 3))
tf_flags |= ATA_TFLAG_FUA;
+ if (!ata_check_nblocks(scmd, n_block))
+ goto invalid_fld;
break;
default:
DPRINTK("no-byte command\n");