⬢ DragonFlyBSD Kernel Audit
DF-3987 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/amd/display/dc/core/dc_link_dp.c
+++ b/sys/dev/drm/amd/display/dc/core/dc_link_dp.c
@@ -1593,7 +1593,11 @@
 	/*1. Check that Link Status changed, before re-training.*/
 
 	/*parse lane status*/
-	for (lane = 0; lane < link->cur_link_settings.lane_count; lane++) {
+	/* DF-3987: cur_link_settings.lane_count is sink-fed state; the
+	 * nibble reads below run off union hpd_irq_data (6 bytes) if it
+	 * ever exceeds LANE_COUNT_DP_MAX. */
+	for (lane = 0; lane < LANE_COUNT_DP_MAX &&
+			lane < link->cur_link_settings.lane_count; lane++) {
 		/* check status of lanes 0,1
 		 * changed DpcdAddress_Lane01Status (0x202)
 		 */
@@ -1780,6 +1784,33 @@
 			(unsigned char *)(&link_settings.link_rate),
 			1);
 
+	/*
+	 * DF-1319 / DF-3987: DP_TEST_LANE_COUNT and DP_TEST_LINK_RATE are
+	 * raw sink bytes.  A malicious sink can hand back e.g. 0xFF, which
+	 * is persisted into verified_link_cap (below) and into
+	 * link->cur_link_settings by dp_retrain_link_dp_test
+	 * (dc_link_hwss.c:319) and then used as a loop bound by
+	 * dp_test_send_phy_test_pattern and by
+	 * hpd_rx_irq_check_link_loss_status.  Reject anything the DP spec
+	 * does not define before it poisons driver state.
+	 */
+	if (link_settings.lane_count != LANE_COUNT_ONE &&
+			link_settings.lane_count != LANE_COUNT_TWO &&
+			link_settings.lane_count != LANE_COUNT_FOUR) {
+		DC_LOG_ERROR("%s: invalid DP_TEST_LANE_COUNT 0x%02x\n",
+				__func__, link_settings.lane_count);
+		return;
+	}
+	if (link_settings.link_rate != LINK_RATE_LOW &&
+			link_settings.link_rate != LINK_RATE_HIGH &&
+			link_settings.link_rate != LINK_RATE_RBR2 &&
+			link_settings.link_rate != LINK_RATE_HIGH2 &&
+			link_settings.link_rate != LINK_RATE_HIGH3) {
+		DC_LOG_ERROR("%s: invalid DP_TEST_LINK_RATE 0x%02x\n",
+				__func__, link_settings.link_rate);
+		return;
+	}
+
 	/* Set preferred link settings */
 	link->verified_link_cap.lane_count = link_settings.lane_count;
 	link->verified_link_cap.link_rate = link_settings.link_rate;
@@ -1878,8 +1909,10 @@
 	/* prepare link training settings */
 	link_settings.link = link->cur_link_settings;
 
-	for (lane = 0; lane <
-		(unsigned int)(link->cur_link_settings.lane_count);
+	/* DF-3987: cur_link_settings.lane_count is sink-fed state; never
+	 * index lane_settings[LANE_COUNT_DP_MAX] past LANE_COUNT_DP_MAX. */
+	for (lane = 0; lane < LANE_COUNT_DP_MAX &&
+		lane < (unsigned int)(link->cur_link_settings.lane_count);
 		lane++) {
 		dpcd_lane_adjust.raw =
 			get_nibble_at_index(&dpcd_lane_adjustment[0].raw, lane);