diff options
| author | Vincent Mailhol <mailhol.vincent@wanadoo.fr> | 2025-02-18 23:32:28 +0900 | 
|---|---|---|
| committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2025-03-14 09:44:54 +0100 | 
| commit | 1d22a122ffb116c3cf78053e812b8b21f8852ee9 (patch) | |
| tree | c4cb38ea61628bfcf252edade80c9b95b1c37d2c | |
| parent | 4003c9e78778e93188a09d6043a74f7154449d43 (diff) | |
can: ucan: fix out of bound read in strscpy() source
Commit 7fdaf8966aae ("can: ucan: use strscpy() to instead of strncpy()")
unintentionally introduced a one byte out of bound read on strscpy()'s
source argument (which is kind of ironic knowing that strscpy() is meant
to be a more secure alternative :)).
Let's consider below buffers:
  dest[len + 1]; /* will be NUL terminated */
  src[len]; /* may not be NUL terminated */
When doing:
  strncpy(dest, src, len);
  dest[len] = '\0';
strncpy() will read up to len bytes from src.
On the other hand:
  strscpy(dest, src, len + 1);
will read up to len + 1 bytes from src, that is to say, an out of bound
read of one byte will occur on src if it is not NUL terminated. Note
that the src[len] byte is never copied, but strscpy() still needs to
read it to check whether a truncation occurred or not.
This exact pattern happened in ucan.
The root cause is that the source is not NUL terminated. Instead of
doing a copy in a local buffer, directly NUL terminate it as soon as
usb_control_msg() returns. With this, the local firmware_str[] variable
can be removed.
On top of this do a couple refactors:
  - ucan_ctl_payload->raw is only used for the firmware string, so
    rename it to ucan_ctl_payload->fw_str and change its type from u8 to
    char.
  - ucan_device_request_in() is only used to retrieve the firmware
    string, so rename it to ucan_get_fw_str() and refactor it to make it
    directly handle all the string termination logic.
Reported-by: syzbot+d7d8c418e8317899e88c@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/linux-can/67b323a4.050a0220.173698.002b.GAE@google.com/
Fixes: 7fdaf8966aae ("can: ucan: use strscpy() to instead of strncpy()")
Signed-off-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Link: https://patch.msgid.link/20250218143515.627682-2-mailhol.vincent@wanadoo.fr
Cc: stable@vger.kernel.org
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
| -rw-r--r-- | drivers/net/can/usb/ucan.c | 43 | 
1 files changed, 18 insertions, 25 deletions
| diff --git a/drivers/net/can/usb/ucan.c b/drivers/net/can/usb/ucan.c index 39a63b7313a4..07406daf7c88 100644 --- a/drivers/net/can/usb/ucan.c +++ b/drivers/net/can/usb/ucan.c @@ -186,7 +186,7 @@ union ucan_ctl_payload {  	 */  	struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version; -	u8 raw[128]; +	u8 fw_str[128];  } __packed;  enum { @@ -424,18 +424,20 @@ static int ucan_ctrl_command_out(struct ucan_priv *up,  			       UCAN_USB_CTL_PIPE_TIMEOUT);  } -static int ucan_device_request_in(struct ucan_priv *up, -				  u8 cmd, u16 subcmd, u16 datalen) +static void ucan_get_fw_str(struct ucan_priv *up, char *fw_str, size_t size)  { -	return usb_control_msg(up->udev, -			       usb_rcvctrlpipe(up->udev, 0), -			       cmd, -			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, -			       subcmd, -			       0, -			       up->ctl_msg_buffer, -			       datalen, -			       UCAN_USB_CTL_PIPE_TIMEOUT); +	int ret; + +	ret = usb_control_msg(up->udev, usb_rcvctrlpipe(up->udev, 0), +			      UCAN_DEVICE_GET_FW_STRING, +			      USB_DIR_IN | USB_TYPE_VENDOR | +			      USB_RECIP_DEVICE, +			      0, 0, fw_str, size - 1, +			      UCAN_USB_CTL_PIPE_TIMEOUT); +	if (ret > 0) +		fw_str[ret] = '\0'; +	else +		strscpy(fw_str, "unknown", size);  }  /* Parse the device information structure reported by the device and @@ -1314,7 +1316,6 @@ static int ucan_probe(struct usb_interface *intf,  	u8 in_ep_addr;  	u8 out_ep_addr;  	union ucan_ctl_payload *ctl_msg_buffer; -	char firmware_str[sizeof(union ucan_ctl_payload) + 1];  	udev = interface_to_usbdev(intf); @@ -1527,17 +1528,6 @@ static int ucan_probe(struct usb_interface *intf,  	 */  	ucan_parse_device_info(up, &ctl_msg_buffer->cmd_get_device_info); -	/* just print some device information - if available */ -	ret = ucan_device_request_in(up, UCAN_DEVICE_GET_FW_STRING, 0, -				     sizeof(union ucan_ctl_payload)); -	if (ret > 0) { -		/* copy string while ensuring zero termination */ -		strscpy(firmware_str, up->ctl_msg_buffer->raw, -			sizeof(union ucan_ctl_payload) + 1); -	} else { -		strcpy(firmware_str, "unknown"); -	} -  	/* device is compatible, reset it */  	ret = ucan_ctrl_command_out(up, UCAN_COMMAND_RESET, 0, 0);  	if (ret < 0) @@ -1555,7 +1545,10 @@ static int ucan_probe(struct usb_interface *intf,  	/* initialisation complete, log device info */  	netdev_info(up->netdev, "registered device\n"); -	netdev_info(up->netdev, "firmware string: %s\n", firmware_str); +	ucan_get_fw_str(up, up->ctl_msg_buffer->fw_str, +			sizeof(up->ctl_msg_buffer->fw_str)); +	netdev_info(up->netdev, "firmware string: %s\n", +		    up->ctl_msg_buffer->fw_str);  	/* success */  	return 0; | 
