[PATCH v6] drm/ingenic: fix bridge allocation

H. Nikolaus Schaller posted 1 patch 1 month ago
drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 30 ++++++++++++++++++++---
1 file changed, 26 insertions(+), 4 deletions(-)
[PATCH v6] drm/ingenic: fix bridge allocation
Posted by H. Nikolaus Schaller 1 month ago
Bridge allocation API has changed and ingenic/drm was broken
leading to

[   54.997593] dw-hdmi-ingenic 10180000.hdmi: Detected HDMI \X controller v1.31a with HDCP (DWC HDMI 3D TX PHY)
[   55.491338] dw-hdmi-ingenic 10180000.hdmi: registered DesignWare HDMI I2C bus driver
[   55.899132] [drm] DRM bridge corrupted or not allocated by devm_drm_bridge_alloc()
[   55.904136] ------------[ cut here ]------------
[   55.908753] WARNING: lib/refcount.c:25 at drm_bridge_get+0x58/0x6c [drm], CPU#0: kworker/u4:2/36
[   55.917538] refcount_t: addition on 0; use-after-free.
...
[   56.354928] [<c04898b8>] drm_bridge_attach+0x80/0x208 [drm]
...

Fixes: 9347f2fbb0183b0 ("drm/bridge: add warning for bridges using neither devm_drm_bridge_alloc() nor drm_bridge_add()")
Tested-by: Waldemar Brodkorb <wbx@openadk.org> (on CI20 with HDMI)
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
Cc: Waldemar Brodkorb <wbx@openadk.org>
Cc: stable@vger.kernel.org
---

Notes:
    v6: Sashiko found another weakness in the v5 object lifecycle repair attempt...
        https://sashiko.dev/#/patchset/f77ed4e1547e452668a6549e3966471a6b638a6b.1787593585.git.hns@goldelico.com?part=1
    
    v5: fixed a potential issue with directly allocating struct drm_bridge and
        depending on its internal structure. Solve by allocating a stable
        driver-specific container struct ingenic_drm_bridge_alloc that embeds
        a struct drm_bridge.
        Also revisit and adjust object lifecycle by doing drm_bridge_add() before
        drm_bridge_attach().
        Suggested by Sashiko-reviews:
        https://sashiko.dev/#/patchset/1630a544a26fac0b87187885374ebe59fc92df3b.1787575203.git.hns@goldelico.com?part=1
    
    v4: remove setting interlaced mode (would be new feature and not a fix)
        as suggested by paul@crapouillou.net
    
    v3: fixed a malformed diff in v2
    
    v2: removed ib->bridge->ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT
        as suggested by Sashiko-reviews:
        https://sashiko.dev/#/patchset/400ba2fe0d4f76484e929d2efaa32f67a940163a.1787477392.git.hns@goldelico.com?part=1

 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 30 ++++++++++++++++++++---
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 42c86f195c66b3..b3dc28f78f3807 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -122,11 +122,15 @@ struct ingenic_drm {
 
 struct ingenic_drm_bridge {
 	struct drm_encoder encoder;
-	struct drm_bridge bridge, *next_bridge;
+	struct drm_bridge *bridge, *next_bridge;
 
 	struct drm_bus_cfg bus_cfg;
 };
 
+struct ingenic_drm_bridge_alloc {
+	struct drm_bridge bridge;
+};
+
 static inline struct ingenic_drm_bridge *
 to_ingenic_drm_bridge(struct drm_encoder *encoder)
 {
@@ -802,7 +806,7 @@ static int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
 	struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(encoder);
 
 	return drm_bridge_attach(encoder, ib->next_bridge,
-				 &ib->bridge, flags);
+				 bridge, flags);
 }
 
 static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,
@@ -1107,6 +1111,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
 	struct clk *parent_clk;
 	struct drm_plane *primary;
 	struct drm_bridge *bridge;
+	struct ingenic_drm_bridge_alloc *bridge_alloc;
 	struct drm_panel *panel;
 	struct drm_connector *connector;
 	struct drm_encoder *encoder;
@@ -1314,10 +1319,27 @@ static int ingenic_drm_bind(struct device *dev, bool has_components)
 
 		drm_encoder_helper_add(encoder, &ingenic_drm_encoder_helper_funcs);
 
-		ib->bridge.funcs = &ingenic_drm_bridge_funcs;
+		bridge_alloc = devm_drm_bridge_alloc(priv->dev,
+						     struct ingenic_drm_bridge_alloc,
+						     bridge,
+						     &ingenic_drm_bridge_funcs);
+		if (IS_ERR(bridge_alloc)) {
+			ret = PTR_ERR(bridge_alloc);
+			goto err_drvdata;
+		}
+
+		ib->bridge = &bridge_alloc->bridge;
+		ib->bridge->of_node = priv->dev->of_node;
+
 		ib->next_bridge = bridge;
 
-		ret = drm_bridge_attach(encoder, &ib->bridge, NULL,
+		ret = devm_drm_bridge_add(priv->dev, ib->bridge);
+		if (ret) {
+			dev_err(dev, "Failed to register DRM bridge: %d\n", ret);
+			goto err_drvdata;
+		}
+
+		ret = drm_bridge_attach(encoder, ib->bridge, NULL,
 					DRM_BRIDGE_ATTACH_NO_CONNECTOR);
 		if (ret) {
 			dev_err(dev, "Unable to attach bridge\n");
-- 
2.55.0
Re: [PATCH v6] drm/ingenic: fix bridge allocation
Posted by Paul Cercueil 3 weeks, 5 days ago
Hi Nikolaus,

Le lundi 24 août 2026 à 22:43 +0200, H. Nikolaus Schaller a écrit :
> Bridge allocation API has changed and ingenic/drm was broken
> leading to
> 
> [   54.997593] dw-hdmi-ingenic 10180000.hdmi: Detected HDMI \X
> controller v1.31a with HDCP (DWC HDMI 3D TX PHY)
> [   55.491338] dw-hdmi-ingenic 10180000.hdmi: registered DesignWare
> HDMI I2C bus driver
> [   55.899132] [drm] DRM bridge corrupted or not allocated by
> devm_drm_bridge_alloc()
> [   55.904136] ------------[ cut here ]------------
> [   55.908753] WARNING: lib/refcount.c:25 at drm_bridge_get+0x58/0x6c
> [drm], CPU#0: kworker/u4:2/36
> [   55.917538] refcount_t: addition on 0; use-after-free.
> ...
> [   56.354928] [<c04898b8>] drm_bridge_attach+0x80/0x208 [drm]
> ...
> 
> Fixes: 9347f2fbb0183b0 ("drm/bridge: add warning for bridges using
> neither devm_drm_bridge_alloc() nor drm_bridge_add()")
> Tested-by: Waldemar Brodkorb <wbx@openadk.org> (on CI20 with HDMI)
> Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
> Cc: Waldemar Brodkorb <wbx@openadk.org>
> Cc: stable@vger.kernel.org

Acked-by: Paul Cercueil <paul@crapouillou.net>

Cheers,
-Paul

> ---
> 
> Notes:
>     v6: Sashiko found another weakness in the v5 object lifecycle
> repair attempt...
>        
> https://sashiko.dev/#/patchset/f77ed4e1547e452668a6549e3966471a6b638a6b.1787593585.git.hns@goldelico.com?part=1
>     
>     v5: fixed a potential issue with directly allocating struct
> drm_bridge and
>         depending on its internal structure. Solve by allocating a
> stable
>         driver-specific container struct ingenic_drm_bridge_alloc
> that embeds
>         a struct drm_bridge.
>         Also revisit and adjust object lifecycle by doing
> drm_bridge_add() before
>         drm_bridge_attach().
>         Suggested by Sashiko-reviews:
>        
> https://sashiko.dev/#/patchset/1630a544a26fac0b87187885374ebe59fc92df3b.1787575203.git.hns@goldelico.com?part=1
>     
>     v4: remove setting interlaced mode (would be new feature and not
> a fix)
>         as suggested by paul@crapouillou.net
>     
>     v3: fixed a malformed diff in v2
>     
>     v2: removed ib->bridge->ops = DRM_BRIDGE_OP_EDID |
> DRM_BRIDGE_OP_DETECT
>         as suggested by Sashiko-reviews:
>        
> https://sashiko.dev/#/patchset/400ba2fe0d4f76484e929d2efaa32f67a940163a.1787477392.git.hns@goldelico.com?part=1
> 
>  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 30 ++++++++++++++++++++-
> --
>  1 file changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> index 42c86f195c66b3..b3dc28f78f3807 100644
> --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> @@ -122,11 +122,15 @@ struct ingenic_drm {
>  
>  struct ingenic_drm_bridge {
>  	struct drm_encoder encoder;
> -	struct drm_bridge bridge, *next_bridge;
> +	struct drm_bridge *bridge, *next_bridge;
>  
>  	struct drm_bus_cfg bus_cfg;
>  };
>  
> +struct ingenic_drm_bridge_alloc {
> +	struct drm_bridge bridge;
> +};
> +
>  static inline struct ingenic_drm_bridge *
>  to_ingenic_drm_bridge(struct drm_encoder *encoder)
>  {
> @@ -802,7 +806,7 @@ static int ingenic_drm_bridge_attach(struct
> drm_bridge *bridge,
>  	struct ingenic_drm_bridge *ib =
> to_ingenic_drm_bridge(encoder);
>  
>  	return drm_bridge_attach(encoder, ib->next_bridge,
> -				 &ib->bridge, flags);
> +				 bridge, flags);
>  }
>  
>  static int ingenic_drm_bridge_atomic_check(struct drm_bridge
> *bridge,
> @@ -1107,6 +1111,7 @@ static int ingenic_drm_bind(struct device *dev,
> bool has_components)
>  	struct clk *parent_clk;
>  	struct drm_plane *primary;
>  	struct drm_bridge *bridge;
> +	struct ingenic_drm_bridge_alloc *bridge_alloc;
>  	struct drm_panel *panel;
>  	struct drm_connector *connector;
>  	struct drm_encoder *encoder;
> @@ -1314,10 +1319,27 @@ static int ingenic_drm_bind(struct device
> *dev, bool has_components)
>  
>  		drm_encoder_helper_add(encoder,
> &ingenic_drm_encoder_helper_funcs);
>  
> -		ib->bridge.funcs = &ingenic_drm_bridge_funcs;
> +		bridge_alloc = devm_drm_bridge_alloc(priv->dev,
> +						     struct
> ingenic_drm_bridge_alloc,
> +						     bridge,
> +						    
> &ingenic_drm_bridge_funcs);
> +		if (IS_ERR(bridge_alloc)) {
> +			ret = PTR_ERR(bridge_alloc);
> +			goto err_drvdata;
> +		}
> +
> +		ib->bridge = &bridge_alloc->bridge;
> +		ib->bridge->of_node = priv->dev->of_node;
> +
>  		ib->next_bridge = bridge;
>  
> -		ret = drm_bridge_attach(encoder, &ib->bridge, NULL,
> +		ret = devm_drm_bridge_add(priv->dev, ib->bridge);
> +		if (ret) {
> +			dev_err(dev, "Failed to register DRM bridge:
> %d\n", ret);
> +			goto err_drvdata;
> +		}
> +
> +		ret = drm_bridge_attach(encoder, ib->bridge, NULL,
>  					DRM_BRIDGE_ATTACH_NO_CONNECT
> OR);
>  		if (ret) {
>  			dev_err(dev, "Unable to attach bridge\n");