summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_ioctl.c
diff options
context:
space:
mode:
authorVegard Nossum <vegard.nossum@gmail.com>2008-12-02 04:38:47 +0100
committerDave Airlie <airlied@linux.ie>2008-12-29 08:47:22 +0100
commit1147c9cdd0f60f09a98702a9f865176af18a989f (patch)
tree4f3c33102566475cd145cf0235c1738d07b8b715 /drivers/gpu/drm/drm_ioctl.c
parentdrm: move to kref per-master structures. (diff)
downloadlinux-1147c9cdd0f60f09a98702a9f865176af18a989f.tar.xz
linux-1147c9cdd0f60f09a98702a9f865176af18a989f.zip
drm: fix leak of uninitialized data to userspace
...so drm_getunique() is trying to copy some uninitialized data to userspace. The ECX register contains the number of words that are left to copy -- so there are 5 * 4 = 20 bytes left. The offset of the first uninitialized byte (counting from the start of the string) is also 20 (i.e. 0xf65d2294&((1 << 5)-1) == 20). So somebody tried to copy 40 bytes when the string was only 19 long. In drm_set_busid() we have this code: dev->unique_len = 40; dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER); ... len = snprintf(dev->unique, dev->unique_len, pci:%04x:%02x:%02x.%d", ...so it seems that dev->unique is never updated to reflect the actual length of the string. The remaining bytes (20 in this case) are random uninitialized bytes that are copied into userspace. This patch fixes the problem by setting dev->unique_len after the snprintf(). airlied- I've had to fix this up to store the alloced size so we have it for drm_free later. Reported-by: Sitsofe Wheeler <sitsofe@yahoo.com> Signed-off-by: Vegard Nossum <vegardno@thuin.ifi.uio.no> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_ioctl.c')
-rw-r--r--drivers/gpu/drm/drm_ioctl.c10
1 files changed, 7 insertions, 3 deletions
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index e35126a35093..1fad76289e66 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -92,7 +92,8 @@ int drm_setunique(struct drm_device *dev, void *data,
return -EINVAL;
master->unique_len = u->unique_len;
- master->unique = drm_alloc(u->unique_len + 1, DRM_MEM_DRIVER);
+ master->unique_size = u->unique_len + 1;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
if (!master->unique)
return -ENOMEM;
if (copy_from_user(master->unique, u->unique, master->unique_len))
@@ -136,7 +137,8 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
return -EBUSY;
master->unique_len = 40;
- master->unique = drm_alloc(master->unique_len + 1, DRM_MEM_DRIVER);
+ master->unique_size = master->unique_len;
+ master->unique = drm_alloc(master->unique_size, DRM_MEM_DRIVER);
if (master->unique == NULL)
return -ENOMEM;
@@ -145,8 +147,10 @@ static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv)
dev->pdev->bus->number,
PCI_SLOT(dev->pdev->devfn),
PCI_FUNC(dev->pdev->devfn));
- if (len > master->unique_len)
+ if (len >= master->unique_len)
DRM_ERROR("buffer overflow");
+ else
+ master->unique_len = len;
dev->devname =
drm_alloc(strlen(dev->driver->pci_driver.name) + master->unique_len +