⬢ DragonFlyBSD Kernel Audit
DF-3626 / fix.diff
← back to finding ↓ download raw
--- 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) {