Tighten up AGP security. Verify that all uses of AGP are done inside
authorJon Smirl <jonsmirl@yahoo.com>
Thu, 4 Aug 2005 14:39:25 +0000 (14:39 +0000)
committerJon Smirl <jonsmirl@yahoo.com>
Thu, 4 Aug 2005 14:39:25 +0000 (14:39 +0000)
    buffers that have been allocated from AGP. This includes some new
    capable(CAP_SYS_ADMIN) checks, these functions are also protected by
    the root requirement on the IOCTL macros.

linux-core/drm_bufs.c

index baa9630..c508c39 100644 (file)
@@ -181,15 +181,34 @@ int drm_addmap(drm_device_t * dev, unsigned int offset,
                        dev->sigdata.lock = dev->lock.hw_lock = map->handle;    /* Pointer to lock */
                }
                break;
-       case _DRM_AGP:
-               if (drm_core_has_AGP(dev)) {
+       case _DRM_AGP: {
+               drm_agp_mem_t *entry;
+               int valid = 0;
+
+               if (!drm_core_has_AGP(dev)) {
+                       drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+                       return -EINVAL;
+               }
 #ifdef __alpha__
-                       map->offset += dev->hose->mem_space->start;
+               map->offset += dev->hose->mem_space->start;
 #endif
-                       map->offset += dev->agp->base;
-                       map->mtrr = dev->agp->agp_mtrr; /* for getmap */
+               map->offset += dev->agp->base;
+               map->mtrr = dev->agp->agp_mtrr; /* for getmap */
+
+               for (entry = dev->agp->memory; entry; entry = entry->next) {
+                       if ((map->offset >= entry->bound) &&
+                           (map->offset + map->size <= entry->bound + entry->pages * PAGE_SIZE)) {
+                               valid = 1;
+                               break;
+                       }
+               }
+               if (!valid) {
+                       drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+                       return -EPERM;
                }
+               DRM_DEBUG("AGP offset = 0x%08lx, size = 0x%08lx\n", map->offset, map->size);
                break;
+       }
        case _DRM_SCATTER_GATHER:
                if (!dev->sg) {
                        drm_free(map, sizeof(*map), DRM_MEM_MAPS);
@@ -258,6 +277,9 @@ int drm_addmap_ioctl(struct inode *inode, struct file *filp,
                return -EFAULT;
        }
 
+       if (!(capable(CAP_SYS_ADMIN) || map.type == _DRM_AGP))
+               return -EPERM;
+
        err = drm_addmap( dev, map.offset, map.size, map.type, map.flags,
                          & map_ptr );
 
@@ -475,6 +497,7 @@ int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request)
 {
        drm_device_dma_t *dma = dev->dma;
        drm_buf_entry_t *entry;
+       drm_agp_mem_t *agp_entry;
        drm_buf_t *buf;
        unsigned long offset;
        unsigned long agp_offset;
@@ -485,10 +508,9 @@ int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request)
        int page_order;
        int total;
        int byte_count;
-       int i;
+       int i, valid;
        drm_buf_t **temp_buflist;
 
-
        if (!dma)
                return -EINVAL;
 
@@ -507,7 +529,7 @@ int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request)
        DRM_DEBUG("count:      %d\n", count);
        DRM_DEBUG("order:      %d\n", order);
        DRM_DEBUG("size:       %d\n", size);
-       DRM_DEBUG("agp_offset: %lu\n", agp_offset);
+       DRM_DEBUG("agp_offset: %lx\n", agp_offset);
        DRM_DEBUG("alignment:  %d\n", alignment);
        DRM_DEBUG("page_order: %d\n", page_order);
        DRM_DEBUG("total:      %d\n", total);
@@ -517,6 +539,19 @@ int drm_addbufs_agp(drm_device_t * dev, drm_buf_desc_t * request)
        if (dev->queue_count)
                return -EBUSY;  /* Not while in use */
 
+       /* Make sure buffers are located in AGP memory that we own */
+       valid = 0;
+       for (agp_entry = dev->agp->memory; agp_entry; agp_entry = agp_entry->next) {
+               if ((agp_offset >= agp_entry->bound) &&
+                   (agp_offset + total * count <= agp_entry->bound + agp_entry->pages * PAGE_SIZE)) {
+                       valid = 1;
+                       break;
+               }
+       }
+       if (!valid) {
+               DRM_DEBUG("zone invalid\n");
+               return -EINVAL;
+       }
        spin_lock(&dev->count_lock);
        if (dev->buf_use) {
                spin_unlock(&dev->count_lock);
@@ -651,6 +686,9 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request)
        if (!dma)
                return -EINVAL;
 
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        count = request->count;
        order = drm_order(request->size);
        size = 1 << order;
@@ -867,6 +905,9 @@ static int drm_addbufs_sg(drm_device_t * dev, drm_buf_desc_t * request)
        if (!dma)
                return -EINVAL;
 
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        count = request->count;
        order = drm_order(request->size);
        size = 1 << order;
@@ -1025,6 +1066,9 @@ int drm_addbufs_fb(drm_device_t * dev, drm_buf_desc_t * request)
        if (!dma)
                return -EINVAL;
 
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
        count = request->count;
        order = drm_order(request->size);
        size = 1 << order;