From nobody Wed Dec 17 09:47:59 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 460CCCDB47E for ; Fri, 20 Oct 2023 10:30:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1376736AbjJTKa2 (ORCPT ); Fri, 20 Oct 2023 06:30:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44640 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1376881AbjJTKaT (ORCPT ); Fri, 20 Oct 2023 06:30:19 -0400 Received: from mail.ispras.ru (mail.ispras.ru [83.149.199.84]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 58D6510E3 for ; Fri, 20 Oct 2023 03:30:14 -0700 (PDT) Received: from lvc-arm12.ispras.local (unknown [83.149.199.88]) by mail.ispras.ru (Postfix) with ESMTPSA id 3E6AE40F1DD9; Fri, 20 Oct 2023 10:30:10 +0000 (UTC) DKIM-Filter: OpenDKIM Filter v2.11.0 mail.ispras.ru 3E6AE40F1DD9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ispras.ru; s=default; t=1697797810; bh=IRtFEArw+NnLYbXcC65wfJLbzVU+ZnVP8oeZ6x7qQ9E=; h=From:To:Cc:Subject:Date:From; b=qFaj/SEY99RErCZiLw8wjcxcv02GiNYSOQZRY1oOceope3TPbPivuqbp0nDlkz/5T 1lqSl6ewAe0iydXQVmNgr/bvudDSdrj4jOazMfPPyD/4Mw4oKeC8ii1n4b3ypYBPuD gzoMSt60a0WIKxJPM01mq568oa39inWyK9Oyfigg= From: Katya Orlova To: Yannick Fertre Cc: Katya Orlova , Raphael Gallais-Pou , Philippe Cornu , David Airlie , Daniel Vetter , Maxime Coquelin , Alexandre Torgue , Philipp Zabel , dri-devel@lists.freedesktop.org, linux-stm32@st-md-mailman.stormreply.com, linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, lvc-project@linuxtesting.org Subject: [PATCH] drm/stm: Avoid use-after-free issues with crtc and plane Date: Fri, 20 Oct 2023 13:29:35 +0300 Message-Id: <20231020102935.27634-1-e.orlova@ispras.ru> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" ltdc_load() calls functions drm_crtc_init_with_planes() and drm_universal_plane_init(). These functions should not be called with parameters allocated with devm_kzalloc() to avoid use-after-free issues [1]. The patch replaces managed resource allocations with regular ones. Found by Linux Verification Center (linuxtesting.org). [1] https://lore.kernel.org/lkml/u366i76e3qhh3ra5oxrtngjtm2u5lterkekcz6y2jkndhu= xzli@diujon4h7qwb/ Signed-off-by: Katya Orlova --- drivers/gpu/drm/stm/drv.c | 11 ++++-- drivers/gpu/drm/stm/ltdc.c | 72 ++++++++++++++++++++++++++------------ 2 files changed, 58 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index cb4404b3ce62..409f26d3e400 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -74,7 +74,7 @@ static int drv_load(struct drm_device *ddev) =20 DRM_DEBUG("%s\n", __func__); =20 - ldev =3D devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL); + ldev =3D kzalloc(sizeof(*ldev), GFP_KERNEL); if (!ldev) return -ENOMEM; =20 @@ -82,7 +82,7 @@ static int drv_load(struct drm_device *ddev) =20 ret =3D drmm_mode_config_init(ddev); if (ret) - return ret; + goto err; =20 /* * set max width and height as default value. @@ -98,7 +98,7 @@ static int drv_load(struct drm_device *ddev) =20 ret =3D ltdc_load(ddev); if (ret) - return ret; + goto err; =20 drm_mode_config_reset(ddev); drm_kms_helper_poll_init(ddev); @@ -106,6 +106,9 @@ static int drv_load(struct drm_device *ddev) platform_set_drvdata(pdev, ddev); =20 return 0; +err: + kfree(ldev); + return ret; } =20 static void drv_unload(struct drm_device *ddev) @@ -114,6 +117,8 @@ static void drv_unload(struct drm_device *ddev) =20 drm_kms_helper_poll_fini(ddev); ltdc_unload(ddev); + kfree(ddev->dev_private); + ddev->dev_private =3D NULL; } =20 static __maybe_unused int drv_suspend(struct device *dev) diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index b8be4c1db423..ec3bc3637a63 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1120,6 +1120,12 @@ static const struct drm_crtc_helper_funcs ltdc_crtc_= helper_funcs =3D { .get_scanout_position =3D ltdc_crtc_get_scanout_position, }; =20 +static void ltdc_crtc_destroy(struct drm_crtc *crtc) +{ + drm_crtc_cleanup(crtc); + kfree(crtc); +} + static int ltdc_crtc_enable_vblank(struct drm_crtc *crtc) { struct ltdc_device *ldev =3D crtc_to_ltdc(crtc); @@ -1200,7 +1206,7 @@ static void ltdc_crtc_atomic_print_state(struct drm_p= rinter *p, } =20 static const struct drm_crtc_funcs ltdc_crtc_funcs =3D { - .destroy =3D drm_crtc_cleanup, + .destroy =3D ltdc_crtc_destroy, .set_config =3D drm_atomic_helper_set_config, .page_flip =3D drm_atomic_helper_page_flip, .reset =3D drm_atomic_helper_crtc_reset, @@ -1213,7 +1219,7 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = =3D { }; =20 static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs =3D { - .destroy =3D drm_crtc_cleanup, + .destroy =3D ltdc_crtc_destroy, .set_config =3D drm_atomic_helper_set_config, .page_flip =3D drm_atomic_helper_page_flip, .reset =3D drm_atomic_helper_crtc_reset, @@ -1543,10 +1549,16 @@ static void ltdc_plane_atomic_print_state(struct dr= m_printer *p, fpsi->counter =3D 0; } =20 +static void ltdc_plane_destroy(struct drm_plane *plane) +{ + drm_plane_cleanup(plane); + kfree(plane); +} + static const struct drm_plane_funcs ltdc_plane_funcs =3D { .update_plane =3D drm_atomic_helper_update_plane, .disable_plane =3D drm_atomic_helper_disable_plane, - .destroy =3D drm_plane_cleanup, + .destroy =3D ltdc_plane_destroy, .reset =3D drm_atomic_helper_plane_reset, .atomic_duplicate_state =3D drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state =3D drm_atomic_helper_plane_destroy_state, @@ -1565,7 +1577,6 @@ static struct drm_plane *ltdc_plane_create(struct drm= _device *ddev, { unsigned long possible_crtcs =3D CRTC_MASK; struct ltdc_device *ldev =3D ddev->dev_private; - struct device *dev =3D ddev->dev; struct drm_plane *plane; unsigned int i, nb_fmt =3D 0; u32 *formats; @@ -1576,7 +1587,7 @@ static struct drm_plane *ltdc_plane_create(struct drm= _device *ddev, int ret; =20 /* Allocate the biggest size according to supported color formats */ - formats =3D devm_kzalloc(dev, (ldev->caps.pix_fmt_nb + + formats =3D kzalloc((ldev->caps.pix_fmt_nb + ARRAY_SIZE(ltdc_drm_fmt_ycbcr_cp) + ARRAY_SIZE(ltdc_drm_fmt_ycbcr_sp) + ARRAY_SIZE(ltdc_drm_fmt_ycbcr_fp)) * @@ -1614,15 +1625,20 @@ static struct drm_plane *ltdc_plane_create(struct d= rm_device *ddev, } } =20 - plane =3D devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL); - if (!plane) + plane =3D kzalloc(sizeof(*plane), GFP_KERNEL); + if (!plane) { + kfree(formats); return NULL; + } =20 ret =3D drm_universal_plane_init(ddev, plane, possible_crtcs, <dc_plane_funcs, formats, nb_fmt, modifiers, type, NULL); - if (ret < 0) + kfree(formats); + if (ret < 0) { + kfree(plane); return NULL; + } =20 if (ldev->caps.ycbcr_input) { if (val & (LXCR_C1R_YIA | LXCR_C1R_YSPA | LXCR_C1R_YFPA)) @@ -1650,7 +1666,7 @@ static void ltdc_plane_destroy_all(struct drm_device = *ddev) =20 list_for_each_entry_safe(plane, plane_temp, &ddev->mode_config.plane_list, head) - drm_plane_cleanup(plane); + ltdc_plane_destroy(plane); } =20 static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) @@ -1936,7 +1952,7 @@ int ltdc_load(struct drm_device *ddev) if (!nb_endpoints) return -ENODEV; =20 - ldev->pixel_clk =3D devm_clk_get(dev, "lcd"); + ldev->pixel_clk =3D clk_get(dev, "lcd"); if (IS_ERR(ldev->pixel_clk)) { if (PTR_ERR(ldev->pixel_clk) !=3D -EPROBE_DEFER) DRM_ERROR("Unable to get lcd clock\n"); @@ -1982,7 +1998,7 @@ int ltdc_load(struct drm_device *ddev) } } =20 - rstc =3D devm_reset_control_get_exclusive(dev, NULL); + rstc =3D reset_control_get_exclusive(dev, NULL); =20 mutex_init(&ldev->err_lock); =20 @@ -1993,25 +2009,25 @@ int ltdc_load(struct drm_device *ddev) } =20 res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); - ldev->regs =3D devm_ioremap_resource(dev, res); + ldev->regs =3D ioremap(res->start, resource_size(res)); if (IS_ERR(ldev->regs)) { DRM_ERROR("Unable to get ltdc registers\n"); ret =3D PTR_ERR(ldev->regs); goto err; } =20 - ldev->regmap =3D devm_regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltd= c_regmap_cfg); + ldev->regmap =3D regmap_init_mmio(&pdev->dev, ldev->regs, &stm32_ltdc_reg= map_cfg); if (IS_ERR(ldev->regmap)) { DRM_ERROR("Unable to regmap ltdc registers\n"); ret =3D PTR_ERR(ldev->regmap); - goto err; + goto err_iounmap; } =20 ret =3D ltdc_get_caps(ddev); if (ret) { DRM_ERROR("hardware identifier (0x%08x) not supported!\n", ldev->caps.hw_version); - goto err; + goto err_regmap_exit; } =20 /* Disable interrupts */ @@ -2034,49 +2050,57 @@ int ltdc_load(struct drm_device *ddev) irq =3D platform_get_irq(pdev, i); if (irq < 0) { ret =3D irq; - goto err; + goto err_regmap_exit; } =20 - ret =3D devm_request_threaded_irq(dev, irq, ltdc_irq, + ret =3D request_threaded_irq(irq, ltdc_irq, ltdc_irq_thread, IRQF_ONESHOT, dev_name(dev), ddev); if (ret) { DRM_ERROR("Failed to register LTDC interrupt\n"); - goto err; + goto err_regmap_exit; } } =20 - crtc =3D devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); + crtc =3D kzalloc(sizeof(*crtc), GFP_KERNEL); if (!crtc) { DRM_ERROR("Failed to allocate crtc\n"); ret =3D -ENOMEM; - goto err; + goto err_regmap_exit; } =20 ret =3D ltdc_crtc_init(ddev, crtc); if (ret) { DRM_ERROR("Failed to init crtc\n"); - goto err; + goto free_crtc; } =20 ret =3D drm_vblank_init(ddev, NB_CRTC); if (ret) { DRM_ERROR("Failed calling drm_vblank_init()\n"); - goto err; + goto free_crtc; } =20 clk_disable_unprepare(ldev->pixel_clk); + clk_put(ldev->pixel_clk); =20 pinctrl_pm_select_sleep_state(ddev->dev); =20 pm_runtime_enable(ddev->dev); =20 return 0; +free_crtc: + kfree(crtc); +err_regmap_exit: + regmap_exit(ldev->regmap); +err_iounmap: + iounmap(ldev->regs); err: for (i =3D 0; i < nb_endpoints; i++) drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); =20 clk_disable_unprepare(ldev->pixel_clk); + clk_put(ldev->pixel_clk); =20 return ret; } @@ -2084,6 +2108,7 @@ int ltdc_load(struct drm_device *ddev) void ltdc_unload(struct drm_device *ddev) { struct device *dev =3D ddev->dev; + struct ltdc_device *ldev =3D ddev->dev_private; int nb_endpoints, i; =20 DRM_DEBUG_DRIVER("\n"); @@ -2094,6 +2119,9 @@ void ltdc_unload(struct drm_device *ddev) drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); =20 pm_runtime_disable(ddev->dev); + + regmap_exit(ldev->regmap); + iounmap(ldev->regs); } =20 MODULE_AUTHOR("Philippe Cornu "); --=20 2.30.2