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. Signed-off-by: Thomas Zimmermann <[email protected]> Suggested-by: Sashiko <[email protected]> --- drivers/gpu/drm/sysfb/simpledrm.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index 76b9a3f5c4ef..a04f0c852ea4 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> @@ -624,6 +625,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; @@ -704,6 +706,15 @@ 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); + } + size = ALIGN(size, PAGE_SIZE); + if (size < PAGE_SIZE) { + drm_err(dev, "framebuffer alignment exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); sysfb->fb_format = format; @@ -729,6 +740,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); + if (resource_size(mem) > size) { + 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; @@ -762,6 +780,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, mem = res; } + if (resource_size(mem) > size) { + 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
