vmw_gb_surface_reference_internal() checks srf->res.guest_memory_bo
for NULL and reads its vma_node offset and base.size outside the
cmdbuf_mutex scope that protects the backup pointer.
A concurrent backup switch, e.g. from SVGA_3D_CMD_BIND_GB_SURFACE
via vmw_resource_unreserve(), drops its reference to the old buffer
and installs a new one under cmdbuf_mutex. The unlocked check in
the surface reference ioctl can see a transient NULL. The reads
after the unlock can return another buffer's offset and size; a
further switch can drop that buffer's last reference and free it
while the ioctl reads from it.
Move the NULL check inside the mutex, and read buffer_map_handle
and buffer_size while still holding it.
Fixes: a97e21923b42 ("drm/vmwgfx: Hook up guest-backed surfaces")
Cc: [email protected]
Reported-by: Sashiko <[email protected]>
Signed-off-by: Aldo Ariel Panzardo <[email protected]>
---
drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index 97966d849583..291e3efad9ad 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -1718,23 +1718,32 @@ vmw_gb_surface_reference_internal(struct drm_device
*dev,
user_srf = container_of(base, struct vmw_user_surface, prime.base);
srf = &user_srf->srf;
+ metadata = &srf->metadata;
+
+ mutex_lock(&dev_priv->cmdbuf_mutex);
+
if (!srf->res.guest_memory_bo) {
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
DRM_ERROR("Shared GB surface is missing a backup buffer.\n");
ret = -EINVAL;
goto out_bad_resource;
}
- metadata = &srf->metadata;
- mutex_lock(&dev_priv->cmdbuf_mutex); /* Protect res->backup */
ret = drm_gem_handle_create(file_priv,
&srf->res.guest_memory_bo->tbo.base,
&backup_handle);
- mutex_unlock(&dev_priv->cmdbuf_mutex);
if (ret != 0) {
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
drm_err(dev, "Wasn't able to create a backing handle for
surface sid = %u.\n",
req->sid);
goto out_bad_resource;
}
+ rep->crep.buffer_map_handle =
+
drm_vma_node_offset_addr(&srf->res.guest_memory_bo->tbo.base.vma_node);
+ rep->crep.buffer_size = srf->res.guest_memory_bo->tbo.base.size;
+
+ mutex_unlock(&dev_priv->cmdbuf_mutex);
+
rep->creq.base.svga3d_flags = SVGA3D_FLAGS_LOWER_32(metadata->flags);
rep->creq.base.format = metadata->format;
rep->creq.base.mip_levels = metadata->mip_levels[0];
@@ -1747,9 +1756,6 @@ vmw_gb_surface_reference_internal(struct drm_device *dev,
rep->crep.handle = user_srf->prime.base.handle;
rep->crep.backup_size = srf->res.guest_memory_size;
rep->crep.buffer_handle = backup_handle;
- rep->crep.buffer_map_handle =
-
drm_vma_node_offset_addr(&srf->res.guest_memory_bo->tbo.base.vma_node);
- rep->crep.buffer_size = srf->res.guest_memory_bo->tbo.base.size;
rep->creq.version = drm_vmw_gb_surface_v1;
rep->creq.svga3d_flags_upper_32_bits =
--
2.43.0