Re: [PATCH 1/2] drm/imx: fix use after free

2020-07-20 Thread Philipp Zabel
On Thu, 2020-06-11 at 14:43 +0200, Marco Felsch wrote:
> From: Philipp Zabel 
> 
> 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: 17b5001b5143 ("imx-drm: convert to componentised device support")
> Signed-off-by: Philipp Zabel 
> [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 

Thank you, applied to imx-drm/next.

regards
Philipp
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH 1/2] drm/imx: fix use after free

2020-06-11 Thread Philipp Zabel
Hi Russell,

On Thu, 2020-06-11 at 14:01 +0100, Russell King - ARM Linux admin wrote:
> On Thu, Jun 11, 2020 at 02:43:31PM +0200, Marco Felsch wrote:
> > From: Philipp Zabel 
> > 
> > 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.
> 
> You need to be extremely careful doing this.  If the allocation is
> in the probe function, it's lifetime is not just until unbind, but
> potentitally to the _next_ bind, unbind, bind, unbind.  In other
> words, it's lifetime is from the point that the component is probed
> to the point that it is later removed.
> 
> If the driver relies on initialisation of that structure, then that
> must be _very_ carefully handled - any state in that structure will
> remain.
> 
> So, you need to think long and hard about changes like this, and do
> a thorough review of the lifetime of every structure member.

Thank you for the warning, I've tried to make sure that no driver relies
on prior initialization by explicitly replacing each
x = devm_kzalloc(dev, sizeof(*x), GFP_KERNEL);
in .bind() with a
memset(x, 0, sizeof(*x));

The patch still requires the lifetime of embedded connector and encoder
structures to end somewhere between .unbind() and the next .bind(), but
that should be guaranteed by calling drm_mode_config_cleanup() after
component_unbind_all().

I'd like to replace this with devm_drm_dev_alloc() afterwards, but doing
this first would allow to fix stable kernels as well.

regards
Philipp
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH 1/2] drm/imx: fix use after free

2020-06-11 Thread Russell King - ARM Linux admin
On Thu, Jun 11, 2020 at 02:43:31PM +0200, Marco Felsch wrote:
> From: Philipp Zabel 
> 
> 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.

You need to be extremely careful doing this.  If the allocation is
in the probe function, it's lifetime is not just until unbind, but
potentitally to the _next_ bind, unbind, bind, unbind.  In other
words, it's lifetime is from the point that the component is probed
to the point that it is later removed.

If the driver relies on initialisation of that structure, then that
must be _very_ carefully handled - any state in that structure will
remain.

So, you need to think long and hard about changes like this, and do
a thorough review of the lifetime of every structure member.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTC for 0.8m (est. 1762m) line in suburbia: sync at 13.1Mbps down 503kbps up
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 1/2] drm/imx: fix use after free

2020-06-11 Thread Marco Felsch
From: Philipp Zabel 

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: 17b5001b5143 ("imx-drm: convert to componentised device support")
Signed-off-by: Philipp Zabel 
[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 
---
Hi,

I've tested this patch on the imx6 based h100 and the imx53 based qsb
evk. My tests cases were:

 - Bind / Unbind driver
 - Correct error handling during bind() calls

Other testers are welcome =)

Regards,
  Marco

 drivers/gpu/drm/imx/dw_hdmi-imx.c  | 15 ++-
 drivers/gpu/drm/imx/imx-drm-core.c |  3 ++-
 drivers/gpu/drm/imx/imx-ldb.c  | 15 ++-
 drivers/gpu/drm/imx/imx-tve.c  | 15 ++-
 drivers/gpu/drm/imx/ipuv3-crtc.c   | 21 ++---
 drivers/gpu/drm/imx/parallel-display.c | 15 ++-
 6 files changed, 52 insertions(+), 32 deletions(-)

diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c 
b/drivers/gpu/drm/imx/dw_hdmi-imx.c
index f22cfbf9353e..2e12a4a3bfa1 100644
--- a/drivers/gpu/drm/imx/dw_hdmi-imx.c
+++ b/drivers/gpu/drm/imx/dw_hdmi-imx.c
@@ -212,9 +212,8 @@ static int dw_hdmi_imx_bind(struct device *dev, struct 
device *master,
if (!pdev->dev.of_node)
return -ENODEV;
 
-   hdmi = devm_kzalloc(>dev, sizeof(*hdmi), GFP_KERNEL);
-   if (!hdmi)
-   return -ENOMEM;
+   hdmi = dev_get_drvdata(dev);
+   memset(hdmi, 0, sizeof(*hdmi));
 
match = of_match_node(dw_hdmi_imx_dt_ids, pdev->dev.of_node);
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, _hdmi_imx_encoder_funcs,
 DRM_MODE_ENCODER_TMDS, NULL);
 
-   platform_set_drvdata(pdev, hdmi);
-
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)
 {
+   struct imx_hdmi *hdmi;
+
+   hdmi = devm_kzalloc(>dev, sizeof(*hdmi), GFP_KERNEL);
+   if (!hdmi)
+   return -ENOMEM;
+
+   platform_set_drvdata(pdev, hdmi);
+
return component_add(>dev, _hdmi_imx_ops);
 }
 
diff --git a/drivers/gpu/drm/imx/imx-drm-core.c 
b/drivers/gpu/drm/imx/imx-drm-core.c
index da87c70e413b..881c36d0f16b 100644
--- a/drivers/gpu/drm/imx/imx-drm-core.c
+++ b/drivers/gpu/drm/imx/imx-drm-core.c
@@ -281,9 +281,10 @@ static void imx_drm_unbind(struct device *dev)
 
drm_kms_helper_poll_fini(drm);
 
+   component_unbind_all(drm->dev, drm);
+
drm_mode_config_cleanup(drm);
 
-   component_unbind_all(drm->dev, drm);
dev_set_drvdata(dev, NULL);
 
drm_dev_put(drm);
diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c
index 4da22a94790c..8e209117b049 100644
--- a/drivers/gpu/drm/imx/imx-ldb.c
+++ b/drivers/gpu/drm/imx/imx-ldb.c
@@ -594,9 +594,8 @@ static int imx_ldb_bind(struct device *dev, struct device 
*master, void *data)
int ret;
int i;
 
-   imx_ldb = devm_kzalloc(dev, sizeof(*imx_ldb), GFP_KERNEL);
-   if (!imx_ldb)
-   return -ENOMEM;
+   imx_ldb = dev_get_drvdata(dev);
+   memset(imx_ldb, 0, sizeof(*imx_ldb));
 
imx_ldb->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
if (IS_ERR(imx_ldb->regmap)) {
@@ -704,8 +703,6 @@ static int imx_ldb_bind(struct device *dev, struct device 
*master, void *data)
}
}
 
-   dev_set_drvdata(dev, imx_ldb);
-
return 0;
 
 free_child:
@@ -737,6 +734,14 @@ static const struct component_ops imx_ldb_ops = {
 
 static int imx_ldb_probe(struct platform_device *pdev)
 {
+   struct imx_ldb *imx_ldb;
+
+   imx_ldb = devm_kzalloc(>dev, sizeof(*imx_ldb), GFP_KERNEL);
+   if (!imx_ldb)
+   return -ENOMEM;
+
+   platform_set_drvdata(pdev, imx_ldb);
+
return component_add(>dev, _ldb_ops);
 }
 
diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/imx-tve.c
index