Message ID | 20240308090357.8758-1-johan+linaro@kernel.org (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
Series | [stable-6.7] soc: qcom: pmic_glink_altmode: fix drm bridge use-after-free | expand |
On Fri, Mar 08, 2024 at 10:03:57AM +0100, Johan Hovold wrote: >commit b979f2d50a099f3402418d7ff5f26c3952fb08bb upstream. > >A recent DRM series purporting to simplify support for "transparent >bridges" and handling of probe deferrals ironically exposed a >use-after-free issue on pmic_glink_altmode probe deferral. > >This has manifested itself as the display subsystem occasionally failing >to initialise and NULL-pointer dereferences during boot of machines like >the Lenovo ThinkPad X13s. > >Specifically, the dp-hpd bridge is currently registered before all >resources have been acquired which means that it can also be >deregistered on probe deferrals. > >In the meantime there is a race window where the new aux bridge driver >(or PHY driver previously) may have looked up the dp-hpd bridge and >stored a (non-reference-counted) pointer to the bridge which is about to >be deallocated. > >When the display controller is later initialised, this triggers a >use-after-free when attaching the bridges: > > dp -> aux -> dp-hpd (freed) > >which may, for example, result in the freed bridge failing to attach: > > [drm:drm_bridge_attach [drm]] *ERROR* failed to attach bridge /soc@0/phy@88eb000 to encoder TMDS-31: -16 > >or a NULL-pointer dereference: > > Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 > ... > Call trace: > drm_bridge_attach+0x70/0x1a8 [drm] > drm_aux_bridge_attach+0x24/0x38 [aux_bridge] > drm_bridge_attach+0x80/0x1a8 [drm] > dp_bridge_init+0xa8/0x15c [msm] > msm_dp_modeset_init+0x28/0xc4 [msm] > >The DRM bridge implementation is clearly fragile and implicitly built on >the assumption that bridges may never go away. In this case, the fix is >to move the bridge registration in the pmic_glink_altmode driver to >after all resources have been looked up. > >Incidentally, with the new dp-hpd bridge implementation, which registers >child devices, this is also a requirement due to a long-standing issue >in driver core that can otherwise lead to a probe deferral loop (see >commit fbc35b45f9f6 ("Add documentation on meaning of -EPROBE_DEFER")). > >[DB: slightly fixed commit message by adding the word 'commit'] >Fixes: 080b4e24852b ("soc: qcom: pmic_glink: Introduce altmode support") >Fixes: 2bcca96abfbf ("soc: qcom: pmic-glink: switch to DRM_AUX_HPD_BRIDGE") >Cc: <stable@vger.kernel.org> # 6.3 >Cc: Bjorn Andersson <andersson@kernel.org> >Cc: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> >Signed-off-by: Johan Hovold <johan+linaro@kernel.org> >Reviewed-by: Bjorn Andersson <andersson@kernel.org> >Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> >Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> >Link: https://patchwork.freedesktop.org/patch/msgid/20240217150228.5788-4-johan+linaro@kernel.org >[ johan: backport to 6.7 which does not have DRM aux bridge ] >Signed-off-by: Johan Hovold <johan+linaro@kernel.org> Queued up, thanks!
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index ad922f0dca6b..a890fafdafb8 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -469,12 +469,6 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->bridge.ops = DRM_BRIDGE_OP_HPD; alt_port->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; - ret = devm_drm_bridge_add(dev, &alt_port->bridge); - if (ret) { - fwnode_handle_put(fwnode); - return ret; - } - alt_port->dp_alt.svid = USB_TYPEC_DP_SID; alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; alt_port->dp_alt.active = 1; @@ -525,6 +519,16 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, } } + for (port = 0; port < ARRAY_SIZE(altmode->ports); port++) { + alt_port = &altmode->ports[port]; + if (!alt_port->altmode) + continue; + + ret = devm_drm_bridge_add(dev, &alt_port->bridge); + if (ret) + return ret; + } + altmode->client = devm_pmic_glink_register_client(dev, altmode->owner_id, pmic_glink_altmode_callback,