drm/imx: fix use after free
[ Upstream commit5.4-rM2-2.2.x-imx-squashedba807c94f6
] Component driver structures allocated with devm_kmalloc() in bind() are freed automatically after unbind(). Since the contained drm structures are accessed afterwards in drm_mode_config_cleanup(), move the allocation into probe() to extend the driver structure's lifetime to the lifetime of the device. This should eventually be changed to use drm resource managed allocations with lifetime of the drm device. We also need to ensure that all componets are available during the unbind() so we need to call component_unbind_all() before we free non-devres resources like planes. Note this patch fixes the the use after free bug but introduces a possible boot loop issue. The issue is triggered if the HDMI support is enabled and a component driver always return -EPROBE_DEFER, see discussion [1] for more details. [1] https://lkml.org/lkml/2020/3/24/1467 Fixes:17b5001b51
("imx-drm: convert to componentised device support") Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> [m.felsch@pengutronix: fix imx_tve_probe()] [m.felsch@pengutronix: resort component_unbind_all()) [m.felsch@pengutronix: adapt commit message] Signed-off-by: Marco Felsch <m.felsch@pengutronix.de> Signed-off-by: Philipp Zabel <p.zabel@pengutronix.de> Signed-off-by: Sasha Levin <sashal@kernel.org>
parent
44ae76d01d
commit
1a27987101
|
@ -212,9 +212,8 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
|
||||||
if (!pdev->dev.of_node)
|
if (!pdev->dev.of_node)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
hdmi = dev_get_drvdata(dev);
|
||||||
if (!hdmi)
|
memset(hdmi, 0, sizeof(*hdmi));
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
|
match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
|
||||||
plat_data = match->data;
|
plat_data = match->data;
|
||||||
|
@ -239,8 +238,6 @@ static int dw_hdmi_imx_bind(struct device *dev, struct device *master,
|
||||||
drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
|
drm_encoder_init(drm, encoder, &dw_hdmi_imx_encoder_funcs,
|
||||||
DRM_MODE_ENCODER_TMDS, NULL);
|
DRM_MODE_ENCODER_TMDS, NULL);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, hdmi);
|
|
||||||
|
|
||||||
hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -270,6 +267,14 @@ static const struct component_ops dw_hdmi_imx_ops = {
|
||||||
|
|
||||||
static int dw_hdmi_imx_probe(struct platform_device *pdev)
|
static int dw_hdmi_imx_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct imx_hdmi *hdmi;
|
||||||
|
|
||||||
|
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
|
||||||
|
if (!hdmi)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, hdmi);
|
||||||
|
|
||||||
return component_add(&pdev->dev, &dw_hdmi_imx_ops);
|
return component_add(&pdev->dev, &dw_hdmi_imx_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -281,9 +281,10 @@ static void imx_drm_unbind(struct device *dev)
|
||||||
|
|
||||||
drm_kms_helper_poll_fini(drm);
|
drm_kms_helper_poll_fini(drm);
|
||||||
|
|
||||||
|
component_unbind_all(drm->dev, drm);
|
||||||
|
|
||||||
drm_mode_config_cleanup(drm);
|
drm_mode_config_cleanup(drm);
|
||||||
|
|
||||||
component_unbind_all(drm->dev, drm);
|
|
||||||
dev_set_drvdata(dev, NULL);
|
dev_set_drvdata(dev, NULL);
|
||||||
|
|
||||||
drm_dev_put(drm);
|
drm_dev_put(drm);
|
||||||
|
|
|
@ -593,9 +593,8 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL);
|
imx_ldb = dev_get_drvdata(dev);
|
||||||
if (!imx_ldb)
|
memset(imx_ldb, 0, sizeof(*imx_ldb));
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
|
imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
|
||||||
if (IS_ERR(imx_ldb->regmap)) {
|
if (IS_ERR(imx_ldb->regmap)) {
|
||||||
|
@ -703,8 +702,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_set_drvdata(dev, imx_ldb);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_child:
|
free_child:
|
||||||
|
@ -736,6 +733,14 @@ static const struct component_ops imx_ldb_ops = {
|
||||||
|
|
||||||
static int imx_ldb_probe(struct platform_device *pdev)
|
static int imx_ldb_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct imx_ldb *imx_ldb;
|
||||||
|
|
||||||
|
imx_ldb = devm_kzalloc(&pdev->dev, sizeof(*imx_ldb), GFP_KERNEL);
|
||||||
|
if (!imx_ldb)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, imx_ldb);
|
||||||
|
|
||||||
return component_add(&pdev->dev, &imx_ldb_ops);
|
return component_add(&pdev->dev, &imx_ldb_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -546,9 +546,8 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
|
||||||
int irq;
|
int irq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
tve = devm_kzalloc(dev, sizeof(*tve), GFP_KERNEL);
|
tve = dev_get_drvdata(dev);
|
||||||
if (!tve)
|
memset(tve, 0, sizeof(*tve));
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
tve->dev = dev;
|
tve->dev = dev;
|
||||||
spin_lock_init(&tve->lock);
|
spin_lock_init(&tve->lock);
|
||||||
|
@ -659,8 +658,6 @@ static int imx_tve_bind(struct device *dev, struct device *master, void *data)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dev_set_drvdata(dev, tve);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,6 +677,14 @@ static const struct component_ops imx_tve_ops = {
|
||||||
|
|
||||||
static int imx_tve_probe(struct platform_device *pdev)
|
static int imx_tve_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct imx_tve *tve;
|
||||||
|
|
||||||
|
tve = devm_kzalloc(&pdev->dev, sizeof(*tve), GFP_KERNEL);
|
||||||
|
if (!tve)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, tve);
|
||||||
|
|
||||||
return component_add(&pdev->dev, &imx_tve_ops);
|
return component_add(&pdev->dev, &imx_tve_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -438,21 +438,13 @@ static int ipu_drm_bind(struct device *dev, struct device *master, void *data)
|
||||||
struct ipu_client_platformdata *pdata = dev->platform_data;
|
struct ipu_client_platformdata *pdata = dev->platform_data;
|
||||||
struct drm_device *drm = data;
|
struct drm_device *drm = data;
|
||||||
struct ipu_crtc *ipu_crtc;
|
struct ipu_crtc *ipu_crtc;
|
||||||
int ret;
|
|
||||||
|
|
||||||
ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL);
|
ipu_crtc = dev_get_drvdata(dev);
|
||||||
if (!ipu_crtc)
|
memset(ipu_crtc, 0, sizeof(*ipu_crtc));
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
ipu_crtc->dev = dev;
|
ipu_crtc->dev = dev;
|
||||||
|
|
||||||
ret = ipu_crtc_init(ipu_crtc, pdata, drm);
|
return ipu_crtc_init(ipu_crtc, pdata, drm);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
dev_set_drvdata(dev, ipu_crtc);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ipu_drm_unbind(struct device *dev, struct device *master,
|
static void ipu_drm_unbind(struct device *dev, struct device *master,
|
||||||
|
@ -474,6 +466,7 @@ static const struct component_ops ipu_crtc_ops = {
|
||||||
static int ipu_drm_probe(struct platform_device *pdev)
|
static int ipu_drm_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
struct ipu_crtc *ipu_crtc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!dev->platform_data)
|
if (!dev->platform_data)
|
||||||
|
@ -483,6 +476,12 @@ static int ipu_drm_probe(struct platform_device *pdev)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ipu_crtc = devm_kzalloc(dev, sizeof(*ipu_crtc), GFP_KERNEL);
|
||||||
|
if (!ipu_crtc)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
dev_set_drvdata(dev, ipu_crtc);
|
||||||
|
|
||||||
return component_add(dev, &ipu_crtc_ops);
|
return component_add(dev, &ipu_crtc_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -204,9 +204,8 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
|
||||||
u32 bus_format = 0;
|
u32 bus_format = 0;
|
||||||
const char *fmt;
|
const char *fmt;
|
||||||
|
|
||||||
imxpd = devm_kzalloc(dev, sizeof(*imxpd), GFP_KERNEL);
|
imxpd = dev_get_drvdata(dev);
|
||||||
if (!imxpd)
|
memset(imxpd, 0, sizeof(*imxpd));
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
edidp = of_get_property(np, "edid", &imxpd->edid_len);
|
edidp = of_get_property(np, "edid", &imxpd->edid_len);
|
||||||
if (edidp)
|
if (edidp)
|
||||||
|
@ -236,8 +235,6 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
dev_set_drvdata(dev, imxpd);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,6 +256,14 @@ static const struct component_ops imx_pd_ops = {
|
||||||
|
|
||||||
static int imx_pd_probe(struct platform_device *pdev)
|
static int imx_pd_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct imx_parallel_display *imxpd;
|
||||||
|
|
||||||
|
imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
|
||||||
|
if (!imxpd)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, imxpd);
|
||||||
|
|
||||||
return component_add(&pdev->dev, &imx_pd_ops);
|
return component_add(&pdev->dev, &imx_pd_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue