This is a note to let you know that I've just added the patch titled drm/stm: Avoid use-after-free issues with crtc and plane to the 6.1-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: drm-stm-avoid-use-after-free-issues-with-crtc-and-pl.patch and it can be found in the queue-6.1 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. commit 9ac01690cd4042b151ce2fe7170e59cf00e8465c Author: Katya Orlova <e.orlova@xxxxxxxxx> Date: Fri Feb 16 15:50:40 2024 +0300 drm/stm: Avoid use-after-free issues with crtc and plane [ Upstream commit 19dd9780b7ac673be95bf6fd6892a184c9db611f ] ltdc_load() calls functions drm_crtc_init_with_planes(), drm_universal_plane_init() and drm_encoder_init(). These functions should not be called with parameters allocated with devm_kzalloc() to avoid use-after-free issues [1]. Use allocations managed by the DRM framework. Found by Linux Verification Center (linuxtesting.org). [1] https://lore.kernel.org/lkml/u366i76e3qhh3ra5oxrtngjtm2u5lterkekcz6y2jkndhuxzli@diujon4h7qwb/ Signed-off-by: Katya Orlova <e.orlova@xxxxxxxxx> Acked-by: Raphaël Gallais-Pou <raphael.gallais-pou@xxxxxxxxxxx> Link: https://patchwork.freedesktop.org/patch/msgid/20240216125040.8968-1-e.orlova@xxxxxxxxx Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@xxxxxxxxxxx> Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 77b1b10456f52..7a3f1b4020292 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -24,6 +24,7 @@ #include <drm/drm_module.h> #include <drm/drm_probe_helper.h> #include <drm/drm_vblank.h> +#include <drm/drm_managed.h> #include "ltdc.h" @@ -74,7 +75,7 @@ static int drv_load(struct drm_device *ddev) DRM_DEBUG("%s\n", __func__); - ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL); + ldev = drmm_kzalloc(ddev, sizeof(*ldev), GFP_KERNEL); if (!ldev) return -ENOMEM; diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index b8c1636168cfa..1d22afcf34ba7 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -37,6 +37,7 @@ #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> #include <drm/drm_vblank.h> +#include <drm/drm_managed.h> #include <video/videomode.h> @@ -1200,7 +1201,6 @@ static void ltdc_crtc_atomic_print_state(struct drm_printer *p, } static const struct drm_crtc_funcs ltdc_crtc_funcs = { - .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, @@ -1213,7 +1213,6 @@ static const struct drm_crtc_funcs ltdc_crtc_funcs = { }; static const struct drm_crtc_funcs ltdc_crtc_with_crc_support_funcs = { - .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, @@ -1546,7 +1545,6 @@ static void ltdc_plane_atomic_print_state(struct drm_printer *p, static const struct drm_plane_funcs ltdc_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = drm_plane_cleanup, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, @@ -1573,7 +1571,6 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, const u64 *modifiers = ltdc_format_modifiers; u32 lofs = index * LAY_OFS; u32 val; - int ret; /* Allocate the biggest size according to supported color formats */ formats = devm_kzalloc(dev, (ldev->caps.pix_fmt_nb + @@ -1616,14 +1613,10 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, } } - plane = devm_kzalloc(dev, sizeof(*plane), GFP_KERNEL); - if (!plane) - return NULL; - - ret = drm_universal_plane_init(ddev, plane, possible_crtcs, - <dc_plane_funcs, formats, nb_fmt, - modifiers, type, NULL); - if (ret < 0) + plane = drmm_universal_plane_alloc(ddev, struct drm_plane, dev, + possible_crtcs, <dc_plane_funcs, formats, + nb_fmt, modifiers, type, NULL); + if (IS_ERR(plane)) return NULL; if (ldev->caps.ycbcr_input) { @@ -1646,15 +1639,6 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, return plane; } -static void ltdc_plane_destroy_all(struct drm_device *ddev) -{ - struct drm_plane *plane, *plane_temp; - - list_for_each_entry_safe(plane, plane_temp, - &ddev->mode_config.plane_list, head) - drm_plane_cleanup(plane); -} - static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) { struct ltdc_device *ldev = ddev->dev_private; @@ -1680,14 +1664,14 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) /* Init CRTC according to its hardware features */ if (ldev->caps.crc) - ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, - <dc_crtc_with_crc_support_funcs, NULL); + ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL, + <dc_crtc_with_crc_support_funcs, NULL); else - ret = drm_crtc_init_with_planes(ddev, crtc, primary, NULL, - <dc_crtc_funcs, NULL); + ret = drmm_crtc_init_with_planes(ddev, crtc, primary, NULL, + <dc_crtc_funcs, NULL); if (ret) { DRM_ERROR("Can not initialize CRTC\n"); - goto cleanup; + return ret; } drm_crtc_helper_add(crtc, <dc_crtc_helper_funcs); @@ -1701,9 +1685,8 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) for (i = 1; i < ldev->caps.nb_layers; i++) { overlay = ltdc_plane_create(ddev, DRM_PLANE_TYPE_OVERLAY, i); if (!overlay) { - ret = -ENOMEM; DRM_ERROR("Can not create overlay plane %d\n", i); - goto cleanup; + return -ENOMEM; } if (ldev->caps.dynamic_zorder) drm_plane_create_zpos_property(overlay, i, 0, ldev->caps.nb_layers - 1); @@ -1716,10 +1699,6 @@ static int ltdc_crtc_init(struct drm_device *ddev, struct drm_crtc *crtc) } return 0; - -cleanup: - ltdc_plane_destroy_all(ddev); - return ret; } static void ltdc_encoder_disable(struct drm_encoder *encoder) @@ -1779,23 +1758,19 @@ static int ltdc_encoder_init(struct drm_device *ddev, struct drm_bridge *bridge) struct drm_encoder *encoder; int ret; - encoder = devm_kzalloc(ddev->dev, sizeof(*encoder), GFP_KERNEL); - if (!encoder) - return -ENOMEM; + encoder = drmm_simple_encoder_alloc(ddev, struct drm_encoder, dev, + DRM_MODE_ENCODER_DPI); + if (IS_ERR(encoder)) + return PTR_ERR(encoder); encoder->possible_crtcs = CRTC_MASK; encoder->possible_clones = 0; /* No cloning support */ - drm_simple_encoder_init(ddev, encoder, DRM_MODE_ENCODER_DPI); - drm_encoder_helper_add(encoder, <dc_encoder_helper_funcs); ret = drm_bridge_attach(encoder, bridge, NULL, 0); - if (ret) { - if (ret != -EPROBE_DEFER) - drm_encoder_cleanup(encoder); + if (ret) return ret; - } DRM_DEBUG_DRIVER("Bridge encoder:%d created\n", encoder->base.id); @@ -1965,8 +1940,7 @@ int ltdc_load(struct drm_device *ddev) goto err; if (panel) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_DPI); + bridge = drmm_panel_bridge_add(ddev, panel); if (IS_ERR(bridge)) { DRM_ERROR("panel-bridge endpoint %d\n", i); ret = PTR_ERR(bridge); @@ -2048,7 +2022,7 @@ int ltdc_load(struct drm_device *ddev) } } - crtc = devm_kzalloc(dev, sizeof(*crtc), GFP_KERNEL); + crtc = drmm_kzalloc(ddev, sizeof(*crtc), GFP_KERNEL); if (!crtc) { DRM_ERROR("Failed to allocate crtc\n"); ret = -ENOMEM; @@ -2075,9 +2049,6 @@ int ltdc_load(struct drm_device *ddev) return 0; err: - for (i = 0; i < nb_endpoints; i++) - drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); - clk_disable_unprepare(ldev->pixel_clk); return ret; @@ -2085,16 +2056,8 @@ int ltdc_load(struct drm_device *ddev) void ltdc_unload(struct drm_device *ddev) { - struct device *dev = ddev->dev; - int nb_endpoints, i; - DRM_DEBUG_DRIVER("\n"); - nb_endpoints = of_graph_get_endpoint_count(dev->of_node); - - for (i = 0; i < nb_endpoints; i++) - drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); - pm_runtime_disable(ddev->dev); }