drm fixes for 5.18-rc7 (part two)

fbdev:
 - revert NULL deref fix that turned into a use-after-free
 - prevent use-after-free in fbdev
 - efifb/simplefb/vesafb: fix cleanup paths to avoid use-after-frees
 
 dma-buf:
 - fix panic in stats setup
 
 vc4:
 - fix hdmi build
 
 nouveau:
 - tegra iommu present fix
 - fix leak in backlight name
 
 vmwgfx:
 - Black screen due to fences using FIFO checks on SVGA3
 - Random black screens on boot due to uninitialized drm_mode_fb_cmd2
 - Hangs on SVGA3 due to command buffers being used with gbobjects
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmJ+37oACgkQDHTzWXnE
 hr4ctRAAn4ew42dIcIySrU5OdXnSKkT3172SqI02w84lT3vTNFgfr7PMyeP0DGg6
 64l1OHl/NpGXA53UaUJ/uLIp/qctg56YOUeQYj1OiAilqmmauHJlBgZF4czDl88W
 gmz4LLdM8E0UP10LVDe60xqXIr6WEFxUlv1E6gu4Y9vv7EKTDtRsfBOvrkvPQKOJ
 v6Y6iyulWQAk1KdiCxuKnQxA8/Pvk1bgE+gNdemX8ebwSr8i0UMi+JfpBxSeQXVw
 Ot4AW9zWu1gDYden9D+zQTfcCEIrfDKyEHOz3+JHQXHUrW3HsYlCH/PfDCH0pS6w
 fKjOGDVhmnU5oAElKzz1ipDVP+x2b8T9lXXWkI8TMQyk5NIgdBwO8nz1vPRgcyQJ
 aoORiurmONxv7Hacf3xq1+4iLrkCnUFfDmDSYC1fKjomgcinBkvBjjAgGPZQ/rv0
 cL0FAE+SQC1agku6PoWur5C5QZWBEnrSkFBBjEJRmdM26HoPJVvvzwh/MaT2vvhd
 gk8iBijA3jKR+Oummv0IypsOblU8rNfl6kr4s1nxnOUDYvly+942MPw3yNXfHRmh
 Vi7s2AeRcJzrsy0+gmCr62+cH00/cHPHfMde9c1r6ekVp+TCA2/nkUgt8WQf2PIq
 OG5jvIqdNVGqDNXexS1kfrRB9KMXf/T83h/brqFDuMk5JhPlub8=
 =wF/s
 -----END PGP SIGNATURE-----

Merge tag 'drm-fixes-2022-05-14' of git://anongit.freedesktop.org/drm/drm

Pull more drm fixes from Dave Airlie:
 "Turns out I was right, some fixes hadn't made it to me yet. The vmwgfx
  ones also popped up later, but all seem like bad enough things to fix.
  The dma-buf, vc4 and nouveau ones are all pretty small.

  The fbdev fixes are a bit more complicated: a fix to cleanup fbdev
  devices properly, uncovered some use-after-free bugs in existing
  drivers. Then the fix for those bugs wasn't correct. This reverts that
  fix, and puts the proper fixes in place in the drivers to avoid the
  use-after-frees.

  This has had a fair number of eyes on it at this stage, and I'm
  confident enough that it puts things in the right place, and is less
  dangerous than reverting our way out of the initial change at this
  stage.

  fbdev:
   - revert NULL deref fix that turned into a use-after-free
   - prevent use-after-free in fbdev
   - efifb/simplefb/vesafb: fix cleanup paths to avoid use-after-frees

  dma-buf:
   - fix panic in stats setup

  vc4:
   - fix hdmi build

  nouveau:
   - tegra iommu present fix
   - fix leak in backlight name

  vmwgfx:
   - Black screen due to fences using FIFO checks on SVGA3
   - Random black screens on boot due to uninitialized drm_mode_fb_cmd2
   - Hangs on SVGA3 due to command buffers being used with gbobjects"

* tag 'drm-fixes-2022-05-14' of git://anongit.freedesktop.org/drm/drm:
  drm/vmwgfx: Disable command buffers on svga3 without gbobjects
  drm/vmwgfx: Initialize drm_mode_fb_cmd2
  drm/vmwgfx: Fix fencing on SVGAv3
  drm/vc4: hdmi: Fix build error for implicit function declaration
  dma-buf: call dma_buf_stats_setup after dmabuf is in valid list
  fbdev: efifb: Fix a use-after-free due early fb_info cleanup
  drm/nouveau: Fix a potential theorical leak in nouveau_get_backlight_name()
  drm/nouveau/tegra: Stop using iommu_present()
  fbdev: vesafb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: efifb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: simplefb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: Prevent possible use-after-free in fb_release()
  Revert "fbdev: Make fb_release() return -ENODEV if fbdev was unregistered"
This commit is contained in:
Linus Torvalds 2022-05-13 16:20:25 -07:00
commit ec7f49619d
15 changed files with 99 additions and 40 deletions

View File

@ -543,10 +543,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
file->f_mode |= FMODE_LSEEK;
dmabuf->file = file;
ret = dma_buf_stats_setup(dmabuf);
if (ret)
goto err_sysfs;
mutex_init(&dmabuf->lock);
INIT_LIST_HEAD(&dmabuf->attachments);
@ -554,6 +550,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
list_add(&dmabuf->list_node, &db_list.head);
mutex_unlock(&db_list.lock);
ret = dma_buf_stats_setup(dmabuf);
if (ret)
goto err_sysfs;
return dmabuf;
err_sysfs:

View File

@ -46,8 +46,9 @@ static bool
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
struct nouveau_backlight *bl)
{
const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL);
if (nb < 0 || nb >= 100)
const int nb = ida_alloc_max(&bl_ida, 99, GFP_KERNEL);
if (nb < 0)
return false;
if (nb > 0)
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
@ -414,7 +415,7 @@ nouveau_backlight_init(struct drm_connector *connector)
nv_encoder, ops, &props);
if (IS_ERR(bl->dev)) {
if (bl->id >= 0)
ida_simple_remove(&bl_ida, bl->id);
ida_free(&bl_ida, bl->id);
ret = PTR_ERR(bl->dev);
goto fail_alloc;
}
@ -442,7 +443,7 @@ nouveau_backlight_fini(struct drm_connector *connector)
return;
if (bl->id >= 0)
ida_simple_remove(&bl_ida, bl->id);
ida_free(&bl_ida, bl->id);
backlight_device_unregister(bl->dev);
nv_conn->backlight = NULL;

View File

@ -123,7 +123,7 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
mutex_init(&tdev->iommu.mutex);
if (iommu_present(&platform_bus_type)) {
if (device_iommu_mapped(dev)) {
tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type);
if (!tdev->iommu.domain)
goto error;

View File

@ -38,6 +38,7 @@
#include <drm/drm_scdc_helper.h>
#include <linux/clk.h>
#include <linux/component.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>

View File

@ -528,7 +528,7 @@ int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno)
*seqno = atomic_add_return(1, &dev_priv->marker_seq);
} while (*seqno == 0);
if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE)) {
if (!vmw_has_fences(dev_priv)) {
/*
* Don't request hardware to send a fence. The
@ -675,11 +675,14 @@ int vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv,
*/
bool vmw_cmd_supported(struct vmw_private *vmw)
{
if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS |
SVGA_CAP_CMD_BUFFERS_2)) != 0)
return true;
bool has_cmdbufs =
(vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS |
SVGA_CAP_CMD_BUFFERS_2)) != 0;
if (vmw_is_svga_v3(vmw))
return (has_cmdbufs &&
(vmw->capabilities & SVGA_CAP_GBOBJECTS) != 0);
/*
* We have FIFO cmd's
*/
return vmw->fifo_mem != NULL;
return has_cmdbufs || vmw->fifo_mem != NULL;
}

View File

@ -1679,4 +1679,12 @@ static inline void vmw_irq_status_write(struct vmw_private *vmw,
outl(status, vmw->io_start + SVGA_IRQSTATUS_PORT);
}
static inline bool vmw_has_fences(struct vmw_private *vmw)
{
if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS |
SVGA_CAP_CMD_BUFFERS_2)) != 0)
return true;
return (vmw_fifo_caps(vmw) & SVGA_FIFO_CAP_FENCE) != 0;
}
#endif

View File

@ -483,7 +483,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
static int vmw_fb_kms_framebuffer(struct fb_info *info)
{
struct drm_mode_fb_cmd2 mode_cmd;
struct drm_mode_fb_cmd2 mode_cmd = {0};
struct vmw_fb_par *par = info->par;
struct fb_var_screeninfo *var = &info->var;
struct drm_framebuffer *cur_fb;

View File

@ -82,6 +82,22 @@ fman_from_fence(struct vmw_fence_obj *fence)
return container_of(fence->base.lock, struct vmw_fence_manager, lock);
}
static u32 vmw_fence_goal_read(struct vmw_private *vmw)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
return vmw_read(vmw, SVGA_REG_FENCE_GOAL);
else
return vmw_fifo_mem_read(vmw, SVGA_FIFO_FENCE_GOAL);
}
static void vmw_fence_goal_write(struct vmw_private *vmw, u32 value)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
vmw_write(vmw, SVGA_REG_FENCE_GOAL, value);
else
vmw_fifo_mem_write(vmw, SVGA_FIFO_FENCE_GOAL, value);
}
/*
* Note on fencing subsystem usage of irqs:
* Typically the vmw_fences_update function is called
@ -392,7 +408,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
if (likely(!fman->seqno_valid))
return false;
goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL);
goal_seqno = vmw_fence_goal_read(fman->dev_priv);
if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP))
return false;
@ -400,9 +416,8 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
list_for_each_entry(fence, &fman->fence_list, head) {
if (!list_empty(&fence->seq_passed_actions)) {
fman->seqno_valid = true;
vmw_fifo_mem_write(fman->dev_priv,
SVGA_FIFO_FENCE_GOAL,
fence->base.seqno);
vmw_fence_goal_write(fman->dev_priv,
fence->base.seqno);
break;
}
}
@ -434,13 +449,12 @@ static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence)
if (dma_fence_is_signaled_locked(&fence->base))
return false;
goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL);
goal_seqno = vmw_fence_goal_read(fman->dev_priv);
if (likely(fman->seqno_valid &&
goal_seqno - fence->base.seqno < VMW_FENCE_WRAP))
return false;
vmw_fifo_mem_write(fman->dev_priv, SVGA_FIFO_FENCE_GOAL,
fence->base.seqno);
vmw_fence_goal_write(fman->dev_priv, fence->base.seqno);
fman->seqno_valid = true;
return true;

View File

@ -32,6 +32,14 @@
#define VMW_FENCE_WRAP (1 << 24)
static u32 vmw_irqflag_fence_goal(struct vmw_private *vmw)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
return SVGA_IRQFLAG_REG_FENCE_GOAL;
else
return SVGA_IRQFLAG_FENCE_GOAL;
}
/**
* vmw_thread_fn - Deferred (process context) irq handler
*
@ -96,7 +104,7 @@ static irqreturn_t vmw_irq_handler(int irq, void *arg)
wake_up_all(&dev_priv->fifo_queue);
if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE |
SVGA_IRQFLAG_FENCE_GOAL)) &&
vmw_irqflag_fence_goal(dev_priv))) &&
!test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending))
ret = IRQ_WAKE_THREAD;
@ -137,8 +145,7 @@ bool vmw_seqno_passed(struct vmw_private *dev_priv,
if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))
return true;
if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE) &&
vmw_fifo_idle(dev_priv, seqno))
if (!vmw_has_fences(dev_priv) && vmw_fifo_idle(dev_priv, seqno))
return true;
/**
@ -160,6 +167,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
unsigned long timeout)
{
struct vmw_fifo_state *fifo_state = dev_priv->fifo;
bool fifo_down = false;
uint32_t count = 0;
uint32_t signal_seq;
@ -176,12 +184,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
*/
if (fifo_idle) {
down_read(&fifo_state->rwsem);
if (dev_priv->cman) {
ret = vmw_cmdbuf_idle(dev_priv->cman, interruptible,
10*HZ);
if (ret)
goto out_err;
} else if (fifo_state) {
down_read(&fifo_state->rwsem);
fifo_down = true;
}
}
@ -218,12 +228,12 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
}
}
finish_wait(&dev_priv->fence_queue, &__wait);
if (ret == 0 && fifo_idle)
if (ret == 0 && fifo_idle && fifo_state)
vmw_fence_write(dev_priv, signal_seq);
wake_up_all(&dev_priv->fence_queue);
out_err:
if (fifo_idle)
if (fifo_down)
up_read(&fifo_state->rwsem);
return ret;
@ -266,13 +276,13 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv)
void vmw_goal_waiter_add(struct vmw_private *dev_priv)
{
vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_FENCE_GOAL,
vmw_generic_waiter_add(dev_priv, vmw_irqflag_fence_goal(dev_priv),
&dev_priv->goal_queue_waiters);
}
void vmw_goal_waiter_remove(struct vmw_private *dev_priv)
{
vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_FENCE_GOAL,
vmw_generic_waiter_remove(dev_priv, vmw_irqflag_fence_goal(dev_priv),
&dev_priv->goal_queue_waiters);
}

View File

@ -1344,7 +1344,6 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
mode_cmd,
is_bo_proxy);
/*
* vmw_create_bo_proxy() adds a reference that is no longer
* needed
@ -1385,13 +1384,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
ret = vmw_user_lookup_handle(dev_priv, file_priv,
mode_cmd->handles[0],
&surface, &bo);
if (ret)
if (ret) {
DRM_ERROR("Invalid buffer object handle %u (0x%x).\n",
mode_cmd->handles[0], mode_cmd->handles[0]);
goto err_out;
}
if (!bo &&
!vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) {
DRM_ERROR("Surface size cannot exceed %dx%d",
DRM_ERROR("Surface size cannot exceed %dx%d\n",
dev_priv->texture_max_width,
dev_priv->texture_max_height);
goto err_out;

View File

@ -1434,10 +1434,7 @@ fb_release(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{
struct fb_info * const info = file_fb_info(file);
if (!info)
return -ENODEV;
struct fb_info * const info = file->private_data;
lock_fb_info(info);
if (info->fbops->fb_release)

View File

@ -80,6 +80,10 @@ void framebuffer_release(struct fb_info *info)
{
if (!info)
return;
if (WARN_ON(refcount_read(&info->count)))
return;
kfree(info->apertures);
kfree(info);
}

View File

@ -243,6 +243,10 @@ static void efifb_show_boot_graphics(struct fb_info *info)
static inline void efifb_show_boot_graphics(struct fb_info *info) {}
#endif
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void efifb_destroy(struct fb_info *info)
{
if (efifb_pci_dev)
@ -254,10 +258,13 @@ static void efifb_destroy(struct fb_info *info)
else
memunmap(info->screen_base);
}
if (request_mem_succeeded)
release_mem_region(info->apertures->ranges[0].base,
info->apertures->ranges[0].size);
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
static const struct fb_ops efifb_ops = {
@ -620,9 +627,9 @@ static int efifb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
/* efifb_destroy takes care of info cleanup */
unregister_framebuffer(info);
sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
framebuffer_release(info);
return 0;
}

View File

@ -84,6 +84,10 @@ struct simplefb_par {
static void simplefb_clocks_destroy(struct simplefb_par *par);
static void simplefb_regulators_destroy(struct simplefb_par *par);
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void simplefb_destroy(struct fb_info *info)
{
struct simplefb_par *par = info->par;
@ -94,6 +98,8 @@ static void simplefb_destroy(struct fb_info *info)
if (info->screen_base)
iounmap(info->screen_base);
framebuffer_release(info);
if (mem)
release_mem_region(mem->start, resource_size(mem));
}
@ -545,8 +551,8 @@ static int simplefb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
/* simplefb_destroy takes care of info cleanup */
unregister_framebuffer(info);
framebuffer_release(info);
return 0;
}

View File

@ -179,6 +179,10 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
return err;
}
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void vesafb_destroy(struct fb_info *info)
{
struct vesafb_par *par = info->par;
@ -188,6 +192,8 @@ static void vesafb_destroy(struct fb_info *info)
if (info->screen_base)
iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
framebuffer_release(info);
}
static struct fb_ops vesafb_ops = {
@ -484,10 +490,10 @@ static int vesafb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
/* vesafb_destroy takes care of info cleanup */
unregister_framebuffer(info);
if (((struct vesafb_par *)(info->par))->region)
release_region(0x3c0, 32);
framebuffer_release(info);
return 0;
}