Re: [PATCH 1/2] drm/imx: fix use after free
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
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
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
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