mirror of
https://github.com/torvalds/linux.git
synced 2024-09-20 06:53:04 +00:00
drm/stm: Avoid use-after-free issues with crtc and plane
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@ispras.ru> Acked-by: Raphaël Gallais-Pou <raphael.gallais-pou@foss.st.com> Link: https://patchwork.freedesktop.org/patch/msgid/20240216125040.8968-1-e.orlova@ispras.ru Signed-off-by: Raphael Gallais-Pou <raphael.gallais-pou@foss.st.com>
This commit is contained in:
parent
fd39730c58
commit
19dd9780b7
|
@ -25,6 +25,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"
|
||||
|
||||
|
@ -75,7 +76,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;
|
||||
|
||||
|
|
|
@ -36,6 +36,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>
|
||||
|
||||
|
@ -1199,7 +1200,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,
|
||||
|
@ -1212,7 +1212,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,
|
||||
|
@ -1545,7 +1544,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,
|
||||
|
@ -1572,7 +1570,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 +
|
||||
|
@ -1615,14 +1612,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) {
|
||||
|
@ -1645,15 +1638,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;
|
||||
|
@ -1679,14 +1663,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);
|
||||
|
@ -1700,9 +1684,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);
|
||||
|
@ -1715,10 +1698,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)
|
||||
|
@ -1778,23 +1757,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);
|
||||
|
||||
|
@ -1964,8 +1939,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);
|
||||
|
@ -2047,7 +2021,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;
|
||||
|
@ -2074,9 +2048,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;
|
||||
|
@ -2084,16 +2055,8 @@ err:
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user