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

The DRM core (framebuffer_check, drm_framebuffer.c:165) only enforces
pitch >= width*cpp (:211) and height*pitch+offsets <= UINT_MAX (:208);
neither amdgpu_display_user_framebuffer_create (amdgpu_display.c:523, the
.fb_create for BOTH the legacy amdgpu_mode_funcs :561 and the DC
amdgpu_dm.c:945) nor amdgpu_display_framebuffer_init (:506) 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.  The scanout paths pin and program without re-checking
--- a/sys/dev/drm/amd/amdgpu/amdgpu_display.c
+++ b/sys/dev/drm/amd/amdgpu/amdgpu_display.c
@@ -508,9 +508,34 @@ int amdgpu_display_framebuffer_init(stru
 				    const struct drm_mode_fb_cmd2 *mode_cmd,
 				    struct drm_gem_object *obj)
 {
-	int ret;
+	int ret, i;
+
 	rfb->base.obj[0] = obj;
 	drm_helper_mode_fill_fb_struct(dev, &rfb->base, 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 < rfb->base.format->num_planes; i++) {
+		u64 min_size;
+
+		min_size = (u64)mode_cmd->pitches[i] *
+			DIV_ROUND_UP(mode_cmd->height,
+				     i ? rfb->base.format->vsub : 1) +
+			mode_cmd->offsets[i];
+		if (min_size > amdgpu_bo_size(gem_to_amdgpu_bo(obj))) {
+			DRM_DEBUG_KMS("framebuffer plane %d needs %llu bytes, "
+				      "buffer object has %lu\n", i, min_size,
+				      amdgpu_bo_size(gem_to_amdgpu_bo(obj)));
+			rfb->base.obj[0] = NULL;
+			return -EINVAL;
+		}
+	}
+
 	ret = drm_framebuffer_init(dev, &rfb->base, &amdgpu_fb_funcs);
 	if (ret) {
 		rfb->base.obj[0] = NULL;