--- a/sys/dev/drm/i915/intel_dp.c +++ b/sys/dev/drm/i915/intel_dp.c @@ -4282,10 +4282,11 @@ static int intel_dp_check_mst_status(struct intel_dp *intel_dp) { bool bret; if (intel_dp->is_mst) { u8 esi[DP_DPRX_ESI_LEN] = { 0 }; int ret = 0; int retry; bool handled; + int bounces = 0; WARN_ON_ONCE(intel_dp->active_mst_links < 0); bret = intel_dp_get_sink_irq_esi(intel_dp, esi); go_again: if (bret == true) { @@ -4306,14 +4307,24 @@ go_again: DRM_DEBUG_KMS("got esi %3ph\n", esi); ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled); if (handled) { + /* + * A malicious or broken sink can keep the + * ESI irq bits (or the sink count) changing + * forever. Bound the number of re-reads so a + * hostile branch device cannot livelock the + * hotplug workqueue thread; fall back to a + * full hotplug reprobe instead. + */ + if (++bounces > 8) { + DRM_ERROR("MST sink keeps signalling ESI irq, falling back to full detect\n"); + drm_kms_helper_hotplug_event( + dp_to_dig_port(intel_dp)->base.base.dev); + return ret; + } for (retry = 0; retry < 3; retry++) { int wret; wret = drm_dp_dpcd_write(&intel_dp->aux, DP_SINK_COUNT_ESI+1, &esi[1], 3); if (wret == 3) {