diff mbox series

scsi: target: tcmu: Fix crash on ARM during cmd completion

Message ID 20200624085320.31117-1-bstroesser@ts.fujitsu.com (mailing list archive)
State Superseded
Headers show
Series scsi: target: tcmu: Fix crash on ARM during cmd completion | expand

Commit Message

Bodo Stroesser June 24, 2020, 8:53 a.m. UTC
If tcmu_handle_completions() has to process a padding shorter
than sizeof(struct tcmu_cmd_entry), the current call to
tcmu_flush_dcache_range() with sizeof(struct tcmu_cmd_entry) as
length param is wrong and causes crashes on e.g. ARM, because
tcmu_flush_dcache_range() in this case calls
	flush_dcache_page(vmalloc_to_page(start));
with start being an invalid address above the end of the
vmalloc'ed area.

The fix is to use the maximum of remaining ring space and
sizeof(struct tcmu_cmd_entry) as the length param.

The patch was tested on kernel 4.19.118.

See https://bugzilla.kernel.org/show_bug.cgi?id=208045#c10

Signed-off-by: Bodo Stroesser <bstroesser@ts.fujitsu.com>
Tested-by: JiangYu <lnsyyj@hotmail.com>
---
 drivers/target/target_core_user.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Comments

Mike Christie June 28, 2020, 2:17 a.m. UTC | #1
On 6/24/20 3:53 AM, Bodo Stroesser wrote:
> If tcmu_handle_completions() has to process a padding shorter
> than sizeof(struct tcmu_cmd_entry), the current call to
> tcmu_flush_dcache_range() with sizeof(struct tcmu_cmd_entry) as
> length param is wrong and causes crashes on e.g. ARM, because
> tcmu_flush_dcache_range() in this case calls
> 	flush_dcache_page(vmalloc_to_page(start));
> with start being an invalid address above the end of the
> vmalloc'ed area.
> 
> The fix is to use the maximum of remaining ring space and
> sizeof(struct tcmu_cmd_entry) as the length param.
> 
> The patch was tested on kernel 4.19.118.
> 
> See https://urldefense.com/v3/__https://bugzilla.kernel.org/show_bug.cgi?id=208045*c10__;Iw!!GqivPVa7Brio!Kxr99oE0H1b9Ily4SE23nDN7ElSf8Tclo1RILfNSXb6iPh6DA5cSgtBQQsLMBKdrLsmT$ 
> 
> Signed-off-by: Bodo Stroesser <bstroesser@ts.fujitsu.com>
> Tested-by: JiangYu <lnsyyj@hotmail.com>
> ---
>  drivers/target/target_core_user.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
> index 3885ca532f8f..82e476d48194 100644
> --- a/drivers/target/target_core_user.c
> +++ b/drivers/target/target_core_user.c
> @@ -1221,7 +1221,14 @@ static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
>  
>  		struct tcmu_cmd_entry *entry = (void *) mb + CMDR_OFF + udev->cmdr_last_cleaned;
>  
> -		tcmu_flush_dcache_range(entry, sizeof(*entry));
> +		/*
> +		 * Flush max. up to end of cmd ring, since current entry might
> +		 * be a padding that is shorter than sizeof(*entry)
> +		 */
> +		size_t ring_left = head_to_end(udev->cmdr_last_cleaned,
> +					       udev->cmdr_size);
> +		tcmu_flush_dcache_range(entry, ring_left < sizeof(*entry) ?
> +					ring_left : sizeof(*entry));
>  
>  		if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) {
>  			UPDATE_HEAD(udev->cmdr_last_cleaned,
> 

Thanks again.

Acked-by: Mike Christie <michael.christie@oracle.com>
Bart Van Assche June 28, 2020, 2:31 a.m. UTC | #2
On 2020-06-24 01:53, Bodo Stroesser wrote:
> The fix is to use the maximum of remaining ring space and
> sizeof(struct tcmu_cmd_entry) as the length param.
> 

[ ... ]

> +		/*
> +		 * Flush max. up to end of cmd ring, since current entry might
> +		 * be a padding that is shorter than sizeof(*entry)
> +		 */
> +		size_t ring_left = head_to_end(udev->cmdr_last_cleaned,
> +					       udev->cmdr_size);
> +		tcmu_flush_dcache_range(entry, ring_left < sizeof(*entry) ?
> +					ring_left : sizeof(*entry));
>  
>  		if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) {
>  			UPDATE_HEAD(udev->cmdr_last_cleaned,

The patch description says "maximum" but the above formula calculates the
minimum of "ring_left" and sizeof(*entry). Did I perhaps misread this patch?

Thanks,

Bart.
Mike Christie June 28, 2020, 7:35 p.m. UTC | #3
> On Jun 27, 2020, at 9:31 PM, Bart Van Assche <bvanassche@acm.org> wrote:
> 
> On 2020-06-24 01:53, Bodo Stroesser wrote:
>> The fix is to use the maximum of remaining ring space and
>> sizeof(struct tcmu_cmd_entry) as the length param.
>> 
> 
> [ ... ]
> 
>> +		/*
>> +		 * Flush max. up to end of cmd ring, since current entry might
>> +		 * be a padding that is shorter than sizeof(*entry)
>> +		 */
>> +		size_t ring_left = head_to_end(udev->cmdr_last_cleaned,
>> +					       udev->cmdr_size);
>> +		tcmu_flush_dcache_range(entry, ring_left < sizeof(*entry) ?
>> +					ring_left : sizeof(*entry));
>> 
>> 		if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) {
>> 			UPDATE_HEAD(udev->cmdr_last_cleaned,
> 
> The patch description says "maximum" but the above formula calculates the
> minimum of "ring_left" and sizeof(*entry). Did I perhaps misread this patch?

Ah yeah, Bodo probably meant to write what they wrote for the comment above about the max up to the end of the ring and not max of space left and entry size.
Bodo Stroesser June 29, 2020, 8:53 a.m. UTC | #4
On 2020-06-28 21:35, Michael Christie wrote:
> 
> 
>> On Jun 27, 2020, at 9:31 PM, Bart Van Assche <bvanassche@acm.org> wrote:
>>
>> On 2020-06-24 01:53, Bodo Stroesser wrote:
>>> The fix is to use the maximum of remaining ring space and
>>> sizeof(struct tcmu_cmd_entry) as the length param.
>>>
>>
>> [ ... ]
>>
>>> +		/*
>>> +		 * Flush max. up to end of cmd ring, since current entry might
>>> +		 * be a padding that is shorter than sizeof(*entry)
>>> +		 */
>>> +		size_t ring_left = head_to_end(udev->cmdr_last_cleaned,
>>> +					       udev->cmdr_size);
>>> +		tcmu_flush_dcache_range(entry, ring_left < sizeof(*entry) ?
>>> +					ring_left : sizeof(*entry));
>>>
>>> 		if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) {
>>> 			UPDATE_HEAD(udev->cmdr_last_cleaned,
>>
>> The patch description says "maximum" but the above formula calculates the
>> minimum of "ring_left" and sizeof(*entry). Did I perhaps misread this patch?
> 
> Ah yeah, Bodo probably meant to write what they wrote for the comment above about the max up to the end of the ring and not max of space left and entry size.
> 

Thank you, you both are right.

While the code and the comment in the code are fine, patch description
is misleading or even wrong.

So I'm going to re-send the patch with fixed description and Mike's
Acked-by.

BR, Bodo
diff mbox series

Patch

diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c
index 3885ca532f8f..82e476d48194 100644
--- a/drivers/target/target_core_user.c
+++ b/drivers/target/target_core_user.c
@@ -1221,7 +1221,14 @@  static unsigned int tcmu_handle_completions(struct tcmu_dev *udev)
 
 		struct tcmu_cmd_entry *entry = (void *) mb + CMDR_OFF + udev->cmdr_last_cleaned;
 
-		tcmu_flush_dcache_range(entry, sizeof(*entry));
+		/*
+		 * Flush max. up to end of cmd ring, since current entry might
+		 * be a padding that is shorter than sizeof(*entry)
+		 */
+		size_t ring_left = head_to_end(udev->cmdr_last_cleaned,
+					       udev->cmdr_size);
+		tcmu_flush_dcache_range(entry, ring_left < sizeof(*entry) ?
+					ring_left : sizeof(*entry));
 
 		if (tcmu_hdr_get_op(entry->hdr.len_op) == TCMU_OP_PAD) {
 			UPDATE_HEAD(udev->cmdr_last_cleaned,