The primary plane's atomic_update() copies the shadow framebuffer
into the ioremapped VRAM with the fb pitch as the row stride.  The
blit offset and length grow with the pitch, but nothing checks them
against the size of the VRAM mapping.

Addfb2 only enforces a minimum pitch, so userspace can pick an
arbitrarily large one.  A single atomic commit then writes
framebuffer content beyond the VRAM mapping, e.g. on mainline
v7.4-rc1 (KASAN build):

    BUG: unable to handle page fault for address: ffffc90002800000
    Oops: Oops: 0002 [#1] SMP KASAN NOPTI
    CPU: 2 UID: 1000 PID: 1258 Comm: poc Not tainted 7.4.0-rc1 #1
    RIP: 0010:memcpy_toio+0x22/0x50
    CR2: ffffc90002800000 CR3: 0000000110f75000 CR4: 00000000003006f0
    Call Trace:
     drm_fb_memcpy+0x37c/0x6b0
     bochs_primary_plane_helper_atomic_update+0x36c/0xa20

Reject framebuffers whose pitch * height exceeds the VRAM size in
the primary plane's atomic_check(), same as the hyperv DRM driver
does.

This issue was discovered by Tencent CodeBuddy Security.

Reported-by: zeroday zhao <[email protected]>
Fixes: c3ac343c14485 ("drm/bochs: Use GEM SHMEM helpers for memory management")
Cc: [email protected]
Signed-off-by: Henry Martin <[email protected]>
---
 drivers/gpu/drm/tiny/bochs.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index 8192decddbc1e..527ddd8bcf369 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -422,7 +422,9 @@ static const uint32_t bochs_primary_plane_formats[] = {
 static int bochs_primary_plane_helper_atomic_check(struct drm_plane *plane,
                                                   struct drm_atomic_commit 
*state)
 {
+       struct bochs_device *bochs = to_bochs_device(plane->dev);
        struct drm_plane_state *new_plane_state = 
drm_atomic_get_new_plane_state(state, plane);
+       struct drm_framebuffer *fb = new_plane_state->fb;
        struct drm_crtc *new_crtc = new_plane_state->crtc;
        struct drm_crtc_state *new_crtc_state = NULL;
        int ret;
@@ -439,6 +441,15 @@ static int bochs_primary_plane_helper_atomic_check(struct 
drm_plane *plane,
        else if (!new_plane_state->visible)
                return 0;
 
+       /*
+        * atomic_update() blits the shadow framebuffer into the
+        * ioremapped VRAM with the fb pitch as the row stride. The
+        * blit offset and length grow with the pitch, so make sure
+        * the framebuffer fits into the VRAM mapping.
+        */
+       if ((u64)fb->pitches[0] * fb->height > bochs->fb_size)
+               return -EINVAL;
+
        return 0;
 }
 
-- 
2.43.7


Reply via email to