7.3-stable review patch. If anyone has any objections, please let me know. ------------------ From: Yibo Tan commit 20b3b97cf89cb381bf07516090d99399cc9b91fd upstream. The LCD controller IRQ schedules sinfo->task to recover from a FIFO underflow. Probe currently requests the IRQ before initializing the work, so an early interrupt can schedule an uninitialized work item. The probe error path and remove path cancel the work before freeing the IRQ. A FIFO-underflow interrupt can schedule the work again after that cancellation. framebuffer_release() may then free sinfo while the work is still pending. KASAN reported a use-after-free in atmel_lcdfb_task() when an underflow interrupt occurred during removal. With the IRQ freed before the work was cancelled, the same test completed without a kernel diagnostic. Initialize the work before requesting the IRQ. In both cleanup paths, free the IRQ before cancelling the work so that it cannot be scheduled again after cancellation. Fixes: d22579b83735 ("atmel_lcdfb: FIFO underflow management") Cc: stable@vger.kernel.org Assisted-by: Codex:GPT-5 Signed-off-by: Yibo Tan Signed-off-by: Helge Deller Signed-off-by: Greg Kroah-Hartman --- drivers/video/fbdev/atmel_lcdfb.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) --- a/drivers/video/fbdev/atmel_lcdfb.c +++ b/drivers/video/fbdev/atmel_lcdfb.c @@ -1146,6 +1146,9 @@ static int atmel_lcdfb_probe(struct plat /* Initialize PWM for contrast or backlight ("off") */ init_contrast(sinfo); + /* Some LCDC recovery operations require process context. */ + INIT_WORK(&sinfo->task, atmel_lcdfb_task); + /* interrupt */ ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); if (ret) { @@ -1153,10 +1156,6 @@ static int atmel_lcdfb_probe(struct plat goto unmap_mmio; } - /* Some operations on the LCDC might sleep and - * require a preemptible task context */ - INIT_WORK(&sinfo->task, atmel_lcdfb_task); - ret = atmel_lcdfb_init_fbinfo(sinfo); if (ret < 0) { dev_err(dev, "init fbinfo failed: %d\n", ret); @@ -1192,8 +1191,8 @@ reset_drvdata: dev_set_drvdata(dev, NULL); fb_dealloc_cmap(&info->cmap); unregister_irqs: - cancel_work_sync(&sinfo->task); free_irq(sinfo->irq_base, info); + cancel_work_sync(&sinfo->task); unmap_mmio: exit_backlight(sinfo); iounmap(sinfo->mmio); @@ -1230,6 +1229,7 @@ static void atmel_lcdfb_remove(struct pl return; sinfo = info->par; + free_irq(sinfo->irq_base, info); cancel_work_sync(&sinfo->task); exit_backlight(sinfo); atmel_lcdfb_power_control(sinfo, 0); @@ -1238,7 +1238,6 @@ static void atmel_lcdfb_remove(struct pl clk_put(sinfo->lcdc_clk); clk_put(sinfo->bus_clk); fb_dealloc_cmap(&info->cmap); - free_irq(sinfo->irq_base, info); iounmap(sinfo->mmio); release_mem_region(info->fix.mmio_start, info->fix.mmio_len); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) {