Skip to contentSkip to conversation

drm/vmwgfx: Fix potential Spectre v1

PATCH In review Gustavo A. R. Silva 3 participants 4 messages Aug 16, 2018 – Aug 23, 2018

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 drm/vmwgfx: Fix potential Spectre v1

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

---
 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +5 -2
index 1f13457..ad91c6e 100644
@@ -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)
Signature · 1 line
2.7.4
#1 lore raw

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

Quoted 67 lines

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

---
 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +1 -0
index 1f13457..ad91c6e 100644
@@ -25,6 +25,7 @@
  *
 
 **********************************************************
 ****************/
 #include <linux/sync_file.h>
+#include <linux/nospec.h>
 

#include "vmwgfx_drv.h"
#include "vmwgfx_reg.h"

drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +4 -2
@@ -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]),
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +0 -1
 			   copy_offset[arg.version - 1] -
 			   copy_offset[0]) != 0)
--

2.7.4

_______________________________________________
Sent to [email protected]

#2 lore raw
Quoted 50 lines · Deepak Singh Rawat

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

Quoted 44 lines

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]

---
  drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 7 +++++--
  1 file changed, 5 insertions(+), 2 deletions(-)
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +1 -0
index 1f13457..ad91c6e 100644
@@ -25,6 +25,7 @@
   *
 
 **********************************************************
 ****************/
  #include <linux/sync_file.h>
+#include <linux/nospec.h>
 
  #include "vmwgfx_drv.h"
  #include "vmwgfx_reg.h"
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +0 -0
@@ -4520,8 +4521,10 @@ int vmw_execbuf_ioctl(struct drm_device *dev,
 unsigned long data,
  		return -EINVAL;
  	}
 
Quoted 4 lines

drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c int vmw_execbuf_ioctl(struct drm_device *dev, unsigned long data, · line 4525

Quoted 4 lines
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +2 -2
-	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?

Quoted 6 lines
Quoted 6 lines
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +2 -0
+	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

Quoted 5 lines
Quoted 5 lines

--
2.7.4

_______________________________________________
Sent to [email protected]

_______________________________________________
dri-devel mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/dri-devel

#3 lore raw

Hi all,

Quoted 7 lines · Thomas Hellstrom
Quoted 7 lines
Quoted 7 lines

  #include "vmwgfx_drv.h"
  #include "vmwgfx_reg.h"

(quoted diff) +0 -0
@@ -4520,8 +4521,10 @@ int vmw_execbuf_ioctl(struct drm_device *dev,
 unsigned long data,
           return -EINVAL;
       }
 
Quoted 8 lines
Quoted 4 lines

Review int vmw_execbuf_ioctl(struct drm_device *dev, · line 4526

Quoted 4 lines
(quoted diff) +2 -2
-    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:

drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +3 -1
index 1f13457..3ef9f7b 100644
@@ -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,
Quoted 11 lines
Quoted 8 lines
Quoted 8 lines
(quoted diff) +2 -0
+    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