Skip to content

Commit

Permalink
Merge tag 'drm-fixes-2022-05-14' of git://anongit.freedesktop.org/drm…
Browse files Browse the repository at this point in the history
…/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"
  • Loading branch information
Linus Torvalds committed May 13, 2022
2 parents d928e8f + eb7bac3 commit ec7f496
Show file tree
Hide file tree
Showing 15 changed files with 99 additions and 40 deletions.
8 changes: 4 additions & 4 deletions drivers/dma-buf/dma-buf.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,17 +543,17 @@ 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);

mutex_lock(&db_list.lock);
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:
Expand Down
9 changes: 5 additions & 4 deletions drivers/gpu/drm/nouveau/nouveau_backlight.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion drivers/gpu/drm/nouveau/nvkm/engine/device/tegra.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions drivers/gpu/drm/vc4/vc4_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
13 changes: 8 additions & 5 deletions drivers/gpu/drm/vmwgfx/vmwgfx_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
8 changes: 8 additions & 0 deletions drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 21 additions & 7 deletions drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -392,17 +408,16 @@ 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;

fman->seqno_valid = false;
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;
}
}
Expand Down Expand Up @@ -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;
Expand Down
26 changes: 18 additions & 8 deletions drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;

/**
Expand All @@ -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;
Expand All @@ -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;
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}

Expand Down
8 changes: 5 additions & 3 deletions drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 1 addition & 4 deletions drivers/video/fbdev/core/fbmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions drivers/video/fbdev/core/fbsysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
9 changes: 8 additions & 1 deletion drivers/video/fbdev/efifb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 = {
Expand Down Expand Up @@ -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;
}
Expand Down
8 changes: 7 additions & 1 deletion drivers/video/fbdev/simplefb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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));
}
Expand Down Expand Up @@ -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;
}
Expand Down
Loading

0 comments on commit ec7f496

Please sign in to comment.