Skip to content

Commit 533a0b9

Browse files
bryamzxzdamien-lemoal
authored andcommitted
ata: libata-core: Reject an invalid concurrent positioning ranges count
ata_dev_config_cpr() takes the number of range descriptors from buf[0] of the concurrent positioning ranges log (up to 255), which the device reports independently of the log size in the GPL directory. The count is then walked at a fixed 32-byte stride in two places with no bound: the log read here, and the INQUIRY VPD page B9h emitter, which writes one descriptor per range into the fixed 2048-byte ata_scsi_rbuf. A device reporting a count larger than its own log overflows the read buffer (up to 7704 bytes past a 512-byte slab), and a count above 62 overflows the response buffer on the emit side. Bound the count once, on probe, against both the log the device returned and the number of descriptors the VPD B9h response buffer can hold (ATA_DEV_MAX_CPR, derived from the rbuf size). Reject an out-of-range count with a warning; this keeps the emitter in bounds with no separate change there. Suggested-by: Damien Le Moal <dlemoal@kernel.org> Fixes: fe22e1c ("libata: support concurrent positioning ranges log") Fixes: c745dfc ("libata: fix reading concurrent positioning ranges log") Cc: stable@vger.kernel.org Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Niklas Cassel <cassel@kernel.org> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
1 parent 462775c commit 533a0b9

3 files changed

Lines changed: 27 additions & 2 deletions

File tree

drivers/ata/libata-core.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2847,6 +2847,24 @@ static void ata_dev_config_cpr(struct ata_device *dev)
28472847
if (!nr_cpr)
28482848
goto out;
28492849

2850+
/*
2851+
* The device reports the number of CPR descriptors independently of the
2852+
* log size, and that count is also used to emit VPD page B9h into the
2853+
* fixed-size rbuf. Reject a count larger than what that buffer can hold
2854+
* (ATA_DEV_MAX_CPR) or larger than the log the device actually returned.
2855+
*/
2856+
if (nr_cpr > ATA_DEV_MAX_CPR) {
2857+
ata_dev_warn(dev,
2858+
"Too many concurrent positioning ranges\n");
2859+
goto out;
2860+
}
2861+
2862+
if (buf_len < 64 + (size_t)nr_cpr * 32) {
2863+
ata_dev_warn(dev,
2864+
"Invalid number of concurrent positioning ranges\n");
2865+
goto out;
2866+
}
2867+
28502868
cpr_log = kzalloc_flex(*cpr_log, cpr, nr_cpr);
28512869
if (!cpr_log)
28522870
goto out;

drivers/ata/libata-scsi.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@
3737
#include "libata.h"
3838
#include "libata-transport.h"
3939

40-
#define ATA_SCSI_RBUF_SIZE 2048
41-
4240
static DEFINE_SPINLOCK(ata_scsi_rbuf_lock);
4341
static u8 ata_scsi_rbuf[ATA_SCSI_RBUF_SIZE];
4442

drivers/ata/libata.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,15 @@ static inline bool ata_acpi_dev_manage_restart(struct ata_device *dev) { return
149149
#endif
150150

151151
/* libata-scsi.c */
152+
#define ATA_SCSI_RBUF_SIZE 2048
153+
154+
/*
155+
* Maximum number of concurrent positioning ranges (CPR) supported. The ACS
156+
* specifications allow up to 255, but we limit this to the number of CPR
157+
* descriptors that fit in the rbuf buffer used to emit VPD page B9h.
158+
*/
159+
#define ATA_DEV_MAX_CPR min(255, ((ATA_SCSI_RBUF_SIZE - 64) / 32))
160+
152161
extern struct ata_device *ata_scsi_find_dev(struct ata_port *ap,
153162
const struct scsi_device *scsidev);
154163
extern int ata_scsi_add_hosts(struct ata_host *host,

0 commit comments

Comments
 (0)