Linux SCSI subsystem development
 help / color / mirror / Atom feed
* [PATCH] scsi: 3w-sas: bound firmware error string parsing
@ 2026-07-04  1:06 Pengpeng Hou
  2026-07-04  1:17 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Pengpeng Hou @ 2026-07-04  1:06 UTC (permalink / raw)
  To: Adam Radford, James E.J. Bottomley, Martin K. Petersen
  Cc: linux-scsi, linux-kernel, Pengpeng Hou

3w-sas parses two NUL-separated firmware strings from the fixed
err_specific_desc[] field.  The AEN and sense paths locate the second
string with strlen(first) + 1 before guaranteeing that the first
string is terminated inside the array.

Force a terminator in the fixed field before parsing, use strnlen()
for the first and optional second strings, and copy only the bytes
known to reside in err_specific_desc[].

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 drivers/scsi/3w-sas.c | 28 +++++++++++++++++++++++-----
 1 file changed, 23 insertions(+), 5 deletions(-)

--- a/drivers/scsi/3w-sas.c
+++ b/drivers/scsi/3w-sas.c
@@ -227,6 +227,7 @@
 	unsigned short aen;
 	char host[16];
 	char *error_str;
+	size_t desc_len, error_len;
 
 	tw_dev->aen_count++;
 
@@ -250,11 +251,21 @@
 	tw_dev->error_sequence_id++;
 
 	/* Check for embedded error string */
-	error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
-
 	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
-	event->parameter_len = strlen(header->err_specific_desc);
-	memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
+	desc_len = strnlen(header->err_specific_desc,
+			   sizeof(header->err_specific_desc));
+	error_str = "";
+	error_len = 0;
+	if (desc_len + 1 < sizeof(header->err_specific_desc)) {
+		error_str = &header->err_specific_desc[desc_len + 1];
+		error_len = strnlen(error_str,
+				    sizeof(header->err_specific_desc) -
+				    desc_len - 1);
+	}
+
+	event->parameter_len = desc_len;
+	memcpy(event->parameter_data, header->err_specific_desc,
+	       desc_len + (error_len ? 1 + error_len : 0));
 	if (event->severity != TW_AEN_SEVERITY_DEBUG)
 		printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",
 		       host,
@@ -862,12 +873,19 @@
 	TW_Command_Full *full_command_packet;
 	unsigned short error;
 	char *error_str;
+	size_t desc_len;
 
 	header = tw_dev->sense_buffer_virt[i];
 	full_command_packet = tw_dev->command_packet_virt[request_id];
 
 	/* Get embedded firmware error string */
-	error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]);
+	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
+	desc_len = strnlen(header->err_specific_desc,
+			   sizeof(header->err_specific_desc));
+	if (desc_len + 1 < sizeof(header->err_specific_desc))
+		error_str = &header->err_specific_desc[desc_len + 1];
+	else
+		error_str = "";
 
 	/* Don't print error for Logical unit not supported during rollcall */
 	error = le16_to_cpu(header->status_block.error);


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] scsi: 3w-sas: bound firmware error string parsing
  2026-07-04  1:06 [PATCH] scsi: 3w-sas: bound firmware error string parsing Pengpeng Hou
@ 2026-07-04  1:17 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-07-04  1:17 UTC (permalink / raw)
  To: Pengpeng Hou; +Cc: linux-scsi

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [High] Incomplete Security Fix / Missing Bug Fix in Sibling Driver
- [High] Use-After-Free (UAF) in character device ioctl
- [Medium] Teardown Asymmetry breaks all remaining controllers on single device removal
- [Medium] Incorrect endianness conversion for hardware error code in printk statement
- [Low] Missing endianness annotations on hardware structures hides bugs
--

commit 027577f5ce5e60299d84e8b1953276ed554014b3
Author: Pengpeng Hou <pengpeng@iscas.ac.cn>

scsi: 3w-sas: bound firmware error string parsing

3w-sas parses two NUL-separated firmware strings from the fixed
err_specific_desc[] field.  The AEN and sense paths locate the second
string with strlen(first) + 1 before guaranteeing that the first
string is terminated inside the array.

Force a terminator in the fixed field before parsing, use strnlen()
for the first and optional second strings, and copy only the bytes
known to reside in err_specific_desc[].

> diff --git a/drivers/scsi/3w-sas.c b/drivers/scsi/3w-sas.c
> index 52dc1aa639f7e..0a5e23819a826 100644
> --- a/drivers/scsi/3w-sas.c
> +++ b/drivers/scsi/3w-sas.c

[ ... ]

> @@ -250,11 +251,21 @@ static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_H
>  	tw_dev->error_sequence_id++;
>  
>  	/* Check for embedded error string */
> -	error_str = &(header->err_specific_desc[strlen(header->err_specific_desc)+1]);
> -
>  	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
> -	event->parameter_len = strlen(header->err_specific_desc);
> -	memcpy(event->parameter_data, header->err_specific_desc, event->parameter_len + 1 + strlen(error_str));
> +	desc_len = strnlen(header->err_specific_desc,
> +			   sizeof(header->err_specific_desc));
> +	error_str = "";
> +	error_len = 0;
> +	if (desc_len + 1 < sizeof(header->err_specific_desc)) {
> +		error_str = &header->err_specific_desc[desc_len + 1];
> +		error_len = strnlen(error_str,
> +				    sizeof(header->err_specific_desc) -
> +				    desc_len - 1);
> +	}
> +
> +	event->parameter_len = desc_len;
> +	memcpy(event->parameter_data, header->err_specific_desc,
> +	       desc_len + (error_len ? 1 + error_len : 0));

[Severity: High]
This is a pre-existing issue, but the exact same vulnerability patched by 
this commit in 3w-sas.c appears to be present in the sibling 3w-9xxx.c
driver.

Does twa_aen_queue_event() in drivers/scsi/3w-9xxx.c also need this string
termination fix to prevent a heap buffer overflow when writing to the
event->parameter_data array?

>  	if (event->severity != TW_AEN_SEVERITY_DEBUG)
>  		printk(KERN_WARNING "3w-sas:%s AEN: %s (0x%02X:0x%04X): %s:%s.\n",

[ ... ]

> @@ -862,12 +873,19 @@ static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, in
>  	TW_Command_Full *full_command_packet;
>  	unsigned short error;
>  	char *error_str;
> +	size_t desc_len;
>  
>  	header = tw_dev->sense_buffer_virt[i];
>  	full_command_packet = tw_dev->command_packet_virt[request_id];
>  
>  	/* Get embedded firmware error string */
> -	error_str = &(header->err_specific_desc[strlen(header->err_specific_desc) + 1]);
> +	header->err_specific_desc[sizeof(header->err_specific_desc) - 1] = '\0';
> +	desc_len = strnlen(header->err_specific_desc,
> +			   sizeof(header->err_specific_desc));
> +	if (desc_len + 1 < sizeof(header->err_specific_desc))
> +		error_str = &header->err_specific_desc[desc_len + 1];
> +	else
> +		error_str = "";
>  
>  	/* Don't print error for Logical unit not supported during rollcall */
>  	error = le16_to_cpu(header->status_block.error);

[Severity: Medium]
This isn't a bug introduced by this patch, but does the printk statement
immediately following this line log the byte-swapped hardware error code on
big-endian architectures? 

The code correctly converts the hardware error code to local endianness
using le16_to_cpu(header->status_block.error), but then passes the
unconverted header->status_block.error to %04X instead of using the local
error variable.


While reviewing these changes, I noticed a few other pre-existing issues
in this driver that might be worth looking into:

[Severity: High]
This is a pre-existing issue, but is it possible for a use-after-free to
occur if a user-space process opens /dev/twl and the PCI device is
hot-removed?

In twl_remove(), scsi_host_put() frees the device structure without clearing
the pointer from twl_device_extension_list:

drivers/scsi/3w-sas.c:twl_remove() {
    ...
    scsi_host_put(tw_dev->host);
    pci_disable_device(pdev);
    twl_device_extension_count--;
    ...
}

Could a subsequent ioctl call to twl_chrdev_ioctl() dereference the freed
pointer since it unconditionally fetches it from the global array?

drivers/scsi/3w-sas.c:twl_chrdev_ioctl() {
    ...
    TW_Device_Extension *tw_dev = twl_device_extension_list[iminor(inode)];
    mutex_lock(&twl_chrdev_mutex);
    if (mutex_lock_interruptible(&tw_dev->ioctl_lock)) {
    ...
}

[Severity: Medium]
This is a pre-existing issue, but does twl_remove() unconditionally destroy
the management ioctl interface for all active controllers?

If one controller is unbound on a multi-controller system,
unregister_chrdev() is called and twl_major is set to -1, which might break
communication with the remaining controllers:

drivers/scsi/3w-sas.c:twl_remove() {
    ...
    if (twl_major >= 0) {
        unregister_chrdev(twl_major, "twl");
        twl_major = -1;
    }
    ...
}

[Severity: Low]
This is a pre-existing issue, but should the hardware-defined structures
like TW_Command_Apache_Header use __le16 instead of unsigned short for
fields like error?

drivers/scsi/3w-sas.h:TAG_TW_Command_Apache_Header {
    ...
    unsigned short error;
    ...
}

Using __le16 might help static analysis tools catch endianness regressions
like the printk bug mentioned above.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260704010619.81878-1-pengpeng@iscas.ac.cn?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-07-04  1:17 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-04  1:06 [PATCH] scsi: 3w-sas: bound firmware error string parsing Pengpeng Hou
2026-07-04  1:17 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox