drm: fix leak of uninitialized data to userspace
authorVegard Nossum <vegard.nossum@gmail.com>
Tue, 2 Dec 2008 03:38:47 +0000 (13:38 +1000)
committerDave Airlie <airlied@linux.ie>
Mon, 29 Dec 2008 07:47:22 +0000 (17:47 +1000)
...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>
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_stub.c
include/drm/drmP.h

index e35126a35093c60526becf03b31e38d412415687..1fad76289e665d0cfbc6f7026f6f568ff6809a53 100644 (file)
@@ -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 +
index 0f24c2dcd5176c81614bb4de42bf400a13fe6f37..f7985c303cb0009d358199f95e0195049239369c 100644 (file)
@@ -117,7 +117,7 @@ static void drm_master_destroy(struct kref *kref)
                dev->driver->master_destroy(dev, master);
 
        if (master->unique) {
-               drm_free(master->unique, strlen(master->unique) + 1, DRM_MEM_DRIVER);
+               drm_free(master->unique, master->unique_size, DRM_MEM_DRIVER);
                master->unique = NULL;
                master->unique_len = 0;
        }
index 4c6e8298b424c8b15e24d2303d6b0df9f5cdc6f9..c9cc618dbcfc39ac139116f2eee8d0e27db32583 100644 (file)
@@ -627,6 +627,7 @@ struct drm_master {
 
        char *unique;                   /**< Unique identifier: e.g., busid */
        int unique_len;                 /**< Length of unique field */
+       int unique_size;                /**< amount allocated */
 
        int blocked;                    /**< Blocked due to VC switch? */