dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
@ 2025-09-26 14:59 Luca Ceresoli
  2025-09-26 22:39 ` Dmitry Baryshkov
                   ` (4 more replies)
  0 siblings, 5 replies; 8+ messages in thread
From: Luca Ceresoli @ 2025-09-26 14:59 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Dmitry Baryshkov
  Cc: Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel, Luca Ceresoli

drm_bridge_connector_init() takes eight pointers to various bridges, some
of which can be identical, and stores them in pointers inside struct
drm_bridge_connector. Get a reference to each of the taken bridges and put
it on cleanup.

This is tricky because the pointers are currently stored directly in the
drm_bridge_connector in the loop, but there is no nice and clean way to put
those pointers on error return paths. To overcome this, store all pointers
in temporary local variables with a cleanup action, and only on success
copy them into struct drm_bridge_connector (getting another ref while
copying).

Additionally four of these pointers (edid, hpd, detect and modes) can be
written in multiple loop iterations, in order to eventually store the last
matching bridge. However, when one of those pointers is overwritten, we
need to put the reference that we got during the previous assignment. Add a
drm_bridge_put() before writing them to handle this.

Finally, there is also a function-local panel_bridge pointer taken inside
the loop and used after the loop. Use a cleanup action as well to ensure it
is put on return.

Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
This series ensures the bridge-connector gets a reference to bridges when
storing a pointer to them, and releases them afterwards.

This is part of the work towards removal of bridges from a still existing
DRM pipeline without use-after-free. The grand plan was discussed in [1].
Here's the work breakdown (➜ marks the current series):

 1. ➜ add refcounting to DRM bridges (struct drm_bridge)
    (based on devm_drm_bridge_alloc() [0])
    A. ✔ add new alloc API and refcounting (v6.16)
    B. ✔ convert all bridge drivers to new API (v6.17)
    C. ✔ kunit tests (v6.17)
    D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
         and warn on old allocation pattern (v6.17)
    E. … add get/put on drm_bridge accessors
       1. ✔ drm_bridge_chain_get_first_bridge() + add a cleanup action
            (drm-misc-next)
       2. ✔ drm_bridge_get_prev_bridge() (drm-misc-next)
       3. ✔ drm_bridge_get_next_bridge() (drm-misc-next)
       4. ✔ drm_for_each_bridge_in_chain() (drm-misc-next)
       5. ➜ drm_bridge_connector_init
       6. protect encoder bridge chain with a mutex
       7. of_drm_find_bridge
       8. drm_of_find_panel_or_bridge, *_of_get_bridge
    F. ➜ debugfs improvements
       1. ✔ add top-level 'bridges' file (v6.16)
       2. ✔ show refcount and list removed bridges (drm-misc-next)
 2. … handle gracefully atomic updates during bridge removal
 3. … DSI host-device driver interaction
 4. removing the need for the "always-disconnected" connector
 5. finish the hotplug bridge work, moving code to the core and potentially
    removing the hotplug-bridge itself (this needs to be clarified as
    points 1-3 are developed)

This was tricky both because there is no central place in
drm_bridge_connector.c to put the references on disposal (handled by patch
1) and because of the complex code flow of drm_bridge_connector_init()
(handled by patch 2).
---
Changes in v2:
- Use drmm_add_action() instead of hacking the .destroy connector func
- Removed patch 1 (where the hacking the .destroy connector func was)
- Link to v1: https://lore.kernel.org/r/20250925-drm-bridge-alloc-getput-bridge-connector-v1-0-f0736e1c73ee@bootlin.com
---
 drivers/gpu/drm/display/drm_bridge_connector.c | 114 +++++++++++++++++--------
 1 file changed, 78 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index a5bdd6c1064399ece6b19560f145b877c9e0680e..7b18be3ff9a32b362468351835bdab43c3f524f1 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
  * Bridge Connector Initialisation
  */
 
+static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
+{
+	struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
+
+	drm_bridge_put(bridge_connector->bridge_edid);
+	drm_bridge_put(bridge_connector->bridge_hpd);
+	drm_bridge_put(bridge_connector->bridge_detect);
+	drm_bridge_put(bridge_connector->bridge_modes);
+	drm_bridge_put(bridge_connector->bridge_hdmi);
+	drm_bridge_put(bridge_connector->bridge_hdmi_audio);
+	drm_bridge_put(bridge_connector->bridge_dp_audio);
+	drm_bridge_put(bridge_connector->bridge_hdmi_cec);
+}
+
 /**
  * drm_bridge_connector_init - Initialise a connector for a chain of bridges
  * @drm: the DRM device
@@ -638,7 +652,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 	struct drm_bridge_connector *bridge_connector;
 	struct drm_connector *connector;
 	struct i2c_adapter *ddc = NULL;
-	struct drm_bridge *panel_bridge = NULL;
+	struct drm_bridge *panel_bridge      __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_edid       __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_hpd        __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_detect     __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_modes      __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_hdmi       __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_hdmi_audio __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_dp_audio   __free(drm_bridge_put) = NULL;
+	struct drm_bridge *bridge_hdmi_cec   __free(drm_bridge_put) = NULL;
 	unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
 	unsigned int max_bpc = 8;
 	bool support_hdcp = false;
@@ -649,6 +671,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 	if (!bridge_connector)
 		return ERR_PTR(-ENOMEM);
 
+	ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
+	if (ret)
+		return ERR_PTR(ret);
+
 	bridge_connector->encoder = encoder;
 
 	/*
@@ -672,22 +698,30 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 		if (!bridge->ycbcr_420_allowed)
 			connector->ycbcr_420_allowed = false;
 
-		if (bridge->ops & DRM_BRIDGE_OP_EDID)
-			bridge_connector->bridge_edid = bridge;
-		if (bridge->ops & DRM_BRIDGE_OP_HPD)
-			bridge_connector->bridge_hpd = bridge;
-		if (bridge->ops & DRM_BRIDGE_OP_DETECT)
-			bridge_connector->bridge_detect = bridge;
-		if (bridge->ops & DRM_BRIDGE_OP_MODES)
-			bridge_connector->bridge_modes = bridge;
+		if (bridge->ops & DRM_BRIDGE_OP_EDID) {
+			drm_bridge_put(bridge_edid);
+			bridge_edid = drm_bridge_get(bridge);
+		}
+		if (bridge->ops & DRM_BRIDGE_OP_HPD) {
+			drm_bridge_put(bridge_hpd);
+			bridge_hpd = drm_bridge_get(bridge);
+		}
+		if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
+			drm_bridge_put(bridge_detect);
+			bridge_detect = drm_bridge_get(bridge);
+		}
+		if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+			drm_bridge_put(bridge_modes);
+			bridge_modes = drm_bridge_get(bridge);
+		}
 		if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
-			if (bridge_connector->bridge_hdmi)
+			if (bridge_hdmi)
 				return ERR_PTR(-EBUSY);
 			if (!bridge->funcs->hdmi_write_infoframe ||
 			    !bridge->funcs->hdmi_clear_infoframe)
 				return ERR_PTR(-EINVAL);
 
-			bridge_connector->bridge_hdmi = bridge;
+			bridge_hdmi = drm_bridge_get(bridge);
 
 			if (bridge->supported_formats)
 				supported_formats = bridge->supported_formats;
@@ -696,10 +730,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 		}
 
 		if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
-			if (bridge_connector->bridge_hdmi_audio)
+			if (bridge_hdmi_audio)
 				return ERR_PTR(-EBUSY);
 
-			if (bridge_connector->bridge_dp_audio)
+			if (bridge_dp_audio)
 				return ERR_PTR(-EBUSY);
 
 			if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -710,14 +744,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			    !bridge->funcs->hdmi_audio_shutdown)
 				return ERR_PTR(-EINVAL);
 
-			bridge_connector->bridge_hdmi_audio = bridge;
+			bridge_hdmi_audio = drm_bridge_get(bridge);
 		}
 
 		if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
-			if (bridge_connector->bridge_dp_audio)
+			if (bridge_dp_audio)
 				return ERR_PTR(-EBUSY);
 
-			if (bridge_connector->bridge_hdmi_audio)
+			if (bridge_hdmi_audio)
 				return ERR_PTR(-EBUSY);
 
 			if (!bridge->hdmi_audio_max_i2s_playback_channels &&
@@ -728,7 +762,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			    !bridge->funcs->dp_audio_shutdown)
 				return ERR_PTR(-EINVAL);
 
-			bridge_connector->bridge_dp_audio = bridge;
+			bridge_dp_audio = drm_bridge_get(bridge);
 		}
 
 		if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
@@ -739,10 +773,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 		}
 
 		if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
-			if (bridge_connector->bridge_hdmi_cec)
+			if (bridge_hdmi_cec)
 				return ERR_PTR(-EBUSY);
 
-			bridge_connector->bridge_hdmi_cec = bridge;
+			bridge_hdmi_cec = drm_bridge_get(bridge);
 
 			if (!bridge->funcs->hdmi_cec_enable ||
 			    !bridge->funcs->hdmi_cec_log_addr ||
@@ -762,7 +796,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			ddc = bridge->ddc;
 
 		if (drm_bridge_is_panel(bridge))
-			panel_bridge = bridge;
+			panel_bridge = drm_bridge_get(bridge);
 
 		if (bridge->support_hdcp)
 			support_hdcp = true;
@@ -771,13 +805,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
 		return ERR_PTR(-EINVAL);
 
-	if (bridge_connector->bridge_hdmi) {
+	if (bridge_hdmi) {
 		if (!connector->ycbcr_420_allowed)
 			supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
 
 		ret = drmm_connector_hdmi_init(drm, connector,
-					       bridge_connector->bridge_hdmi->vendor,
-					       bridge_connector->bridge_hdmi->product,
+					       bridge_hdmi->vendor,
+					       bridge_hdmi->product,
 					       &drm_bridge_connector_funcs,
 					       &drm_bridge_connector_hdmi_funcs,
 					       connector_type, ddc,
@@ -793,15 +827,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			return ERR_PTR(ret);
 	}
 
-	if (bridge_connector->bridge_hdmi_audio ||
-	    bridge_connector->bridge_dp_audio) {
+	if (bridge_hdmi_audio || bridge_dp_audio) {
 		struct device *dev;
 		struct drm_bridge *bridge;
 
-		if (bridge_connector->bridge_hdmi_audio)
-			bridge = bridge_connector->bridge_hdmi_audio;
+		if (bridge_hdmi_audio)
+			bridge = bridge_hdmi_audio;
 		else
-			bridge = bridge_connector->bridge_dp_audio;
+			bridge = bridge_dp_audio;
 
 		dev = bridge->hdmi_audio_dev;
 
@@ -815,9 +848,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			return ERR_PTR(ret);
 	}
 
-	if (bridge_connector->bridge_hdmi_cec &&
-	    bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
-		struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+	if (bridge_hdmi_cec &&
+	    bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
+		struct drm_bridge *bridge = bridge_hdmi_cec;
 
 		ret = drmm_connector_hdmi_cec_notifier_register(connector,
 								NULL,
@@ -826,9 +859,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 			return ERR_PTR(ret);
 	}
 
-	if (bridge_connector->bridge_hdmi_cec &&
-	    bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
-		struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
+	if (bridge_hdmi_cec &&
+	    bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
+		struct drm_bridge *bridge = bridge_hdmi_cec;
 
 		ret = drmm_connector_hdmi_cec_register(connector,
 						       &drm_bridge_connector_hdmi_cec_funcs,
@@ -841,9 +874,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 
 	drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
 
-	if (bridge_connector->bridge_hpd)
+	if (bridge_hpd)
 		connector->polled = DRM_CONNECTOR_POLL_HPD;
-	else if (bridge_connector->bridge_detect)
+	else if (bridge_detect)
 		connector->polled = DRM_CONNECTOR_POLL_CONNECT
 				  | DRM_CONNECTOR_POLL_DISCONNECT;
 
@@ -854,6 +887,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 	    IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
 		drm_connector_attach_content_protection_property(connector, true);
 
+	bridge_connector->bridge_edid       = drm_bridge_get(bridge_edid);
+	bridge_connector->bridge_hpd        = drm_bridge_get(bridge_hpd);
+	bridge_connector->bridge_detect     = drm_bridge_get(bridge_detect);
+	bridge_connector->bridge_modes      = drm_bridge_get(bridge_modes);
+	bridge_connector->bridge_hdmi       = drm_bridge_get(bridge_hdmi);
+	bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge_hdmi_audio);
+	bridge_connector->bridge_dp_audio   = drm_bridge_get(bridge_dp_audio);
+	bridge_connector->bridge_hdmi_cec   = drm_bridge_get(bridge_hdmi_cec);
+
 	return connector;
 }
 EXPORT_SYMBOL_GPL(drm_bridge_connector_init);

---
base-commit: 063db451832b8849faf1b0b8404b3a6a39995b29
change-id: 20250925-drm-bridge-alloc-getput-bridge-connector-556f8dc14af4

Best regards,
-- 
Luca Ceresoli <luca.ceresoli@bootlin.com>


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-09-26 14:59 [PATCH v2] drm/display: bridge_connector: get/put the stored bridges Luca Ceresoli
@ 2025-09-26 22:39 ` Dmitry Baryshkov
  2025-09-29  8:37 ` Maxime Ripard
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Dmitry Baryshkov @ 2025-09-26 22:39 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Hui Pu,
	Thomas Petazzoni, dri-devel, linux-kernel

On Fri, Sep 26, 2025 at 04:59:40PM +0200, Luca Ceresoli wrote:
> drm_bridge_connector_init() takes eight pointers to various bridges, some
> of which can be identical, and stores them in pointers inside struct
> drm_bridge_connector. Get a reference to each of the taken bridges and put
> it on cleanup.
> 
> This is tricky because the pointers are currently stored directly in the
> drm_bridge_connector in the loop, but there is no nice and clean way to put
> those pointers on error return paths. To overcome this, store all pointers
> in temporary local variables with a cleanup action, and only on success
> copy them into struct drm_bridge_connector (getting another ref while
> copying).
> 
> Additionally four of these pointers (edid, hpd, detect and modes) can be
> written in multiple loop iterations, in order to eventually store the last
> matching bridge. However, when one of those pointers is overwritten, we
> need to put the reference that we got during the previous assignment. Add a
> drm_bridge_put() before writing them to handle this.
> 
> Finally, there is also a function-local panel_bridge pointer taken inside
> the loop and used after the loop. Use a cleanup action as well to ensure it
> is put on return.
> 
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> This series ensures the bridge-connector gets a reference to bridges when
> storing a pointer to them, and releases them afterwards.
> 
> This is part of the work towards removal of bridges from a still existing
> DRM pipeline without use-after-free. The grand plan was discussed in [1].
> Here's the work breakdown (➜ marks the current series):
> 
>  1. ➜ add refcounting to DRM bridges (struct drm_bridge)
>     (based on devm_drm_bridge_alloc() [0])
>     A. ✔ add new alloc API and refcounting (v6.16)
>     B. ✔ convert all bridge drivers to new API (v6.17)
>     C. ✔ kunit tests (v6.17)
>     D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
>          and warn on old allocation pattern (v6.17)
>     E. … add get/put on drm_bridge accessors
>        1. ✔ drm_bridge_chain_get_first_bridge() + add a cleanup action
>             (drm-misc-next)
>        2. ✔ drm_bridge_get_prev_bridge() (drm-misc-next)
>        3. ✔ drm_bridge_get_next_bridge() (drm-misc-next)
>        4. ✔ drm_for_each_bridge_in_chain() (drm-misc-next)
>        5. ➜ drm_bridge_connector_init
>        6. protect encoder bridge chain with a mutex
>        7. of_drm_find_bridge
>        8. drm_of_find_panel_or_bridge, *_of_get_bridge
>     F. ➜ debugfs improvements
>        1. ✔ add top-level 'bridges' file (v6.16)
>        2. ✔ show refcount and list removed bridges (drm-misc-next)
>  2. … handle gracefully atomic updates during bridge removal
>  3. … DSI host-device driver interaction
>  4. removing the need for the "always-disconnected" connector
>  5. finish the hotplug bridge work, moving code to the core and potentially
>     removing the hotplug-bridge itself (this needs to be clarified as
>     points 1-3 are developed)
> 
> This was tricky both because there is no central place in
> drm_bridge_connector.c to put the references on disposal (handled by patch
> 1) and because of the complex code flow of drm_bridge_connector_init()
> (handled by patch 2).
> ---
> Changes in v2:
> - Use drmm_add_action() instead of hacking the .destroy connector func
> - Removed patch 1 (where the hacking the .destroy connector func was)
> - Link to v1: https://lore.kernel.org/r/20250925-drm-bridge-alloc-getput-bridge-connector-v1-0-f0736e1c73ee@bootlin.com
> ---
>  drivers/gpu/drm/display/drm_bridge_connector.c | 114 +++++++++++++++++--------
>  1 file changed, 78 insertions(+), 36 deletions(-)
> 

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>


-- 
With best wishes
Dmitry

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-09-26 14:59 [PATCH v2] drm/display: bridge_connector: get/put the stored bridges Luca Ceresoli
  2025-09-26 22:39 ` Dmitry Baryshkov
@ 2025-09-29  8:37 ` Maxime Ripard
  2025-10-03  7:03 ` Luca Ceresoli
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 8+ messages in thread
From: Maxime Ripard @ 2025-09-29  8:37 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: Maarten Lankhorst, Thomas Zimmermann, David Airlie, Simona Vetter,
	Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
	Jonas Karlman, Jernej Skrabec, Dmitry Baryshkov, Hui Pu,
	Thomas Petazzoni, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5041 bytes --]

On Fri, Sep 26, 2025 at 04:59:40PM +0200, Luca Ceresoli wrote:
> drm_bridge_connector_init() takes eight pointers to various bridges, some
> of which can be identical, and stores them in pointers inside struct
> drm_bridge_connector. Get a reference to each of the taken bridges and put
> it on cleanup.
> 
> This is tricky because the pointers are currently stored directly in the
> drm_bridge_connector in the loop, but there is no nice and clean way to put
> those pointers on error return paths. To overcome this, store all pointers
> in temporary local variables with a cleanup action, and only on success
> copy them into struct drm_bridge_connector (getting another ref while
> copying).
> 
> Additionally four of these pointers (edid, hpd, detect and modes) can be
> written in multiple loop iterations, in order to eventually store the last
> matching bridge. However, when one of those pointers is overwritten, we
> need to put the reference that we got during the previous assignment. Add a
> drm_bridge_put() before writing them to handle this.
> 
> Finally, there is also a function-local panel_bridge pointer taken inside
> the loop and used after the loop. Use a cleanup action as well to ensure it
> is put on return.
> 
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> This series ensures the bridge-connector gets a reference to bridges when
> storing a pointer to them, and releases them afterwards.
> 
> This is part of the work towards removal of bridges from a still existing
> DRM pipeline without use-after-free. The grand plan was discussed in [1].
> Here's the work breakdown (➜ marks the current series):
> 
>  1. ➜ add refcounting to DRM bridges (struct drm_bridge)
>     (based on devm_drm_bridge_alloc() [0])
>     A. ✔ add new alloc API and refcounting (v6.16)
>     B. ✔ convert all bridge drivers to new API (v6.17)
>     C. ✔ kunit tests (v6.17)
>     D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
>          and warn on old allocation pattern (v6.17)
>     E. … add get/put on drm_bridge accessors
>        1. ✔ drm_bridge_chain_get_first_bridge() + add a cleanup action
>             (drm-misc-next)
>        2. ✔ drm_bridge_get_prev_bridge() (drm-misc-next)
>        3. ✔ drm_bridge_get_next_bridge() (drm-misc-next)
>        4. ✔ drm_for_each_bridge_in_chain() (drm-misc-next)
>        5. ➜ drm_bridge_connector_init
>        6. protect encoder bridge chain with a mutex
>        7. of_drm_find_bridge
>        8. drm_of_find_panel_or_bridge, *_of_get_bridge
>     F. ➜ debugfs improvements
>        1. ✔ add top-level 'bridges' file (v6.16)
>        2. ✔ show refcount and list removed bridges (drm-misc-next)
>  2. … handle gracefully atomic updates during bridge removal
>  3. … DSI host-device driver interaction
>  4. removing the need for the "always-disconnected" connector
>  5. finish the hotplug bridge work, moving code to the core and potentially
>     removing the hotplug-bridge itself (this needs to be clarified as
>     points 1-3 are developed)
> 
> This was tricky both because there is no central place in
> drm_bridge_connector.c to put the references on disposal (handled by patch
> 1) and because of the complex code flow of drm_bridge_connector_init()
> (handled by patch 2).
> ---
> Changes in v2:
> - Use drmm_add_action() instead of hacking the .destroy connector func
> - Removed patch 1 (where the hacking the .destroy connector func was)
> - Link to v1: https://lore.kernel.org/r/20250925-drm-bridge-alloc-getput-bridge-connector-v1-0-f0736e1c73ee@bootlin.com
> ---
>  drivers/gpu/drm/display/drm_bridge_connector.c | 114 +++++++++++++++++--------
>  1 file changed, 78 insertions(+), 36 deletions(-)
> 
> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> index a5bdd6c1064399ece6b19560f145b877c9e0680e..7b18be3ff9a32b362468351835bdab43c3f524f1 100644
> --- a/drivers/gpu/drm/display/drm_bridge_connector.c
> +++ b/drivers/gpu/drm/display/drm_bridge_connector.c
> @@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
>   * Bridge Connector Initialisation
>   */
>  
> +static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
> +{
> +	struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
> +
> +	drm_bridge_put(bridge_connector->bridge_edid);
> +	drm_bridge_put(bridge_connector->bridge_hpd);
> +	drm_bridge_put(bridge_connector->bridge_detect);
> +	drm_bridge_put(bridge_connector->bridge_modes);
> +	drm_bridge_put(bridge_connector->bridge_hdmi);
> +	drm_bridge_put(bridge_connector->bridge_hdmi_audio);
> +	drm_bridge_put(bridge_connector->bridge_dp_audio);
> +	drm_bridge_put(bridge_connector->bridge_hdmi_cec);
> +}
> +

I'd rather have a drmm_bridge_get helper that register the action
itself, but that can be fixed later on.

Maxime

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-09-26 14:59 [PATCH v2] drm/display: bridge_connector: get/put the stored bridges Luca Ceresoli
  2025-09-26 22:39 ` Dmitry Baryshkov
  2025-09-29  8:37 ` Maxime Ripard
@ 2025-10-03  7:03 ` Luca Ceresoli
       [not found] ` <CGME20251015082254eucas1p23fc961e7a49f4a29ca7a18d3e2817f86@eucas1p2.samsung.com>
  2025-10-28  8:55 ` Geert Uytterhoeven
  4 siblings, 0 replies; 8+ messages in thread
From: Luca Ceresoli @ 2025-10-03  7:03 UTC (permalink / raw)
  To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Dmitry Baryshkov,
	Luca Ceresoli
  Cc: Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel


On Fri, 26 Sep 2025 16:59:40 +0200, Luca Ceresoli wrote:
> drm_bridge_connector_init() takes eight pointers to various bridges, some
> of which can be identical, and stores them in pointers inside struct
> drm_bridge_connector. Get a reference to each of the taken bridges and put
> it on cleanup.
> 
> This is tricky because the pointers are currently stored directly in the
> drm_bridge_connector in the loop, but there is no nice and clean way to put
> those pointers on error return paths. To overcome this, store all pointers
> in temporary local variables with a cleanup action, and only on success
> copy them into struct drm_bridge_connector (getting another ref while
> copying).
> 
> [...]

Applied, thanks!

[1/1] drm/display: bridge_connector: get/put the stored bridges
      commit: 2be300f9a0b6f6b0ae2a90be97e558ec0535be54

Best regards,
-- 
Luca Ceresoli <luca.ceresoli@bootlin.com>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
       [not found] ` <CGME20251015082254eucas1p23fc961e7a49f4a29ca7a18d3e2817f86@eucas1p2.samsung.com>
@ 2025-10-15  8:22   ` Marek Szyprowski
  2025-10-15 20:08     ` Luca Ceresoli
  0 siblings, 1 reply; 8+ messages in thread
From: Marek Szyprowski @ 2025-10-15  8:22 UTC (permalink / raw)
  To: Luca Ceresoli, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
	Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Dmitry Baryshkov
  Cc: Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel

Hi Luca,

On 26.09.2025 16:59, Luca Ceresoli wrote:
> drm_bridge_connector_init() takes eight pointers to various bridges, some
> of which can be identical, and stores them in pointers inside struct
> drm_bridge_connector. Get a reference to each of the taken bridges and put
> it on cleanup.
>
> This is tricky because the pointers are currently stored directly in the
> drm_bridge_connector in the loop, but there is no nice and clean way to put
> those pointers on error return paths. To overcome this, store all pointers
> in temporary local variables with a cleanup action, and only on success
> copy them into struct drm_bridge_connector (getting another ref while
> copying).
>
> Additionally four of these pointers (edid, hpd, detect and modes) can be
> written in multiple loop iterations, in order to eventually store the last
> matching bridge. However, when one of those pointers is overwritten, we
> need to put the reference that we got during the previous assignment. Add a
> drm_bridge_put() before writing them to handle this.
>
> Finally, there is also a function-local panel_bridge pointer taken inside
> the loop and used after the loop. Use a cleanup action as well to ensure it
> is put on return.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

This patch landed recently in linux-next as commit 2be300f9a0b6 
("drm/display: bridge_connector: get/put the stored bridges"). In my 
tests I found that it causes the following NULL pointer dereference on 
DragonBoard410c (arch/arm64/boot/dts/qcom/apq8016-sbc.dts):

--->8---

  msm_mdp 1a01000.display-controller: bound 1c00000.gpu (ops a3xx_ops [msm])
  msm_mdp 1a01000.display-controller: [drm:mdp5_kms_init [msm]] MDP5 
version v1.6
  msm_mdp 1a01000.display-controller: fall back to the other CTL 
category for INTF 1!
  Unable to handle kernel NULL pointer dereference at virtual address 
0000000000000110
  ...
  Internal error: Oops: 0000000096000004 [#1]  SMP
  Modules linked in: qcom_wcnss_pil(+) cpufreq_powersave 
cpufreq_conservative coresight_stm stm_core coresight_replicator 
coresight_cpu_debug coresight_funnel coresight_tmc coresight_cti 
coresight_tpiu adv7511 nfc coresight rfkill msm snd_soc_lpass_apq8016 
snd_soc_lpass_cpu snd_soc_apq8016_sbc snd_soc_msm8916_digital 
snd_soc_msm8916_analog snd_soc_lpass_platform snd_soc_qcom_common 
snd_soc_core qrtr snd_compress snd_pcm_dmaengine qcom_camss 
qcom_spmi_temp_alarm snd_pcm qcom_q6v5_mss qcom_pil_info ubwc_config 
qcom_q6v5 videobuf2_dma_sg llcc_qcom v4l2_fwnode rtc_pm8xxx ocmem 
venus_core v4l2_async qcom_sysmon qcom_common qcom_spmi_vadc drm_gpuvm 
qcom_vadc_common qcom_pon v4l2_mem2mem snd_timer drm_exec 
videobuf2_memops gpu_sched videobuf2_v4l2 snd qcom_glink_smem 
drm_dp_aux_bus videodev soundcore mdt_loader qmi_helpers 
drm_display_helper qnoc_msm8916 videobuf2_common qcom_stats mc qcom_rng 
rpmsg_ctrl rpmsg_char ramoops reed_solomon display_connector socinfo 
rmtfs_mem ax88796b asix usbnet phy_qcom_usb_hs ipv6
  CPU: 2 UID: 0 PID: 42 Comm: kworker/u16:1 Tainted: G W           
6.17.0-rc6+ #16051 PREEMPT
  Tainted: [W]=WARN
  Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT)
  Workqueue: events_unbound deferred_probe_work_func
  pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
  pc : drm_bridge_connector_hdmi_cec_init+0x8/0x38 [drm_display_helper]
  lr : drmm_connector_hdmi_cec_register+0xf8/0x1c8 [drm_display_helper]
  sp : ffff8000843d3560
  x29: ffff8000843d3560 x28: 0000000000000000 x27: 0000000000000000
  x26: ffff000010c14400 x25: ffff00000c034b68 x24: ffff00003b82a020
  x23: ffff00000c030000 x22: ffff0000097bf7d0 x21: ffff0000042e6a60
  x20: ffff80007c162af8 x19: ffff00000c034080 x18: 00000000ffffffff
  x5 : 00000000000007ff x4 : ffff800083cc5c50 x3 : 0000000000000000
  x2 : 0000000000000000 x1 : ffff00000c034080 x0 : 0000000000000000
  Call trace:
   drm_bridge_connector_hdmi_cec_init+0x8/0x38 [drm_display_helper] (P)
   drm_bridge_connector_init+0x6b4/0x6d4 [drm_display_helper]
   msm_dsi_manager_connector_init+0x9c/0xf0 [msm]
   msm_dsi_modeset_init+0x60/0xe8 [msm]
   modeset_init+0x3c4/0x4c0 [msm]
   mdp5_kms_init+0x3cc/0x670 [msm]
   msm_drm_kms_init+0x40/0x33c [msm]
   msm_drm_init+0x1c4/0x284 [msm]
   msm_drm_bind+0x30/0x3c [msm]
   try_to_bring_up_aggregate_device+0x168/0x1d4
   __component_add+0xa8/0x170
   component_add+0x14/0x20
   dsi_dev_attach+0x20/0x2c [msm]
   dsi_host_attach+0x58/0x98 [msm]
   devm_mipi_dsi_attach+0x34/0x90
   adv7533_attach_dsi+0x8c/0x104 [adv7511]
   adv7511_probe+0x764/0x988 [adv7511]
   i2c_device_probe+0x154/0x350
   really_probe+0xbc/0x298
   __driver_probe_device+0x78/0x12c
   driver_probe_device+0xdc/0x164
   __device_attach_driver+0xb8/0x138
   bus_for_each_drv+0x80/0xdc
   __device_attach+0xa8/0x1b0
   device_initial_probe+0x14/0x20
   bus_probe_device+0xb0/0xb4
   deferred_probe_work_func+0x8c/0xc8
   process_one_work+0x208/0x60c
   worker_thread+0x244/0x388
   kthread+0x150/0x228
   ret_from_fork+0x10/0x20
  Code: d50323bf d65f03c0 aa0003e1 f945e400 (f9408802)
  ---[ end trace 0000000000000000 ]---

This can be easily fixed by adding the following check:

diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c 
b/drivers/gpu/drm/display/drm_bridge_connector.c
index 7b18be3ff9a3..222ecbc98155 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -601,7 +601,7 @@ static int drm_bridge_connector_hdmi_cec_init(struct 
drm_connector *connector)

         bridge = bridge_connector->bridge_hdmi_cec;

-       if (!bridge->funcs->hdmi_cec_init)
+       if (!bridge || !bridge->funcs->hdmi_cec_init)
                 return 0;

         return bridge->funcs->hdmi_cec_init(bridge, connector);


However I don't know the internals of the related code to judge that 
such check is the proper way to fix this issue.


> ---
> This series ensures the bridge-connector gets a reference to bridges when
> storing a pointer to them, and releases them afterwards.
>
> This is part of the work towards removal of bridges from a still existing
> DRM pipeline without use-after-free. The grand plan was discussed in [1].
> Here's the work breakdown (➜ marks the current series):
>
>   1. ➜ add refcounting to DRM bridges (struct drm_bridge)
>      (based on devm_drm_bridge_alloc() [0])
>      A. ✔ add new alloc API and refcounting (v6.16)
>      B. ✔ convert all bridge drivers to new API (v6.17)
>      C. ✔ kunit tests (v6.17)
>      D. ✔ add get/put to drm_bridge_add/remove() + attach/detach()
>           and warn on old allocation pattern (v6.17)
>      E. … add get/put on drm_bridge accessors
>         1. ✔ drm_bridge_chain_get_first_bridge() + add a cleanup action
>              (drm-misc-next)
>         2. ✔ drm_bridge_get_prev_bridge() (drm-misc-next)
>         3. ✔ drm_bridge_get_next_bridge() (drm-misc-next)
>         4. ✔ drm_for_each_bridge_in_chain() (drm-misc-next)
>         5. ➜ drm_bridge_connector_init
>         6. protect encoder bridge chain with a mutex
>         7. of_drm_find_bridge
>         8. drm_of_find_panel_or_bridge, *_of_get_bridge
>      F. ➜ debugfs improvements
>         1. ✔ add top-level 'bridges' file (v6.16)
>         2. ✔ show refcount and list removed bridges (drm-misc-next)
>   2. … handle gracefully atomic updates during bridge removal
>   3. … DSI host-device driver interaction
>   4. removing the need for the "always-disconnected" connector
>   5. finish the hotplug bridge work, moving code to the core and potentially
>      removing the hotplug-bridge itself (this needs to be clarified as
>      points 1-3 are developed)
>
> This was tricky both because there is no central place in
> drm_bridge_connector.c to put the references on disposal (handled by patch
> 1) and because of the complex code flow of drm_bridge_connector_init()
> (handled by patch 2).
> ---
> Changes in v2:
> - Use drmm_add_action() instead of hacking the .destroy connector func
> - Removed patch 1 (where the hacking the .destroy connector func was)
> - Link to v1: https://lore.kernel.org/r/20250925-drm-bridge-alloc-getput-bridge-connector-v1-0-f0736e1c73ee@bootlin.com
> ---
>   drivers/gpu/drm/display/drm_bridge_connector.c | 114 +++++++++++++++++--------
>   1 file changed, 78 insertions(+), 36 deletions(-)
>
> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
> index a5bdd6c1064399ece6b19560f145b877c9e0680e..7b18be3ff9a32b362468351835bdab43c3f524f1 100644
> --- a/drivers/gpu/drm/display/drm_bridge_connector.c
> +++ b/drivers/gpu/drm/display/drm_bridge_connector.c
> @@ -618,6 +618,20 @@ static const struct drm_connector_hdmi_cec_funcs drm_bridge_connector_hdmi_cec_f
>    * Bridge Connector Initialisation
>    */
>   
> +static void drm_bridge_connector_put_bridges(struct drm_device *dev, void *data)
> +{
> +	struct drm_bridge_connector *bridge_connector = (struct drm_bridge_connector *)data;
> +
> +	drm_bridge_put(bridge_connector->bridge_edid);
> +	drm_bridge_put(bridge_connector->bridge_hpd);
> +	drm_bridge_put(bridge_connector->bridge_detect);
> +	drm_bridge_put(bridge_connector->bridge_modes);
> +	drm_bridge_put(bridge_connector->bridge_hdmi);
> +	drm_bridge_put(bridge_connector->bridge_hdmi_audio);
> +	drm_bridge_put(bridge_connector->bridge_dp_audio);
> +	drm_bridge_put(bridge_connector->bridge_hdmi_cec);
> +}
> +
>   /**
>    * drm_bridge_connector_init - Initialise a connector for a chain of bridges
>    * @drm: the DRM device
> @@ -638,7 +652,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   	struct drm_bridge_connector *bridge_connector;
>   	struct drm_connector *connector;
>   	struct i2c_adapter *ddc = NULL;
> -	struct drm_bridge *panel_bridge = NULL;
> +	struct drm_bridge *panel_bridge      __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_edid       __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_hpd        __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_detect     __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_modes      __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_hdmi       __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_hdmi_audio __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_dp_audio   __free(drm_bridge_put) = NULL;
> +	struct drm_bridge *bridge_hdmi_cec   __free(drm_bridge_put) = NULL;
>   	unsigned int supported_formats = BIT(HDMI_COLORSPACE_RGB);
>   	unsigned int max_bpc = 8;
>   	bool support_hdcp = false;
> @@ -649,6 +671,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   	if (!bridge_connector)
>   		return ERR_PTR(-ENOMEM);
>   
> +	ret = drmm_add_action(drm, drm_bridge_connector_put_bridges, bridge_connector);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
>   	bridge_connector->encoder = encoder;
>   
>   	/*
> @@ -672,22 +698,30 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   		if (!bridge->ycbcr_420_allowed)
>   			connector->ycbcr_420_allowed = false;
>   
> -		if (bridge->ops & DRM_BRIDGE_OP_EDID)
> -			bridge_connector->bridge_edid = bridge;
> -		if (bridge->ops & DRM_BRIDGE_OP_HPD)
> -			bridge_connector->bridge_hpd = bridge;
> -		if (bridge->ops & DRM_BRIDGE_OP_DETECT)
> -			bridge_connector->bridge_detect = bridge;
> -		if (bridge->ops & DRM_BRIDGE_OP_MODES)
> -			bridge_connector->bridge_modes = bridge;
> +		if (bridge->ops & DRM_BRIDGE_OP_EDID) {
> +			drm_bridge_put(bridge_edid);
> +			bridge_edid = drm_bridge_get(bridge);
> +		}
> +		if (bridge->ops & DRM_BRIDGE_OP_HPD) {
> +			drm_bridge_put(bridge_hpd);
> +			bridge_hpd = drm_bridge_get(bridge);
> +		}
> +		if (bridge->ops & DRM_BRIDGE_OP_DETECT) {
> +			drm_bridge_put(bridge_detect);
> +			bridge_detect = drm_bridge_get(bridge);
> +		}
> +		if (bridge->ops & DRM_BRIDGE_OP_MODES) {
> +			drm_bridge_put(bridge_modes);
> +			bridge_modes = drm_bridge_get(bridge);
> +		}
>   		if (bridge->ops & DRM_BRIDGE_OP_HDMI) {
> -			if (bridge_connector->bridge_hdmi)
> +			if (bridge_hdmi)
>   				return ERR_PTR(-EBUSY);
>   			if (!bridge->funcs->hdmi_write_infoframe ||
>   			    !bridge->funcs->hdmi_clear_infoframe)
>   				return ERR_PTR(-EINVAL);
>   
> -			bridge_connector->bridge_hdmi = bridge;
> +			bridge_hdmi = drm_bridge_get(bridge);
>   
>   			if (bridge->supported_formats)
>   				supported_formats = bridge->supported_formats;
> @@ -696,10 +730,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   		}
>   
>   		if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) {
> -			if (bridge_connector->bridge_hdmi_audio)
> +			if (bridge_hdmi_audio)
>   				return ERR_PTR(-EBUSY);
>   
> -			if (bridge_connector->bridge_dp_audio)
> +			if (bridge_dp_audio)
>   				return ERR_PTR(-EBUSY);
>   
>   			if (!bridge->hdmi_audio_max_i2s_playback_channels &&
> @@ -710,14 +744,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			    !bridge->funcs->hdmi_audio_shutdown)
>   				return ERR_PTR(-EINVAL);
>   
> -			bridge_connector->bridge_hdmi_audio = bridge;
> +			bridge_hdmi_audio = drm_bridge_get(bridge);
>   		}
>   
>   		if (bridge->ops & DRM_BRIDGE_OP_DP_AUDIO) {
> -			if (bridge_connector->bridge_dp_audio)
> +			if (bridge_dp_audio)
>   				return ERR_PTR(-EBUSY);
>   
> -			if (bridge_connector->bridge_hdmi_audio)
> +			if (bridge_hdmi_audio)
>   				return ERR_PTR(-EBUSY);
>   
>   			if (!bridge->hdmi_audio_max_i2s_playback_channels &&
> @@ -728,7 +762,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			    !bridge->funcs->dp_audio_shutdown)
>   				return ERR_PTR(-EINVAL);
>   
> -			bridge_connector->bridge_dp_audio = bridge;
> +			bridge_dp_audio = drm_bridge_get(bridge);
>   		}
>   
>   		if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
> @@ -739,10 +773,10 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   		}
>   
>   		if (bridge->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
> -			if (bridge_connector->bridge_hdmi_cec)
> +			if (bridge_hdmi_cec)
>   				return ERR_PTR(-EBUSY);
>   
> -			bridge_connector->bridge_hdmi_cec = bridge;
> +			bridge_hdmi_cec = drm_bridge_get(bridge);
>   
>   			if (!bridge->funcs->hdmi_cec_enable ||
>   			    !bridge->funcs->hdmi_cec_log_addr ||
> @@ -762,7 +796,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			ddc = bridge->ddc;
>   
>   		if (drm_bridge_is_panel(bridge))
> -			panel_bridge = bridge;
> +			panel_bridge = drm_bridge_get(bridge);
>   
>   		if (bridge->support_hdcp)
>   			support_hdcp = true;
> @@ -771,13 +805,13 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
>   		return ERR_PTR(-EINVAL);
>   
> -	if (bridge_connector->bridge_hdmi) {
> +	if (bridge_hdmi) {
>   		if (!connector->ycbcr_420_allowed)
>   			supported_formats &= ~BIT(HDMI_COLORSPACE_YUV420);
>   
>   		ret = drmm_connector_hdmi_init(drm, connector,
> -					       bridge_connector->bridge_hdmi->vendor,
> -					       bridge_connector->bridge_hdmi->product,
> +					       bridge_hdmi->vendor,
> +					       bridge_hdmi->product,
>   					       &drm_bridge_connector_funcs,
>   					       &drm_bridge_connector_hdmi_funcs,
>   					       connector_type, ddc,
> @@ -793,15 +827,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			return ERR_PTR(ret);
>   	}
>   
> -	if (bridge_connector->bridge_hdmi_audio ||
> -	    bridge_connector->bridge_dp_audio) {
> +	if (bridge_hdmi_audio || bridge_dp_audio) {
>   		struct device *dev;
>   		struct drm_bridge *bridge;
>   
> -		if (bridge_connector->bridge_hdmi_audio)
> -			bridge = bridge_connector->bridge_hdmi_audio;
> +		if (bridge_hdmi_audio)
> +			bridge = bridge_hdmi_audio;
>   		else
> -			bridge = bridge_connector->bridge_dp_audio;
> +			bridge = bridge_dp_audio;
>   
>   		dev = bridge->hdmi_audio_dev;
>   
> @@ -815,9 +848,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			return ERR_PTR(ret);
>   	}
>   
> -	if (bridge_connector->bridge_hdmi_cec &&
> -	    bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
> -		struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
> +	if (bridge_hdmi_cec &&
> +	    bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_NOTIFIER) {
> +		struct drm_bridge *bridge = bridge_hdmi_cec;
>   
>   		ret = drmm_connector_hdmi_cec_notifier_register(connector,
>   								NULL,
> @@ -826,9 +859,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   			return ERR_PTR(ret);
>   	}
>   
> -	if (bridge_connector->bridge_hdmi_cec &&
> -	    bridge_connector->bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
> -		struct drm_bridge *bridge = bridge_connector->bridge_hdmi_cec;
> +	if (bridge_hdmi_cec &&
> +	    bridge_hdmi_cec->ops & DRM_BRIDGE_OP_HDMI_CEC_ADAPTER) {
> +		struct drm_bridge *bridge = bridge_hdmi_cec;
>   
>   		ret = drmm_connector_hdmi_cec_register(connector,
>   						       &drm_bridge_connector_hdmi_cec_funcs,
> @@ -841,9 +874,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   
>   	drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs);
>   
> -	if (bridge_connector->bridge_hpd)
> +	if (bridge_hpd)
>   		connector->polled = DRM_CONNECTOR_POLL_HPD;
> -	else if (bridge_connector->bridge_detect)
> +	else if (bridge_detect)
>   		connector->polled = DRM_CONNECTOR_POLL_CONNECT
>   				  | DRM_CONNECTOR_POLL_DISCONNECT;
>   
> @@ -854,6 +887,15 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
>   	    IS_ENABLED(CONFIG_DRM_DISPLAY_HDCP_HELPER))
>   		drm_connector_attach_content_protection_property(connector, true);
>   
> +	bridge_connector->bridge_edid       = drm_bridge_get(bridge_edid);
> +	bridge_connector->bridge_hpd        = drm_bridge_get(bridge_hpd);
> +	bridge_connector->bridge_detect     = drm_bridge_get(bridge_detect);
> +	bridge_connector->bridge_modes      = drm_bridge_get(bridge_modes);
> +	bridge_connector->bridge_hdmi       = drm_bridge_get(bridge_hdmi);
> +	bridge_connector->bridge_hdmi_audio = drm_bridge_get(bridge_hdmi_audio);
> +	bridge_connector->bridge_dp_audio   = drm_bridge_get(bridge_dp_audio);
> +	bridge_connector->bridge_hdmi_cec   = drm_bridge_get(bridge_hdmi_cec);
> +
>   	return connector;
>   }
>   EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
>
> ---
> base-commit: 063db451832b8849faf1b0b8404b3a6a39995b29
> change-id: 20250925-drm-bridge-alloc-getput-bridge-connector-556f8dc14af4
>
> Best regards,

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-10-15  8:22   ` Marek Szyprowski
@ 2025-10-15 20:08     ` Luca Ceresoli
  2025-10-17 16:20       ` Luca Ceresoli
  0 siblings, 1 reply; 8+ messages in thread
From: Luca Ceresoli @ 2025-10-15 20:08 UTC (permalink / raw)
  To: Marek Szyprowski, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
	Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Dmitry Baryshkov
  Cc: Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel

Hello Marek,

On Wed Oct 15, 2025 at 10:22 AM CEST, Marek Szyprowski wrote:
> Hi Luca,
>
> On 26.09.2025 16:59, Luca Ceresoli wrote:
>> drm_bridge_connector_init() takes eight pointers to various bridges, some
>> of which can be identical, and stores them in pointers inside struct
>> drm_bridge_connector. Get a reference to each of the taken bridges and put
>> it on cleanup.
>>
>> This is tricky because the pointers are currently stored directly in the
>> drm_bridge_connector in the loop, but there is no nice and clean way to put
>> those pointers on error return paths. To overcome this, store all pointers
>> in temporary local variables with a cleanup action, and only on success
>> copy them into struct drm_bridge_connector (getting another ref while
>> copying).
>>
>> Additionally four of these pointers (edid, hpd, detect and modes) can be
>> written in multiple loop iterations, in order to eventually store the last
>> matching bridge. However, when one of those pointers is overwritten, we
>> need to put the reference that we got during the previous assignment. Add a
>> drm_bridge_put() before writing them to handle this.
>>
>> Finally, there is also a function-local panel_bridge pointer taken inside
>> the loop and used after the loop. Use a cleanup action as well to ensure it
>> is put on return.
>>
>> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>
> This patch landed recently in linux-next as commit 2be300f9a0b6
> ("drm/display: bridge_connector: get/put the stored bridges"). In my
> tests I found that it causes the following NULL pointer dereference on
> DragonBoard410c (arch/arm64/boot/dts/qcom/apq8016-sbc.dts):
>
> --->8---
>
>   msm_mdp 1a01000.display-controller: bound 1c00000.gpu (ops a3xx_ops [msm])
>   msm_mdp 1a01000.display-controller: [drm:mdp5_kms_init [msm]] MDP5
> version v1.6
>   msm_mdp 1a01000.display-controller: fall back to the other CTL
> category for INTF 1!
>   Unable to handle kernel NULL pointer dereference at virtual address
> 0000000000000110
>   ...
>   Internal error: Oops: 0000000096000004 [#1]  SMP
>   Modules linked in: qcom_wcnss_pil(+) cpufreq_powersave
> cpufreq_conservative coresight_stm stm_core coresight_replicator
> coresight_cpu_debug coresight_funnel coresight_tmc coresight_cti
> coresight_tpiu adv7511 nfc coresight rfkill msm snd_soc_lpass_apq8016
> snd_soc_lpass_cpu snd_soc_apq8016_sbc snd_soc_msm8916_digital
> snd_soc_msm8916_analog snd_soc_lpass_platform snd_soc_qcom_common
> snd_soc_core qrtr snd_compress snd_pcm_dmaengine qcom_camss
> qcom_spmi_temp_alarm snd_pcm qcom_q6v5_mss qcom_pil_info ubwc_config
> qcom_q6v5 videobuf2_dma_sg llcc_qcom v4l2_fwnode rtc_pm8xxx ocmem
> venus_core v4l2_async qcom_sysmon qcom_common qcom_spmi_vadc drm_gpuvm
> qcom_vadc_common qcom_pon v4l2_mem2mem snd_timer drm_exec
> videobuf2_memops gpu_sched videobuf2_v4l2 snd qcom_glink_smem
> drm_dp_aux_bus videodev soundcore mdt_loader qmi_helpers
> drm_display_helper qnoc_msm8916 videobuf2_common qcom_stats mc qcom_rng
> rpmsg_ctrl rpmsg_char ramoops reed_solomon display_connector socinfo
> rmtfs_mem ax88796b asix usbnet phy_qcom_usb_hs ipv6
>   CPU: 2 UID: 0 PID: 42 Comm: kworker/u16:1 Tainted: G W
> 6.17.0-rc6+ #16051 PREEMPT
>   Tainted: [W]=WARN
>   Hardware name: Qualcomm Technologies, Inc. APQ 8016 SBC (DT)
>   Workqueue: events_unbound deferred_probe_work_func
>   pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
>   pc : drm_bridge_connector_hdmi_cec_init+0x8/0x38 [drm_display_helper]
>   lr : drmm_connector_hdmi_cec_register+0xf8/0x1c8 [drm_display_helper]
>   sp : ffff8000843d3560
>   x29: ffff8000843d3560 x28: 0000000000000000 x27: 0000000000000000
>   x26: ffff000010c14400 x25: ffff00000c034b68 x24: ffff00003b82a020
>   x23: ffff00000c030000 x22: ffff0000097bf7d0 x21: ffff0000042e6a60
>   x20: ffff80007c162af8 x19: ffff00000c034080 x18: 00000000ffffffff
>   x5 : 00000000000007ff x4 : ffff800083cc5c50 x3 : 0000000000000000
>   x2 : 0000000000000000 x1 : ffff00000c034080 x0 : 0000000000000000
>   Call trace:
>    drm_bridge_connector_hdmi_cec_init+0x8/0x38 [drm_display_helper] (P)
>    drm_bridge_connector_init+0x6b4/0x6d4 [drm_display_helper]
>    msm_dsi_manager_connector_init+0x9c/0xf0 [msm]
>    msm_dsi_modeset_init+0x60/0xe8 [msm]
>    modeset_init+0x3c4/0x4c0 [msm]
>    mdp5_kms_init+0x3cc/0x670 [msm]
>    msm_drm_kms_init+0x40/0x33c [msm]
>    msm_drm_init+0x1c4/0x284 [msm]
>    msm_drm_bind+0x30/0x3c [msm]
>    try_to_bring_up_aggregate_device+0x168/0x1d4
>    __component_add+0xa8/0x170
>    component_add+0x14/0x20
>    dsi_dev_attach+0x20/0x2c [msm]
>    dsi_host_attach+0x58/0x98 [msm]
>    devm_mipi_dsi_attach+0x34/0x90
>    adv7533_attach_dsi+0x8c/0x104 [adv7511]
>    adv7511_probe+0x764/0x988 [adv7511]
>    i2c_device_probe+0x154/0x350
>    really_probe+0xbc/0x298
>    __driver_probe_device+0x78/0x12c
>    driver_probe_device+0xdc/0x164
>    __device_attach_driver+0xb8/0x138
>    bus_for_each_drv+0x80/0xdc
>    __device_attach+0xa8/0x1b0
>    device_initial_probe+0x14/0x20
>    bus_probe_device+0xb0/0xb4
>    deferred_probe_work_func+0x8c/0xc8
>    process_one_work+0x208/0x60c
>    worker_thread+0x244/0x388
>    kthread+0x150/0x228
>    ret_from_fork+0x10/0x20
>   Code: d50323bf d65f03c0 aa0003e1 f945e400 (f9408802)
>   ---[ end trace 0000000000000000 ]---

Thanks for testing and reporting.

I'm afraid I have no hardware where the same bug can be reproduced, but by
code inspection the root cause is clear given the call chain:

  drm_bridge_connector_init() [1]
   -> drmm_connector_hdmi_cec_register() [2]
       -> funcs->init() = drm_bridge_connector_hdmi_cec_init() [3]

[1] used to set bridge_connector->bridge_hdmi_cec before calling [2], now
it does it afterwards. But [3] expects it to be set already.

I have overlooked this when writing the patch. My apologies.

> This can be easily fixed by adding the following check:
>
> diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c
> b/drivers/gpu/drm/display/drm_bridge_connector.c
> index 7b18be3ff9a3..222ecbc98155 100644
> --- a/drivers/gpu/drm/display/drm_bridge_connector.c
> +++ b/drivers/gpu/drm/display/drm_bridge_connector.c
> @@ -601,7 +601,7 @@ static int drm_bridge_connector_hdmi_cec_init(struct
> drm_connector *connector)
>
>          bridge = bridge_connector->bridge_hdmi_cec;
>
> -       if (!bridge->funcs->hdmi_cec_init)
> +       if (!bridge || !bridge->funcs->hdmi_cec_init)
>                  return 0;
>
>          return bridge->funcs->hdmi_cec_init(bridge, connector);
>
>
> However I don't know the internals of the related code to judge that
> such check is the proper way to fix this issue.

I'm afraid this is not a correct solution. When this function is called,
the bridge_hdmi_cec is already known, but (after my patch) not yet set
where it is expected to be. So skipping the bridge->funcs->hdmi_cec_init()
call would make the code behave differently for no good reason.

I'm looking at how to properly fix this bug ASAP.

Best regards,
Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-10-15 20:08     ` Luca Ceresoli
@ 2025-10-17 16:20       ` Luca Ceresoli
  0 siblings, 0 replies; 8+ messages in thread
From: Luca Ceresoli @ 2025-10-17 16:20 UTC (permalink / raw)
  To: Luca Ceresoli, Marek Szyprowski, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, David Airlie, Simona Vetter, Andrzej Hajda,
	Neil Armstrong, Robert Foss, Laurent Pinchart, Jonas Karlman,
	Jernej Skrabec, Dmitry Baryshkov
  Cc: Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel

Hello,

On Wed Oct 15, 2025 at 10:08 PM CEST, Luca Ceresoli wrote:
> Hello Marek,
>
> On Wed Oct 15, 2025 at 10:22 AM CEST, Marek Szyprowski wrote:
>> Hi Luca,
>>
>> On 26.09.2025 16:59, Luca Ceresoli wrote:
>>> drm_bridge_connector_init() takes eight pointers to various bridges, some
>>> of which can be identical, and stores them in pointers inside struct
>>> drm_bridge_connector. Get a reference to each of the taken bridges and put
>>> it on cleanup.
>>>
>>> This is tricky because the pointers are currently stored directly in the
>>> drm_bridge_connector in the loop, but there is no nice and clean way to put
>>> those pointers on error return paths. To overcome this, store all pointers
>>> in temporary local variables with a cleanup action, and only on success
>>> copy them into struct drm_bridge_connector (getting another ref while
>>> copying).
>>>
>>> Additionally four of these pointers (edid, hpd, detect and modes) can be
>>> written in multiple loop iterations, in order to eventually store the last
>>> matching bridge. However, when one of those pointers is overwritten, we
>>> need to put the reference that we got during the previous assignment. Add a
>>> drm_bridge_put() before writing them to handle this.
>>>
>>> Finally, there is also a function-local panel_bridge pointer taken inside
>>> the loop and used after the loop. Use a cleanup action as well to ensure it
>>> is put on return.
>>>
>>> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>>
>> This patch landed recently in linux-next as commit 2be300f9a0b6
>> ("drm/display: bridge_connector: get/put the stored bridges"). In my
>> tests I found that it causes the following NULL pointer dereference on
>> DragonBoard410c (arch/arm64/boot/dts/qcom/apq8016-sbc.dts):

...

> Thanks for testing and reporting.
>
> I'm afraid I have no hardware where the same bug can be reproduced, but by
> code inspection the root cause is clear given the call chain:
>
>   drm_bridge_connector_init() [1]
>    -> drmm_connector_hdmi_cec_register() [2]
>        -> funcs->init() = drm_bridge_connector_hdmi_cec_init() [3]
>
> [1] used to set bridge_connector->bridge_hdmi_cec before calling [2], now
> it does it afterwards. But [3] expects it to be set already.
>
> I have overlooked this when writing the patch. My apologies.

...

> I'm looking at how to properly fix this bug ASAP.

Here it is:
https://lore.kernel.org/lkml/20251017-drm-bridge-alloc-getput-bridge-connector-fix-hdmi_cec-v2-0-667abf6d47c0@bootlin.com/

Luca

--
Luca Ceresoli, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2] drm/display: bridge_connector: get/put the stored bridges
  2025-09-26 14:59 [PATCH v2] drm/display: bridge_connector: get/put the stored bridges Luca Ceresoli
                   ` (3 preceding siblings ...)
       [not found] ` <CGME20251015082254eucas1p23fc961e7a49f4a29ca7a18d3e2817f86@eucas1p2.samsung.com>
@ 2025-10-28  8:55 ` Geert Uytterhoeven
  4 siblings, 0 replies; 8+ messages in thread
From: Geert Uytterhoeven @ 2025-10-28  8:55 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
	Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
	Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Dmitry Baryshkov,
	Hui Pu, Thomas Petazzoni, dri-devel, linux-kernel, Linux-Renesas

Hi Luca,

On Sun, 28 Sept 2025 at 16:25, Luca Ceresoli <luca.ceresoli@bootlin.com> wrote:
> drm_bridge_connector_init() takes eight pointers to various bridges, some
> of which can be identical, and stores them in pointers inside struct
> drm_bridge_connector. Get a reference to each of the taken bridges and put
> it on cleanup.
>
> This is tricky because the pointers are currently stored directly in the
> drm_bridge_connector in the loop, but there is no nice and clean way to put
> those pointers on error return paths. To overcome this, store all pointers
> in temporary local variables with a cleanup action, and only on success
> copy them into struct drm_bridge_connector (getting another ref while
> copying).
>
> Additionally four of these pointers (edid, hpd, detect and modes) can be
> written in multiple loop iterations, in order to eventually store the last
> matching bridge. However, when one of those pointers is overwritten, we
> need to put the reference that we got during the previous assignment. Add a
> drm_bridge_put() before writing them to handle this.
>
> Finally, there is also a function-local panel_bridge pointer taken inside
> the loop and used after the loop. Use a cleanup action as well to ensure it
> is put on return.
>
> Signed-off-by: Luca Ceresoli <luca.ceresoli@bootlin.com>

Thanks for your patch, which is now commit 2be300f9a0b6f6b0
("drm/display: bridge_connector: get/put the stored bridges")
in drm-misc/drm-misc-next.

FTR, this causes the following crash on Koelsch (R-Car M2-W):

    Unable to handle kernel NULL pointer dereference at virtual
address 00000050 when read
    [00000050] *pgd=00000000
    Internal error: Oops: 5 [#1] SMP ARM
    CPU: 1 UID: 0 PID: 12 Comm: kworker/u8:0 Not tainted
6.17.0-rc6-shmobile-01124-g2be300f9a0b6 #2283 NONE
    Hardware name: Generic R-Car Gen2 (Flattened Device Tree)
    Workqueue: events_unbound deferred_probe_work_func
    PC is at drm_bridge_connector_hdmi_cec_init+0x8/0x24
    LR is at drmm_connector_hdmi_cec_register+0x104/0x1a8
    pc : [<c0507240>]    lr : [<c051460c>]    psr: 60000013
    sp : f0849c50  ip : 00000000  fp : 00000008
    r10: c1f865c8  r9 : c1f84820  r8 : c1d32ecc
    r7 : c1e4a980  r6 : c1f94000  r5 : c1d32840  r4 : c0a97930
    r3 : c0507238  r2 : c151e040  r1 : c1d32840  r0 : 00000000
    Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
    Control: 10c5387d  Table: 4000406a  DAC: 00000051
    Register r0 information: NULL pointer
    Register r1 information: slab kmalloc-2k start c1d32800 pointer
offset 64 size 2048
    Register r2 information: slab task_struct start c151e040 pointer
offset 0 size 2240
    Register r3 information: non-slab/vmalloc memory
    Register r4 information: non-slab/vmalloc memory
    Register r5 information: slab kmalloc-2k start c1d32800 pointer
offset 64 size 2048
    Register r6 information: slab kmalloc-1k start c1f94000 pointer
offset 0 size 1024
    Register r7 information: slab kmalloc-64 start c1e4a980 pointer
offset 0 size 64
    Register r8 information: slab kmalloc-2k start c1d32800 pointer
offset 1740 size 2048
    Register r9 information: slab kmalloc-1k start c1f84800 pointer
offset 32 size 1024
    Register r10 information: slab kmalloc-1k start c1f86400 pointer
offset 456 size 1024
    Register r11 information: non-paged memory
    Register r12 information: NULL pointer
    Process kworker/u8:0 (pid: 12, stack limit = 0x(ptrval))
    Stack: (0xf0849c50 to 0xf084a000)
    9c40:                                     00000003 00000011
00000001 00000000
    9c60: 00000049 00000000 00000000 00000000 00000000 00000000
00000000 00000000
    9c80: 00000000 00000000 00000000 00000000 00000000 00000000
00000000 a8bfa8e4
    9ca0: c1d32840 c1d32840 c1f865c8 c1f865c8 00000000 c1d32840
00000000 c0507ba0
    9cc0: c1f84820 00000011 00000001 00000002 00000001 00000008
c04fba80 c204800c
    9ce0: 00000dc0 00000000 c1f865c8 c1f865c8 c1f865c8 00000000
c1f865c8 c1f865c8
    9d00: 00000000 c1f865c8 00000000 a8bfa8e4 c1e42a00 00000000
00000000 c2048000
    9d20: c1f865c8 c1e42a00 c204800c c0c66958 ef7f7b44 c0517ab4
00000000 00000000
    9d40: c2048000 00000000 00000000 c204b000 ef7f0794 00000000
00000000 c0518ab8
    9d60: 00000000 00000000 01ffffff 00000000 c204800c ef7f0214
f0f40000 c204f000
    9d80: 00000000 00000000 ef7f0794 c2081f80 c0c7a60d c052d728
c1588c10 a0000013
    9da0: c2081fc0 c052d780 f0f40000 c2081fc0 00040000 c1588c10
f0f40000 c03f57f4
    9dc0: c0c4fe58 feb00000 c1588c10 c16683c0 00000000 a8bfa8e4
00000000 00000000
    9de0: c2048000 c204800c c1588c00 00000000 c1588c10 00000000
c0fe8e30 c0517818
    9e00: c1588c10 c0fe8670 c0fe8670 00000000 00000005 c140ed0d
61c88647 c052c148
    9e20: 00000000 c1588c10 c0fe8670 c052a118 c1588c10 c0fe8670
f0849ecc c1588c10
    9e40: 00000005 c052a3e8 c0fe8670 c1588c10 c10773d0 c10773d8
f0849ecc c1588c10
    9e60: 00000005 61c88647 c0fe8e30 c052a490 00000001 c0fe8670
f0849ecc c1588c10
    9e80: c102fc00 c052a568 00000000 c14ac400 f0849ecc c052a510
c102fc00 c05287fc
    9ea0: c140ed0d c14ac46c c1594db8 a8bfa8e4 c1588c10 c1588c10
c14ac400 00000001
    9ec0: c1588c54 c0529f80 c1588c10 c1588c10 00000001 a8bfa8e4
c14ac400 c1588c10
    9ee0: c14ac400 c1588c10 00000000 c05290f4 c1588c10 c0fe8e10
c0fe8e68 00000000
    9f00: c102fc00 c0529b3c c1496180 c140ed00 c1406600 c0fe8e2c
c102fc00 c01414ac
    9f20: 00000002 a8bfa8e4 c151e040 c151e040 c1406620 c1406600
c140665c c1496180
    9f40: c1406620 c1406600 c140665c c151e040 c14961ac c1030120
c0f03d00 c014173c
    9f60: 00000000 c151e040 c1496400 c1494140 00000001 00000000
c01415cc c1496180
    9f80: 00000000 c01498cc c1494140 a8bfa8e4 c1494140 c014976c
00000000 00000000
    9fa0: 00000000 00000000 00000000 c010014c 00000000 00000000
00000000 00000000
    9fc0: 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000
    9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
00000000 00000000
    Call trace:
     drm_bridge_connector_hdmi_cec_init from
drmm_connector_hdmi_cec_register+0x104/0x1a8
     drmm_connector_hdmi_cec_register from drm_bridge_connector_init+0x4d8/0x5e8
     drm_bridge_connector_init from rcar_du_encoder_init+0x1e4/0x240
     rcar_du_encoder_init from rcar_du_modeset_init+0x4f0/0x640
     rcar_du_modeset_init from rcar_du_probe+0xe0/0x164
     rcar_du_probe from platform_probe+0x58/0x90
     platform_probe from really_probe+0x128/0x28c
     really_probe from __driver_probe_device+0x16c/0x18c
     __driver_probe_device from driver_probe_device+0x3c/0xbc
     driver_probe_device from __device_attach_driver+0x58/0xbc
     __device_attach_driver from bus_for_each_drv+0xc0/0xd4
     bus_for_each_drv from __device_attach+0xec/0x154
     __device_attach from bus_probe_device+0x2c/0x84
     bus_probe_device from deferred_probe_work_func+0x80/0x98
     deferred_probe_work_func from process_scheduled_works+0x1bc/0x2dc
     process_scheduled_works from worker_thread+0x170/0x208
     worker_thread from kthread+0x160/0x1fc
     kthread from ret_from_fork+0x14/0x28
    Exception stack(0xf0849fb0 to 0xf0849ff8)
    9fa0:                                     00000000 00000000
00000000 00000000
    9fc0: 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000
    9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
    Code: e49de004 e12fff1c e1a01000 e59006c8 (e5903050)
    ---[ end trace 0000000000000000 ]---

Applying "[PATCH v2 0/3] drm/display: bridge_connector: get/put the
stored bridges: fix NULL pointer regression"[1] fixes the issue.

[1] https://lore.kernel.org/20251017-drm-bridge-alloc-getput-bridge-connector-fix-hdmi_cec-v2-0-667abf6d47c0@bootlin.com/

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-10-28  8:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-26 14:59 [PATCH v2] drm/display: bridge_connector: get/put the stored bridges Luca Ceresoli
2025-09-26 22:39 ` Dmitry Baryshkov
2025-09-29  8:37 ` Maxime Ripard
2025-10-03  7:03 ` Luca Ceresoli
     [not found] ` <CGME20251015082254eucas1p23fc961e7a49f4a29ca7a18d3e2817f86@eucas1p2.samsung.com>
2025-10-15  8:22   ` Marek Szyprowski
2025-10-15 20:08     ` Luca Ceresoli
2025-10-17 16:20       ` Luca Ceresoli
2025-10-28  8:55 ` Geert Uytterhoeven

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).