CVE-2026-46209
Description
In the Linux kernel, the following vulnerability has been resolved:
drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
drm_gem_fb_init_with_funcs() computes sub-sampled plane dimensions using plain integer division:
unsigned int width = mode_cmd->width / (i ? info->hsub : 1); unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
However, the ioctl-level framebuffer_check() in drm_framebuffer.c uses drm_format_info_plane_width/height() which round up dimensions via DIV_ROUND_UP(). This inconsistency corrupts the subsequent GEM object size check for certain pixel format and dimension combinations.
For example, with NV12 (vsub=2) and a 1-pixel-tall framebuffer the GEM size validation path sees height=0 instead of height=1. The expression (height - 1) then wraps to UINT_MAX as an unsigned int, causing min_size to overflow and wrap back to a small value. A tiny GEM object therefore passes the size guard, yet when the GPU accesses the chroma plane it will read or write memory beyond the object's bounds.
Fix by replacing the open-coded divisions with drm_format_info_plane_width() and drm_format_info_plane_height(), which use DIV_ROUND_UP() and match the calculation already used in framebuffer_check().
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Inconsistent plane dimension rounding in Linux kernel DRM GEM leads to out-of-bounds memory access via integer underflow.
Vulnerability
In the Linux kernel, drm_gem_fb_init_with_funcs() computes sub-sampled plane dimensions using plain integer division (width / hsub, height / vsub), while the ioctl-level framebuffer_check() uses drm_format_info_plane_width/height() which round up via DIV_ROUND_UP(). This inconsistency can cause an integer underflow for certain formats (e.g., NV12 with height=1), leading to a small GEM object passing the size check, enabling out-of-bounds access. The issue affects kernel versions before the fix commit [1].
Exploitation
An attacker with the ability to create a framebuffer (e.g., via DRM ioctl) can craft a mode_cmd with dimensions such that the division yields zero for a plane (e.g., NV12 with height=1). The GEM object size check then wraps, allowing a small object to be used. No special privileges beyond DRM access are required.
Impact
Successful exploitation allows an attacker to trigger out-of-bounds reads or writes when the GPU accesses the chroma plane, potentially leading to information disclosure or privilege escalation.
Mitigation
The fix is included in commit c5fc49d8470c5ebf3b41607600f277158f159950 [1]. Users should apply the patch from the stable kernel tree. No workaround is available; update to a patched kernel version.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
103d4c2268bd72drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 9166c353f131cb..88808e972cc156 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -172,8 +172,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
6b992591e04fdrm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 3bdb6ba37ff42f..2383ebb5e435a4 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -174,8 +174,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
1da4ab7189f1drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 3bdb6ba37ff42f..2383ebb5e435a4 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -174,8 +174,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
1a17ea9861e8drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 4bc89d33df5930..daa5471365c456 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -171,8 +171,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
c5fc49d8470cdrm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 9166c353f131cb..88808e972cc156 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -172,8 +172,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
1a17ea9861e8drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 4bc89d33df5930..daa5471365c456 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -171,8 +171,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
1da4ab7189f1drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 3bdb6ba37ff42f..2383ebb5e435a4 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -174,8 +174,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
3d4c2268bd72drm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 9166c353f131cb..88808e972cc156 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -172,8 +172,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
6b992591e04fdrm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 3bdb6ba37ff42f..2383ebb5e435a4 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -174,8 +174,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
c5fc49d8470cdrm/gem: Fix inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs()
1 file changed · +2 −3
drivers/gpu/drm/drm_gem_framebuffer_helper.c+2 −3 modifieddiff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 9166c353f131cb..88808e972cc156 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -172,8 +172,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, } for (i = 0; i < info->num_planes; i++) { - unsigned int width = mode_cmd->width / (i ? info->hsub : 1); - unsigned int height = mode_cmd->height / (i ? info->vsub : 1); + unsigned int width = drm_format_info_plane_width(info, mode_cmd->width, i); + unsigned int height = drm_format_info_plane_height(info, mode_cmd->height, i); unsigned int min_size; objs[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Inconsistent plane dimension calculation in drm_gem_fb_init_with_funcs() uses plain integer division instead of round-up division, causing an unsigned integer wraparound in the GEM object size check."
Attack vector
An attacker with the ability to create a DRM framebuffer via the ioctl interface can supply a pixel format such as NV12 (vsub=2) with a 1-pixel-tall framebuffer. The inconsistent dimension calculation causes the GEM size validation to compute height=0 instead of height=1, leading to an unsigned integer wraparound in the `min_size` calculation. This allows a tiny GEM object to pass the size guard, and subsequent GPU access to the chroma plane reads or writes out-of-bounds memory. [patch_id=2897741]
Affected code
The vulnerability resides in `drm_gem_fb_init_with_funcs()` in `drivers/gpu/drm/drm_gem_framebuffer_helper.c`. The function computed sub-sampled plane dimensions using plain integer division (`mode_cmd->width / (i ? info->hsub : 1)`) instead of the round-up helpers `drm_format_info_plane_width()` and `drm_format_info_plane_height()` used by the ioctl-level `framebuffer_check()`. [patch_id=2897741]
What the fix does
The patch replaces the open-coded integer divisions with calls to `drm_format_info_plane_width()` and `drm_format_info_plane_height()`, which use `DIV_ROUND_UP()` and therefore match the calculation already performed in `framebuffer_check()`. This ensures that the plane dimensions used for the GEM object size check are consistent with the dimensions used at the ioctl level, preventing the truncation-to-zero that caused the unsigned wraparound. [patch_id=2897741]
Preconditions
- inputThe attacker must be able to issue DRM ioctl calls to create a framebuffer with a multi-plane pixel format (e.g., NV12) and a small dimension (e.g., 1 pixel tall).
- authThe attacker must have access to a DRM device node (local user access to /dev/dri/card*).
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/1a17ea9861e89585361caa8bc231bd22dc6dbe7dnvd
- git.kernel.org/stable/c/1da4ab7189f1064b3b712b388772c008b4d82580nvd
- git.kernel.org/stable/c/3d4c2268bd7243c3780fe32bf24ff876da272acfnvd
- git.kernel.org/stable/c/6b992591e04f2cce813bcf239b354f375bbf84d3nvd
- git.kernel.org/stable/c/c5fc49d8470c5ebf3b41607600f277158f159950nvd
News mentions
0No linked articles in our index yet.