From 2b8f13d3c7e26c46c20d9e367904cf01729c88e6 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Fri, 24 Jul 2026 19:26:20 +0200 Subject: [PATCH 1/7] drm/panthor: skip zero-sized firmware sections panthor_fw_load_section_entry() skips BO creation when the firmware section VA range is empty. If such a section is added to the firmware section list, section->mem is left as NULL. Later reload and unplug paths iterate over all firmware sections and dereference section->mem, which can lead to a NULL pointer dereference. Zero-sized firmware sections are valid, so accept them as no-op entries but skip adding them to the section list. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Liviu Dudau Reviewed-by: Steven Price Link: https://patch.msgid.link/20260724172621.63046-1-osama.abdelkader@gmail.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_fw.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index e45933e333b5..fa88b2f9da76 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -603,6 +603,9 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + if (!section_size) + return 0; + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); From 20697ecb299cd77b4cf8b28f655e56606b0472d8 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Thu, 23 Jul 2026 10:38:06 +0800 Subject: [PATCH 2/7] drm/bridge: ps8640: propagate AUX transfer register errors ps8640_aux_transfer_msg() programs the AUX address registers, starts the AUX transfer, waits for SWAUX_SEND to clear, and reads the AUX status register. Several of those regmap operations have return values, but the function only checks a stale ret after the status read. Propagate failures from the address write, transfer start, completion poll, and status read. This avoids returning a transfer length when the bridge register transaction or AUX completion wait failed. Fixes: 13afcdd7277e ("drm/bridge: parade-ps8640: Add support for AUX channel") Signed-off-by: Pengpeng Hou Reviewed-by: Douglas Anderson Signed-off-by: Douglas Anderson Link: https://patch.msgid.link/20260723103509.2-ps8640-v2-pengpeng@iscas.ac.cn --- drivers/gpu/drm/bridge/parade-ps8640.c | 27 ++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index b93514023baa..39ab94a73c88 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -257,8 +257,14 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, addr_len[PAGE0_SWAUX_LENGTH - base] = (len == 0) ? SWAUX_NO_PAYLOAD : ((len - 1) & SWAUX_LENGTH_MASK); - regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len, - ARRAY_SIZE(addr_len)); + ret = regmap_bulk_write(map, PAGE0_SWAUX_ADDR_7_0, addr_len, + ARRAY_SIZE(addr_len)); + if (ret) { + DRM_DEV_ERROR(dev, + "failed to write AUX address %#x, len %zu: %d\n", + msg->address, len, ret); + return ret; + } if (len && (request == DP_AUX_NATIVE_WRITE || request == DP_AUX_I2C_WRITE)) { @@ -274,13 +280,22 @@ static ssize_t ps8640_aux_transfer_msg(struct drm_dp_aux *aux, } } - regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND); + ret = regmap_write(map, PAGE0_SWAUX_CTRL, SWAUX_SEND); + if (ret) { + DRM_DEV_ERROR(dev, "failed to start AUX transfer: %d\n", ret); + return ret; + } /* Zero delay loop because i2c transactions are slow already */ - regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data, - !(data & SWAUX_SEND), 0, 50 * 1000); + ret = regmap_read_poll_timeout(map, PAGE0_SWAUX_CTRL, data, + !(data & SWAUX_SEND), 0, 50 * 1000); + if (ret) { + DRM_DEV_ERROR(dev, "failed to complete AUX transfer: %d\n", + ret); + return ret; + } - regmap_read(map, PAGE0_SWAUX_STATUS, &data); + ret = regmap_read(map, PAGE0_SWAUX_STATUS, &data); if (ret) { DRM_DEV_ERROR(dev, "failed to read PAGE0_SWAUX_STATUS: %d\n", ret); From 8d51e0fd3e698919d2adeff71936377f0c0d4aa0 Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Thu, 23 Jul 2026 00:42:56 -0700 Subject: [PATCH 3/7] accel/amxdna: Fix page-insertion errors in amdxdna_insert_pages() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two error paths in amdxdna_insert_pages() called vma->vm_ops->close(vma) before returning an error code to the caller. This is incorrect: amdxdna_gem_obj_mmap() registers an HMM interval notifier before calling amdxdna_insert_pages(), and on a hard error it jumps to hmm_unreg to undo that registration. Calling vm_ops->close() manually — which drops the shmem pages_pin_count and the GEM object reference that backs the VMA — before the mmap syscall has even returned causes those resources to be released while the VMA is still alive. The kernel VMA teardown will call vm_ops->close() a second time when the process later unmaps the range, producing a reference count underflow. Replace both hard-error returns with a deferred-fault approach that keeps the VMA alive and retries page insertion through the HMM range-fault path. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260723074256.2435143-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 4628a2787265..aec69d5f463e 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -436,6 +436,23 @@ static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj) amdxdna_gem_destroy_obj(abo); } +static void amdxdna_mark_mapp_invalid(struct amdxdna_gem_obj *abo, + struct vm_area_struct *vma) +{ + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); + struct amdxdna_umap *mapp; + + down_write(&xdna->notifier_lock); + abo->mem.map_invalid = true; + list_for_each_entry(mapp, &abo->mem.umap_list, node) { + if (compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) { + mapp->invalid = true; + break; + } + } + up_write(&xdna->notifier_lock); +} + static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, struct vm_area_struct *vma) { @@ -457,8 +474,7 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, &num_pages); if (ret) { XDNA_ERR(xdna, "Failed insert pages %d", ret); - vma->vm_ops->close(vma); - return ret; + amdxdna_mark_mapp_invalid(abo, vma); } return 0; @@ -478,9 +494,9 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, fault_ret = handle_mm_fault(vma, vma->vm_start + offset, FAULT_FLAG_WRITE, NULL); if (fault_ret & VM_FAULT_ERROR) { - vma->vm_ops->close(vma); XDNA_ERR(xdna, "Fault in page failed"); - return -EFAULT; + amdxdna_mark_mapp_invalid(abo, vma); + break; } offset += PAGE_SIZE; From 4a19f7ab5972ef608b31ae921419bc3e04b3f8ad Mon Sep 17 00:00:00 2001 From: Lizhi Hou Date: Fri, 31 Jul 2026 11:59:55 -0700 Subject: [PATCH 4/7] accel/amdxdna: Fix locally exploitable BUG_ON in amdxdna_insert_pages() In amdxdna_insert_pages(), vm_flags_mod() sets VM_MIXEDMAP and clears VM_PFNMAP. If an unprivileged userspace process mmaps a non-imported GEM object and then calls madvise(MADV_DONTNEED), the PTEs will be successfully cleared because VM_MIXEDMAP allows this (unlike VM_PFNMAP). When userspace subsequently accesses the memory, drm_gem_shmem_fault() handles the page fault and attempts to map the backing shmem page via vmf_insert_pfn() which calls vmf_insert_pfn_prot(). Because the backing shmem page is normal system memory (pfn_valid(pfn) is true) and the VMA now has VM_MIXEDMAP set, won't this predictably trigger the explicit assertion BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn)) Fix by removing the vm_flags_mod() call and replacing the vm_insert_pages() pre-population with the handle_mm_fault() loop that was already used for the import (dma-buf) path. Fixes: e486147c912f ("accel/amdxdna: Add BO import and export") Reviewed-by: Max Zhen Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260731185955.3449311-1-lizhi.hou@amd.com --- drivers/accel/amdxdna/amdxdna_gem.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index aec69d5f463e..1c63eff0a4a8 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -467,25 +467,17 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, XDNA_ERR(xdna, "Failed shmem mmap %d", ret); return ret; } - - /* The buffer is based on memory pages. Fix the flag. */ - vm_flags_mod(vma, VM_MIXEDMAP, VM_PFNMAP); - ret = vm_insert_pages(vma, vma->vm_start, abo->base.pages, - &num_pages); + } else { + vma->vm_private_data = NULL; + vma->vm_ops = NULL; + ret = dma_buf_mmap(abo->dma_buf, vma, 0); if (ret) { - XDNA_ERR(xdna, "Failed insert pages %d", ret); - amdxdna_mark_mapp_invalid(abo, vma); + XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); + return ret; } - return 0; - } - - vma->vm_private_data = NULL; - vma->vm_ops = NULL; - ret = dma_buf_mmap(abo->dma_buf, vma, 0); - if (ret) { - XDNA_ERR(xdna, "Failed to mmap dma buf %d", ret); - return ret; + /* Drop the reference drm_gem_mmap_obj() acquired.*/ + drm_gem_object_put(to_gobj(abo)); } do { @@ -502,9 +494,6 @@ static int amdxdna_insert_pages(struct amdxdna_gem_obj *abo, offset += PAGE_SIZE; } while (--num_pages); - /* Drop the reference drm_gem_mmap_obj() acquired.*/ - drm_gem_object_put(to_gobj(abo)); - return 0; } From 4da94744707b27a3ae1197bdd7127da4505dc5b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Tue, 28 Jul 2026 23:09:22 -0300 Subject: [PATCH 5/7] drm/v3d: Serialize the scheduler timeout handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit V3D exposes several independent hardware queues (BIN, RENDER, TFU and CSD) but has only a single, global reset. A timeout on any one queue therefore has to stop, reset and restart the schedulers of every other queue as well. That makes concurrent timeout handlers unsafe. `reset_lock` was never able to make them safe, as a driver-side lock can only cover the driver's &drm_sched_backend_ops.timedout_job callback. The scheduler handles the timed out job and its pending list around that callback, outside of the driver's control, so a global reset triggered by one queue can still interfere with another queue that is in the middle of handling a timeout of its own. Consequently, if a reset happens in the CSD queue while a CL-intensive application is running, the global reset stops and restarts the CL queue's scheduler while that queue is handling a timeout of its own. As drm_sched_stop() and drm_sched_start() subtract and add the credits of every job sitting on the pending list of the scheduler they are called on, and as the CL queue's handler concurrently takes its job off that same list and puts it back, the stop and the start no longer see the same set of jobs. The CL queue is left with more credits in flight than its limit: [ 327.302739] ------------[ cut here ]------------ [ 327.302744] WARNING: CPU: 2 PID: 43 at drivers/gpu/drm/scheduler/sched_main.c:102 drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] [ 327.302884] CPU: 2 UID: 0 PID: 43 Comm: kworker/u16:1 Not tainted 6.18.39-v8-16k+ #3 PREEMPT [ 327.302889] Hardware name: Raspberry Pi 5 Model B Rev 1.0 (DT) [ 327.302893] Workqueue: v3d_bin drm_sched_run_job_work [gpu_sched] [ 327.302984] Call trace: [ 327.302987] drm_sched_run_job_work+0x238/0x4d0 [gpu_sched] (P) [ 327.302997] process_scheduled_works+0x180/0x3d0 [ 327.303010] worker_thread+0x268/0x3e8 [ 327.303016] kthread+0x140/0x250 [ 327.303022] ret_from_fork+0x10/0x20 [ 327.303031] ---[ end trace 0000000000000000 ]--- From that point on, the credit count of the CL queue is broken, causing a complete GPU hang and UI freeze. The DRM scheduler already provides a mechanism to serialize the timeout handlers of different schedulers: an ordered workqueue passed as drm_sched_init()'s @timeout_wq parameter. By default, each scheduler queues its timeout work on the system workqueue, which runs the handlers concurrently. Give all of the queues a shared ordered workqueue instead, as recommended by the DRM scheduler documentation for hardware that has distinct queues but resets globally. Cc: stable@vger.kernel.org # 6.15 Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260728-v3d-order-global-reset-v1-1-e47be838158d@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/v3d/v3d_drv.h | 6 ++++++ drivers/gpu/drm/v3d/v3d_sched.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 423bcfe331a0..8779a42c6508 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -182,6 +182,12 @@ struct v3d_dev { */ struct mutex reset_lock; + /* Ordered workqueue shared by every queue's scheduler timeout work. + * V3D reset is global to all queues, so the timeout handlers must not + * run concurrently. + */ + struct workqueue_struct *reset_wq; + /* Lock taken when creating and pushing the GPU scheduler * jobs, to keep the sched-fence seqnos in order. */ diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index c35f9f70cf8d..63dd95b828c8 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -837,6 +837,7 @@ v3d_queue_sched_init(struct v3d_dev *v3d, const struct drm_sched_backend_ops *op struct drm_sched_init_args args = { .credit_limit = 1, .timeout = msecs_to_jiffies(500), + .timeout_wq = v3d->reset_wq, .dev = v3d->drm.dev, }; @@ -851,9 +852,13 @@ v3d_sched_init(struct v3d_dev *v3d) { int ret; + v3d->reset_wq = alloc_ordered_workqueue("v3d_reset", 0); + if (!v3d->reset_wq) + return -ENOMEM; + ret = v3d_queue_sched_init(v3d, &v3d_bin_sched_ops, V3D_BIN, "v3d_bin"); if (ret) - return ret; + goto fail; ret = v3d_queue_sched_init(v3d, &v3d_render_sched_ops, V3D_RENDER, "v3d_render"); @@ -896,4 +901,6 @@ v3d_sched_fini(struct v3d_dev *v3d) if (v3d->queue[q].sched.ready) drm_sched_fini(&v3d->queue[q].sched); } + + destroy_workqueue(v3d->reset_wq); } From 617bbd08714857c1613d7c550d43a9092ec0fb97 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Mon, 22 Jun 2026 23:57:17 +0200 Subject: [PATCH 6/7] drm/shmem_helper: Check VMA boundaries for PMD mappings In the ->huge_fault handler do not install a PMD huge page mapping if the huge page exceeds the boundaries of the VMA. All other ->huge_fault handlers have similar checks and the resulting mapping will trigger a VM_BUG_ON_VMA() if it ever reaches copy_pmd_range(). Cc: Pedro Demarchi Gomes Cc: Boris Brezillon Cc: stable@vger.kernel.org Fixes: fc3bbf34e643 ("drm/shmem-helper: Fix huge page mapping in fault handler") Signed-off-by: Christian A. Ehrhardt Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260622215718.1532689-1-lk@c--e.de Signed-off-by: Boris Brezillon --- drivers/gpu/drm/drm_gem_shmem_helper.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index c989459eb215..00807039f8d7 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -597,9 +597,13 @@ static vm_fault_t try_insert_pfn(struct vm_fault *vmf, unsigned int order, #ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP } else if (order == PMD_ORDER) { unsigned long paddr = pfn << PAGE_SHIFT; + struct vm_area_struct *vma = vmf->vma; + unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE); + unsigned long end = start + PMD_SIZE; + bool in_range = vma->vm_start <= start && end <= vma->vm_end; bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK); - if (aligned && + if (aligned && in_range && folio_test_pmd_mappable(page_folio(pfn_to_page(pfn)))) { vm_fault_t ret; From e0d2b5902c5c0d3943af95728753aae165ed1346 Mon Sep 17 00:00:00 2001 From: "Christian A. Ehrhardt" Date: Tue, 23 Jun 2026 20:19:42 +0200 Subject: [PATCH 7/7] drm/panthor: Check VMA boundaries for PMD mappings When checking a different patch[1] sashiko AI pointed out that panthor needs the same fix[2]: In the ->huge_fault handler do not install a PMD huge page mapping if the huge page exceeds the boundaries of the VMA. [1] https://lore.kernel.org/lkml/20260622215718.1532689-1-lk@c--e.de/ [2] https://sashiko.dev/#/patchset/20260622215718.1532689-1-lk%40c--e.de Cc: Boris Brezillon Cc: Steven Price Cc: Liviu Dudau Fixes: 68cbf96b1e9b ("drm/panthor: Part ways with drm_gem_shmem_object") Signed-off-by: Christian A. Ehrhardt Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://lore.kernel.org/lkml/20260622215718.1532689-1-lk@c--e.de/ Link: https://patch.msgid.link/20260623181942.1536598-1-lk@c--e.de Signed-off-by: Boris Brezillon --- drivers/gpu/drm/panthor/panthor_gem.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index a1e2eb1ca7bb..54535bae2b0c 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -802,9 +802,13 @@ static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct p } else if (order == PMD_ORDER) { unsigned long pfn = page_to_pfn(page); unsigned long paddr = pfn << PAGE_SHIFT; + struct vm_area_struct *vma = vmf->vma; + unsigned long start = ALIGN_DOWN(vmf->address, PMD_SIZE); + unsigned long end = start + PMD_SIZE; + bool in_range = vma->vm_start <= start && end <= vma->vm_end; bool aligned = (vmf->address & ~PMD_MASK) == (paddr & ~PMD_MASK); - if (aligned && + if (aligned && in_range && folio_test_pmd_mappable(page_folio(page))) { pfn &= PMD_MASK >> PAGE_SHIFT; return vmf_insert_pfn_pmd(vmf, pfn, vmf->flags & FAULT_FLAG_WRITE);