⬢ DragonFlyBSD Kernel Audit
DF-4225 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/drm_modes.c
+++ b/sys/dev/drm/drm_modes.c
@@ -780,9 +780,9 @@ int drm_mode_vrefresh(const struct drm_display_mode
 	else if (mode->htotal > 0 && mode->vtotal > 0) {
-		unsigned int num, den;
+		u64 num, den;
 
-		num = mode->clock * 1000;
-		den = mode->htotal * mode->vtotal;
+		num = (u64)mode->clock * 1000;
+		den = (u64)mode->htotal * mode->vtotal;
 
 		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 			num *= 2;
 		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
 			den *= 2;
 		if (mode->vscan > 1)
 			den *= mode->vscan;
@@ -1111,6 +1111,15 @@ static enum drm_mode_status
 drm_mode_validate_basic(const struct drm_display_mode *mode)
 {
 	if (mode->type & ~DRM_MODE_TYPE_ALL)
 		return MODE_BAD;
 
 	if (mode->flags & ~DRM_MODE_FLAG_ALL)
 		return MODE_BAD;
 
 	if ((mode->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX)
 		return MODE_BAD;
 
 	if (mode->clock == 0)
 		return MODE_CLOCK_LOW;
+
+	/* drm_mode_modeinfo carries u32 timings but the core and every
+	 * driver consume them as int; anything above the CRTC hardware
+	 * register width (USHRT_MAX, cf. the WARN in
+	 * drm_mode_convert_to_umode) can wrap products such as
+	 * htotal*vtotal to 0 and trap in drm_mode_vrefresh().
+	 */
+	if (mode->hdisplay > USHRT_MAX || mode->hsync_start > USHRT_MAX ||
+	    mode->hsync_end > USHRT_MAX || mode->htotal > USHRT_MAX)
+		return MODE_BAD_HVALUE;
+	if (mode->vdisplay > USHRT_MAX || mode->vsync_start > USHRT_MAX ||
+	    mode->vsync_end > USHRT_MAX || mode->vtotal > USHRT_MAX)
+		return MODE_BAD_VVALUE;
 
 	if (mode->hdisplay == 0 ||
 	    mode->hsync_start < mode->hdisplay ||
 	    mode->hsync_end < mode->hsync_start ||
	    mode->htotal < mode->hsync_end)