⬢ DragonFlyBSD Kernel Audit
DF-4181 / fix.diff
← back to finding ↓ download raw
fix for DF-4181: radeon framebuffer creation never validates that the
backing GEM buffer object is large enough for the scanout image.

The DRM core (framebuffer_check) only enforces pitch >= width*cpp and
height*pitch+offsets <= UINT_MAX; neither radeon_user_framebuffer_create
nor radeon_framebuffer_init compares the required image size against the
BO size, so a 16 KiB BO can back a 1920x1080x4 framebuffer and the display
engine scans out ~8 MB past the end of the object, disclosing adjacent VRAM
contents on the physical display (and the flip path computes the scanout
base from the undersized pinned BO).

Add the per-plane minimum-size check in radeon_framebuffer_init, after
drm_helper_mode_fill_fb_struct has derived fb->format.
--- a/sys/dev/drm/radeon/radeon_display.c
+++ b/sys/dev/drm/radeon/radeon_display.c
@@ -1297,9 +1297,34 @@
 			const struct drm_mode_fb_cmd2 *mode_cmd,
 			struct drm_gem_object *obj)
 {
-	int ret;
+	int ret, i;
+
 	fb->obj[0] = obj;
 	drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd);
+
+	/*
+	 * Verify that the backing buffer object is large enough for the
+	 * scanout image described by mode_cmd.  The DRM core only checks
+	 * pitch >= width * cpp and height * pitch <= UINT_MAX; without
+	 * this check the display engine scans out past the end of the
+	 * object, disclosing adjacent VRAM contents on the display.
+	 */
+	for (i = 0; i < fb->format->num_planes; i++) {
+		u64 min_size;
+
+		min_size = (u64)mode_cmd->pitches[i] *
+			DIV_ROUND_UP(mode_cmd->height,
+				     i ? fb->format->vsub : 1) +
+			mode_cmd->offsets[i];
+		if (min_size > radeon_bo_size(gem_to_radeon_bo(obj))) {
+			DRM_DEBUG_KMS("framebuffer plane %d needs %llu bytes, "
+				      "buffer object has %lu\n", i, min_size,
+				      radeon_bo_size(gem_to_radeon_bo(obj)));
+			fb->obj[0] = NULL;
+			return -EINVAL;
+		}
+	}
+
 	ret = drm_framebuffer_init(dev, fb, &radeon_fb_funcs);
 	if (ret) {
 		fb->obj[0] = NULL;