drm-misc-fixes for v7.2-rc4:

- Fix UAF in host1x, amdxdna.
 - Handle invalid firmware log in ivpu.
 - Fix error handling in panthor.
 - Handle NULL pages and backup handles in ttm_pool_backup() correctly.
 - Reject more invalid amdxdna command submissions.
 - Improve unmap and error handling on gpusvm.
 - Fix virtio deadlock and invalid gem detach.
 - Fix wrong read in ivpu.
 - Always synchronize for CPU in begin_cpu_udmabuf.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEuXvWqAysSYEJGuVH/lWMcqZwE8MFAmpZtq8ACgkQ/lWMcqZw
 E8PLWRAAo9O26HSkkkIHK+eTqURIUFvRdJTEsRWkm/dJJQxeQsngyquCrAWR6Djm
 BZmQS+Kk8hLPvvbv7+d8jMYC7Eft2ebufBq1SSndVuzzwQVnptdw6W+SFr+awCf6
 d9Km4TlVtCANTBez3BeisYoguMDq2UK1WZDSm93NRJQe1t4nDvUJ8yyW2jVpquo6
 UdUokuHZ+d8ZeXyFa3JGXe2JDE9kwiFA9C0nPCYTGem75cJ/7QneiMV6WquMQyXQ
 4dYXBiqbm4lWZfi8scZSdDK9CLuscO04Tw1W+V9yDl+r/szXrEy6e9WIFfZI92sD
 x3vV/X/6/EsCaHMyxcmmgHcP0zxpsXKYvz1BuDsPj37vqNmSZG8yQvHbANR5XleD
 jBOTks6Zzx6PLHaSGJX3UpdDIRwX5xHoopJi6Qj/Ohau9Jwr0BHGHsJmXby7lw66
 Pflt2fcQdmGjzvTCpDr1bKQlfr5d7aQqR8rFP0g3RD59BSaYmNejjv1zz991KX0h
 rY7SUY2TyONHQQzJQ7qIj7aU2G6CQxLX9KTiV6/qrkg0rTyIaGXpYcSsYjhnzNX1
 tN97Ln5LIniKaPBhD5i7Zv24rTBN4Oey8EyM6ZTb45bvCR3uyeY7Mc+/5lpCqiRy
 BlalKZLe7aEPNiHqNpFtXgbcJShbTQP+EDpsq7k2JbwIqvsBcaE=
 =LuJL
 -----END PGP SIGNATURE-----

Merge tag 'drm-misc-fixes-2026-07-17' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-fixes

drm-misc-fixes for v7.2-rc4:
- Fix UAF in host1x, amdxdna.
- Handle invalid firmware log in ivpu.
- Fix error handling in panthor.
- Handle NULL pages and backup handles in ttm_pool_backup() correctly.
- Reject more invalid amdxdna command submissions.
- Improve unmap and error handling on gpusvm.
- Fix virtio deadlock and invalid gem detach.
- Fix wrong read in ivpu.
- Always synchronize for CPU in begin_cpu_udmabuf.

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patch.msgid.link/7f10281e-88f5-4be5-8b42-367e7ce7c547@linux.intel.com
This commit is contained in:
Dave Airlie 2026-07-18 07:15:54 +10:00
commit 4f7b10f3a4
12 changed files with 114 additions and 38 deletions

View File

@ -577,6 +577,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
amdxdna_arg_bos_put(job);
amdxdna_gem_put_obj(job->cmd_bo);
dma_fence_put(job->fence);
mmdrop(job->mm);
}
int amdxdna_cmd_submit(struct amdxdna_client *client,
@ -590,6 +591,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
int ret, idx;
XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt);
if (!xdna->dev_info->ops->cmd_submit)
return -EOPNOTSUPP;
job = kzalloc_flex(*job, bos, arg_bo_cnt);
if (!job)
return -ENOMEM;
@ -603,6 +608,16 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
ret = -EINVAL;
goto free_job;
}
} else if (!drv_cmd) {
/*
* Only internal driver commands (drv_cmd != NULL) may omit a
* command BO. A user command submission with the invalid handle
* would leave job->cmd_bo NULL and later fault when the scheduler
* dereferences it in amdxdna_cmd_set_state().
*/
XDNA_DBG(xdna, "Command BO handle required for user submission");
ret = -EINVAL;
goto free_job;
}
ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
@ -628,6 +643,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
job->hwctx = hwctx;
job->mm = current->mm;
mmgrab(job->mm);
job->fence = amdxdna_fence_create(hwctx);
if (!job->fence) {
@ -662,6 +678,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
cmd_put:
amdxdna_gem_put_obj(job->cmd_bo);
free_job:
if (job->mm)
mmdrop(job->mm);
kfree(job);
return ret;
}

View File

@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off
ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
return -EINVAL;
}
if (log->size < log->header_size) {
ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
return -EINVAL;
}
if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
return -EINVAL;

View File

@ -927,7 +927,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev)
static void diagnose_failure_lnl(struct ivpu_device *vdev)
{
u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",

View File

@ -224,21 +224,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
{
struct udmabuf *ubuf = buf->priv;
struct device *dev = ubuf->device->this_device;
int ret = 0;
if (!ubuf->sg) {
ubuf->sg = get_sg_table(dev, buf, direction);
if (IS_ERR(ubuf->sg)) {
int ret;
ret = PTR_ERR(ubuf->sg);
ubuf->sg = NULL;
return ret;
} else {
ubuf->sg_dir = direction;
}
} else {
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
}
return ret;
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
return 0;
}
static int end_cpu_udmabuf(struct dma_buf *buf,

View File

@ -1146,20 +1146,35 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm,
};
bool use_iova = dma_use_iova(&svm_pages->state);
if (use_iova)
dma_iova_destroy(dev, &svm_pages->state,
svm_pages->state_offset,
svm_pages->dma_addr[0].dir, 0);
/*
* IOVA is reserved for the whole range but only the linked
* system pages (state_offset bytes) need unlinking; free the
* entire reservation to avoid leaking the device-page part.
* On the error path state_offset is 0, so just free it.
*/
if (use_iova) {
if (svm_pages->state_offset)
dma_iova_unlink(dev, &svm_pages->state, 0,
svm_pages->state_offset,
svm_pages->dma_addr[0].dir, 0);
dma_iova_free(dev, &svm_pages->state);
}
for (i = 0, j = 0; i < npages; j++) {
struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j];
if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM)
dma_unmap_page(dev,
addr->addr,
PAGE_SIZE << addr->order,
addr->dir);
else if (dpagemap && dpagemap->ops->device_unmap)
if (addr->proto == DRM_INTERCONNECT_SYSTEM) {
/*
* Linked IOVA pages were already torn down by
* the dma_iova_unlink()/dma_iova_free() above;
* only the non-IOVA mappings need unmap here.
*/
if (!use_iova)
dma_unmap_page(dev,
addr->addr,
PAGE_SIZE << addr->order,
addr->dir);
} else if (dpagemap && dpagemap->ops->device_unmap)
dpagemap->ops->device_unmap(dpagemap,
dev, addr);
i += 1 << addr->order;
@ -1486,7 +1501,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
/* Unlock and restart mapping to allocate memory. */
drm_gpusvm_notifier_unlock(gpusvm);
svm_pages->dma_addr =
kvmalloc_objs(*svm_pages->dma_addr, npages);
kvzalloc_objs(*svm_pages->dma_addr, npages);
if (!svm_pages->dma_addr) {
err = -ENOMEM;
goto err_free;
@ -1529,6 +1544,16 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
err = -EAGAIN;
goto err_unmap;
}
/*
* Set the dpagemap as soon as the first
* device page is mapped so the err_unmap path
* can device_unmap() the device mappings that
* have already been created.
*/
drm_pagemap_get(dpagemap);
drm_pagemap_put(svm_pages->dpagemap);
svm_pages->dpagemap = dpagemap;
}
svm_pages->dma_addr[j] =
dpagemap->ops->device_map(dpagemap,
@ -1596,12 +1621,8 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
goto err_unmap;
}
if (pagemap) {
if (pagemap)
flags.has_devmem_pages = true;
drm_pagemap_get(dpagemap);
drm_pagemap_put(svm_pages->dpagemap);
svm_pages->dpagemap = dpagemap;
}
/* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */
WRITE_ONCE(svm_pages->flags.__flags, flags.__flags);

View File

@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev)
return ret;
#ifdef CONFIG_DEBUG_FS
drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
if (ret)
return ret;
INIT_LIST_HEAD(&ptdev->gems.node);
#endif

View File

@ -829,6 +829,7 @@ static int panthor_fw_load(struct panthor_device *ptdev)
}
if (hdr.size > iter.size) {
ret = -EINVAL;
drm_err(&ptdev->base, "Firmware image is truncated\n");
goto out;
}

View File

@ -1051,9 +1051,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
return -EBUSY;
#ifdef CONFIG_X86
/* Anything returned to the system needs to be cached. */
if (tt->caching != ttm_cached)
set_pages_array_wb(tt->pages, tt->num_pages);
/* Anything returned to the system needs to be cached. Walk allocations
* skipping NULL pages and issue set_pages_array_wb() per contiguous run.
*/
if (tt->caching != ttm_cached) {
pgoff_t run_start = 0, run_count = 0;
for (i = 0; i < tt->num_pages; i += num_pages) {
page = tt->pages[i];
if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
if (run_count) {
set_pages_array_wb(&tt->pages[run_start],
run_count);
run_count = 0;
}
num_pages = 1;
continue;
}
num_pages = 1UL << ttm_pool_page_order(pool, page);
if (!run_count)
run_start = i;
run_count += num_pages;
}
if (run_count)
set_pages_array_wb(&tt->pages[run_start], run_count);
}
#endif
if (tt->dma_address || flags->purge) {
@ -1061,7 +1083,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
unsigned int order;
page = tt->pages[i];
if (unlikely(!page)) {
if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
num_pages = 1;
continue;
}
@ -1104,6 +1126,10 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (unlikely(!page))
continue;
/* Already-handled entry from a previous attempt. */
if (unlikely(ttm_backup_page_ptr_is_handle(page)))
continue;
ttm_pool_split_for_swap(pool, page);
shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,

View File

@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
if (!vgdev->has_virgl_3d)
return;
objs = virtio_gpu_array_alloc(1);
if (!objs)
return;
virtio_gpu_array_add_obj(objs, obj);
if (vfpriv->context_created) {
objs = virtio_gpu_array_alloc(1);
if (!objs)
return;
virtio_gpu_array_add_obj(objs, obj);
virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
objs);
virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
objs);
}
virtio_gpu_notify(vgdev);
}

View File

@ -49,7 +49,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
virtio_gpu_cmd_get_edids(vgdev);
virtio_gpu_cmd_get_display_info(vgdev);
virtio_gpu_notify(vgdev);
drm_helper_hpd_irq_event(vgdev->ddev);
wait_event_timeout(vgdev->resp_wq,
!vgdev->display_info_pending,
5 * HZ);
drm_kms_helper_hotplug_event(vgdev->ddev);
}
events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
}

View File

@ -840,9 +840,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
vgdev->display_info_pending = false;
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
if (!drm_helper_hpd_irq_event(vgdev->ddev))
drm_kms_helper_hotplug_event(vgdev->ddev);
}
static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,

View File

@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
if (WARN_ON(!cache))
continue;
mutex_lock(&mapping->cache->lock);
mutex_lock(&cache->lock);
WARN_ON(kref_read(&mapping->ref) != 1);
__host1x_bo_unpin(&mapping->ref);
mutex_unlock(&mapping->cache->lock);
mutex_unlock(&cache->lock);
}
}
EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);