pmcraid_notify_ccn() and pmcraid_notify_ldn() take data_len out of the HCAM header the IOA wrote into the coherent DMA buffer and pass it on to pmcraid_notify_aen(), which uses it as the netlink attribute length: data_size = le32_to_cpu(pinstance->ccn.hcam->data_len) + sizeof(struct pmcraid_hcam_hdr); ... nla_put(skb, PMCRAID_AEN_ATTR_EVENT, data_size, aen_msg); data_len is never checked against the buffer it came from. The CCN buffer is 4048 bytes, so an IOA reporting more than 4008 makes nla_put() read past the end of it, and what it reads is multicast to whoever is bound to the event group. The LDN buffer is 4112 bytes and has the same problem above 4072. Driving pmcraid_notify_ccn() with data_len one byte past the limit, with the HCAM buffer allocated by kmalloc() at the production size because dma_alloc_coherent() memory is page granular and not tracked by KASAN: BUG: KASAN: slab-out-of-bounds in nla_put+0xc7/0x160 Read of size 4049 at addr 00000000622c2000 by task kunit_try_catch/19 CPU: 0 UID: 0 PID: 19 Comm: kunit_try_catch Tainted: G W N 7.3.0-rc7-g58161ead29e5-dirty #5 VOLUNTARY Tainted: [W]=WARN, [N]=TEST Call Trace: [<6008af12>] ? dump_stack_lvl+0x5e/0x7a [<6003e621>] ? _printk+0x0/0xd7 [<6004b21d>] ? print_report+0x14b/0x517 [<6003e621>] ? _printk+0x0/0xd7 [<606ffcb7>] ? nla_put+0xc7/0x160 [<603aeebb>] ? kasan_report+0x11b/0x160 [<60950e4d>] ? __alloc_skb+0x1bd/0x9c0 [<606ffcb7>] ? nla_put+0xc7/0x160 [<603afe70>] ? kasan_check_range+0x0/0x1c0 [<603affbe>] ? kasan_check_range+0x14e/0x1c0 [<603b0a41>] ? __asan_memcpy+0x31/0x80 [<606ffcb7>] ? nla_put+0xc7/0x160 [<608801af>] ? pmcraid_notify_aen+0x19f/0x440 [<60882ba7>] ? pmcraid_aen_ccn_oob_one+0x1a7/0x380 [<60213550>] ? ktime_get_ts64+0x0/0x2c0 [<606ea77c>] ? kunit_try_run_case+0x1bc/0x310 [<606ea5c0>] ? kunit_try_run_case+0x0/0x310 [<603b08f0>] ? __kasan_check_read+0x0/0x20 [<600af020>] ? um_set_signals+0x0/0x60 [<600af020>] ? um_set_signals+0x0/0x60 [<6012e3cd>] ? __kthread_parkme+0xdd/0x1c0 [<606ea5c0>] ? kunit_try_run_case+0x0/0x310 [<606f0840>] ? kunit_generic_run_threadfn_adapter+0x0/0x100 [<606f08bc>] ? kunit_generic_run_threadfn_adapter+0x7c/0x100 [<6013464e>] ? kthread+0x36e/0x560 [<601342e0>] ? kthread+0x0/0x560 [<6008f0de>] ? new_thread_handler+0xce/0x130 The buggy address belongs to the object at 00000000622c2000 which belongs to the cache kmalloc-4k of size 4096 The buggy address is located 0 bytes inside of allocated 4048-byte region [00000000622c2000, 00000000622c2fd0) W is a UML virtio warning and N marks the KUnit harness that drove the call; neither comes from this path. Clamp data_len to what the buffer can hold before it is used as a length. A working IOA never reports more than that, so this does not change what user space sees from one. Found by inspection. Not tested on a MaxRAID adapter. Fixes: 89a3681041507 ("[SCSI] pmcraid: PMC-Sierra MaxRAID driver to support 6Gb/s SAS RAID controller") Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c index 942a993932049..f59d5a775aebb 100644 --- a/drivers/scsi/pmcraid.c +++ b/drivers/scsi/pmcraid.c @@ -1451,6 +1451,17 @@ static int pmcraid_notify_aen( return result; } +/* The IOA picks data_len, so cap it at what the HCAM buffer holds before it + * becomes a copy length. Returns the size to report, header included. + */ +static u32 pmcraid_hcam_data_size(__le32 data_len, size_t hcam_size) +{ + size_t hdr_size = sizeof(struct pmcraid_hcam_hdr); + + return min_t(u32, le32_to_cpu(data_len), hcam_size - hdr_size) + + hdr_size; +} + /** * pmcraid_notify_ccn - notifies about CCN event msg to user space * @pinstance: pointer adapter instance structure @@ -1460,10 +1471,10 @@ static int pmcraid_notify_aen( */ static int pmcraid_notify_ccn(struct pmcraid_instance *pinstance) { - return pmcraid_notify_aen(pinstance, - pinstance->ccn.msg, - le32_to_cpu(pinstance->ccn.hcam->data_len) + - sizeof(struct pmcraid_hcam_hdr)); + u32 data_size = pmcraid_hcam_data_size(pinstance->ccn.hcam->data_len, + sizeof(struct pmcraid_hcam_ccn_ext)); + + return pmcraid_notify_aen(pinstance, pinstance->ccn.msg, data_size); } /** @@ -1475,10 +1486,10 @@ static int pmcraid_notify_ccn(struct pmcraid_instance *pinstance) */ static int pmcraid_notify_ldn(struct pmcraid_instance *pinstance) { - return pmcraid_notify_aen(pinstance, - pinstance->ldn.msg, - le32_to_cpu(pinstance->ldn.hcam->data_len) + - sizeof(struct pmcraid_hcam_hdr)); + u32 data_size = pmcraid_hcam_data_size(pinstance->ldn.hcam->data_len, + sizeof(struct pmcraid_hcam_ldn)); + + return pmcraid_notify_aen(pinstance, pinstance->ldn.msg, data_size); } /** -- 2.43.0