diff mbox series

drm/vmwgfx: Fix potential Spectre v1

Message ID 20180816193015.GA12093@embeddedor.com (mailing list archive)
State New, archived
Headers show
Series drm/vmwgfx: Fix potential Spectre v1 | expand

Commit Message

Gustavo A. R. Silva Aug. 16, 2018, 7:30 p.m. UTC
arg.version is indirectly controlled by user-space, hence leading to
a potential exploitation of the Spectre variant 1 vulnerability.

This issue was detected with the help of Smatch:

drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c:4526 vmw_execbuf_ioctl() warn:
potential spectre issue 'copy_offset' [w]

Fix this by sanitizing arg.version before using it to index copy_offset

Notice that given that speculation windows are large, the policy is
to kill the speculation on the first load and not worry if it can be
completed with a dependent load/store [1].

[1] https://marc.info/?l=linux-kernel&m=152449131114778&w=2

Cc: stable@vger.kernel.org
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

Comments

Deepak Singh Rawat Aug. 20, 2018, 8:53 p.m. UTC | #1
Looks good to me based on my limited understanding. Thomas/Sinclair can
could you please review and then we can include this in drm-fixes.

Thanks,
Deepak

> 
> arg.version is indirectly controlled by user-space, hence leading to
> a potential exploitation of the Spectre variant 1 vulnerability.
> 
> This issue was detected with the help of Smatch:
> 
> drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c:4526 vmw_execbuf_ioctl()
> warn:
> potential spectre issue 'copy_offset' [w]
> 
> Fix this by sanitizing arg.version before using it to index copy_offset
> 
> Notice that given that speculation windows are large, the policy is
> to kill the speculation on the first load and not worry if it can be
> completed with a dependent load/store [1].
> 
> [1]
> https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fmarc.i
> nfo%2F%3Fl%3Dlinux-
> kernel%26m%3D152449131114778%26w%3D2&amp;data=02%7C01%7Clinux-
> graphics-
> maintainer%40vmware.com%7Cf010b707b8ef4896c1a908d603aebcc6%7Cb39
> 138ca3cee4b4aa4d6cd83d9dd62f0%7C1%7C0%7C636700446365603728&amp;
> sdata=0D8lnUScxOmCCWXLHh8Otc3o%2F1yF1SxgGwIklRdMlXY%3D&amp;re
> served=0
> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
> ---
>  drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
> b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
> index 1f13457..ad91c6e 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
> @@ -25,6 +25,7 @@
>   *
> 
> **********************************************************
> ****************/
>  #include <linux/sync_file.h>
> +#include <linux/nospec.h>
> 
>  #include "vmwgfx_drv.h"
>  #include "vmwgfx_reg.h"
> @@ -4520,8 +4521,10 @@ int vmw_execbuf_ioctl(struct drm_device *dev,
> unsigned long data,
>  		return -EINVAL;
>  	}
> 
> -	if (arg.version > 1 &&
> -	    copy_from_user(&arg.context_handle,
> +	if (arg.version >= ARRAY_SIZE(copy_offset))
> +		return -EFAULT;
> +	arg.version = array_index_nospec(arg.version,
> ARRAY_SIZE(copy_offset));
> +	if (copy_from_user(&arg.context_handle,
>  			   (void __user *) (data + copy_offset[0]),
>  			   copy_offset[arg.version - 1] -
>  			   copy_offset[0]) != 0)
> --
> 2.7.4
> 
> _______________________________________________
> Sent to linux-graphics-maintainer@vmware.com
Thomas Hellstrom Aug. 21, 2018, 8:19 a.m. UTC | #2
On 08/20/2018 10:53 PM, Deepak Singh Rawat wrote:
> Looks good to me based on my limited understanding. Thomas/Sinclair can
> could you please review and then we can include this in drm-fixes.
>
> Thanks,
> Deepak
>
>> arg.version is indirectly controlled by user-space, hence leading to
>> a potential exploitation of the Spectre variant 1 vulnerability.
>>
>> This issue was detected with the help of Smatch:
>>
>> drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c:4526 vmw_execbuf_ioctl()
>> warn:
>> potential spectre issue 'copy_offset' [w]
>>
>> Fix this by sanitizing arg.version before using it to index copy_offset
>>
>> Notice that given that speculation windows are large, the policy is
>> to kill the speculation on the first load and not worry if it can be
>> completed with a dependent load/store [1].
>>
>> [1]
>>
>>
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
>> ---
>>   drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
>>   1 file changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
>> b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
>> index 1f13457..ad91c6e 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
>> @@ -25,6 +25,7 @@
>>    *
>>
>> **********************************************************
>> ****************/
>>   #include <linux/sync_file.h>
>> +#include <linux/nospec.h>
>>
>>   #include "vmwgfx_drv.h"
>>   #include "vmwgfx_reg.h"
>> @@ -4520,8 +4521,10 @@ int vmw_execbuf_ioctl(struct drm_device *dev,
>> unsigned long data,
>>   		return -EINVAL;
>>   	}
>>
>> -	if (arg.version > 1 &&
>> -	    copy_from_user(&arg.context_handle,
>> +	if (arg.version >= ARRAY_SIZE(copy_offset))
>> +		return -EFAULT;

I must admit my understanding of spectre workings in this case is 
limited, but why do you need to compare
arg.version against ARRAY_SIZE here, when it is already checked against 
DRM_VMW_EXECBUF_VERSION earlier?



>> +	arg.version = array_index_nospec(arg.version,
>> ARRAY_SIZE(copy_offset));
>> +	if (copy_from_user(&arg.context_handle,
>>   			   (void __user *) (data + copy_offset[0]),
>>   			   copy_offset[arg.version - 1] -
>>   			   copy_offset[0]) != 0)

Similarly, we want to perform this copy iff arg.version > 1. Why did you 
remove that check?

Thanks,
Thomas

>> --
>> 2.7.4
>>
>> _______________________________________________
>> Sent to linux-graphics-maintainer@vmware.com
Gustavo A. R. Silva Aug. 23, 2018, 2:43 p.m. UTC | #3
Hi all,

On 8/21/18 3:19 AM, Thomas Hellstrom wrote:

>>>   #include "vmwgfx_drv.h"
>>>   #include "vmwgfx_reg.h"
>>> @@ -4520,8 +4521,10 @@ int vmw_execbuf_ioctl(struct drm_device *dev,
>>> unsigned long data,
>>>           return -EINVAL;
>>>       }
>>>
>>> -    if (arg.version > 1 &&
>>> -        copy_from_user(&arg.context_handle,
>>> +    if (arg.version >= ARRAY_SIZE(copy_offset))
>>> +        return -EFAULT;
> 
> I must admit my understanding of spectre workings in this case is limited, but why do you need to compare
> arg.version against ARRAY_SIZE here, when it is already checked against DRM_VMW_EXECBUF_VERSION earlier?
> 
Oh, I wasn't aware of the value in DRM_VMW_EXECBUF_VERSION. But as arg.version is used to index copy_offset,
it is safer to compare its value against the actual size of copy_offset.

So, what do you think if I replace DRM_VMW_EXECBUF_VERSION with ARRAY_SIZE instead of adding a new check
against ARRAY_SIZE?

Something like:

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 1f13457..3ef9f7b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -25,6 +25,7 @@
  *
  **************************************************************************/
 #include <linux/sync_file.h>
+#include <linux/nospec.h>

 #include "vmwgfx_drv.h"
 #include "vmwgfx_reg.h"
@@ -4514,11 +4515,12 @@ int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data,
         * arg.version.
         */

-       if (unlikely(arg.version > DRM_VMW_EXECBUF_VERSION ||
+       if (unlikely(arg.version > ARRAY_SIZE(copy_offset) ||
                     arg.version == 0)) {
                DRM_ERROR("Incorrect execbuf version.\n");
                return -EINVAL;
        }
+       arg.version = array_index_nospec(arg.version, ARRAY_SIZE(copy_offset));

        if (arg.version > 1 &&
            copy_from_user(&arg.context_handle,


> 
> 
>>> +    arg.version = array_index_nospec(arg.version,
>>> ARRAY_SIZE(copy_offset));
>>> +    if (copy_from_user(&arg.context_handle,
>>>                  (void __user *) (data + copy_offset[0]),
>>>                  copy_offset[arg.version - 1] -
>>>                  copy_offset[0]) != 0)
> 
> Similarly, we want to perform this copy iff arg.version > 1. Why did you remove that check?
> 

Yeah, this check must remain in place. I will add it back and send v2.

Thanks for the feedback!
--
Gustavo
diff mbox series

Patch

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 1f13457..ad91c6e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -25,6 +25,7 @@ 
  *
  **************************************************************************/
 #include <linux/sync_file.h>
+#include <linux/nospec.h>
 
 #include "vmwgfx_drv.h"
 #include "vmwgfx_reg.h"
@@ -4520,8 +4521,10 @@  int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data,
 		return -EINVAL;
 	}
 
-	if (arg.version > 1 &&
-	    copy_from_user(&arg.context_handle,
+	if (arg.version >= ARRAY_SIZE(copy_offset))
+		return -EFAULT;
+	arg.version = array_index_nospec(arg.version, ARRAY_SIZE(copy_offset));
+	if (copy_from_user(&arg.context_handle,
 			   (void __user *) (data + copy_offset[0]),
 			   copy_offset[arg.version - 1] -
 			   copy_offset[0]) != 0)