The size of the mmap'ed framebuffer could be smaller than the minimum required framebuffer size. Validate the resource size against the framebuffer size.
Buggy firmware that triggers this check should be fixed up with a quirk on a case-by-case base. v3: - fix size checks (Sashiko) - improve error message for alignment check (Thierry) - do not store aligned size value Signed-off-by: Thomas Zimmermann <[email protected]> Suggested-by: Sashiko <[email protected]> Reviewed-by: Thierry Reding <[email protected]> --- drivers/gpu/drm/sysfb/simpledrm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index a899dfb747bf..9e0711e0095a 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -6,6 +6,7 @@ #include <linux/of_address.h> #include <linux/of_clk.h> #include <linux/of_reserved_mem.h> +#include <linux/overflow.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/pm.h> @@ -623,6 +624,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, u16 width_mm = 0, height_mm = 0; struct device_node *panel_node; const struct drm_format_info *format; + u64 size; struct resource *res, *mem = NULL; struct drm_plane *primary_plane; struct drm_crtc *crtc; @@ -713,6 +715,14 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, } stride = pitch; } + if (check_mul_overflow(height, stride, &size)) { + drm_err(dev, "framebuffer size exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } + if (ALIGN(size, PAGE_SIZE) < PAGE_SIZE) { + drm_err(dev, "page-aligned framebuffer exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); sysfb->fb_format = format; @@ -738,6 +748,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); + if (size > resource_size(mem)) { + drm_err(dev, + "framebuffer size of %llu exceeds memory range %pr\n", + size, mem); + return ERR_PTR(-EINVAL); + } + screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC); if (IS_ERR(screen_base)) return screen_base; @@ -771,6 +788,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, mem = res; } + if (size > resource_size(mem)) { + drm_err(dev, + "framebuffer size of %llu exceeds memory range %pr\n", + size, mem); + return ERR_PTR(-EINVAL); + } + screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); if (!screen_base) return ERR_PTR(-ENOMEM); -- 2.54.0
