summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2012-12-19 14:10:48 +1000
committerDave Airlie <airlied@redhat.com>2012-12-19 14:20:39 +1000
commitfe04655e15b932162f4c0c92449afa65b991c2b6 (patch)
tree4a69c51fb7556987e48baca0068466483fda8daa
parent34149d694edb1732325b320fd444ebe1d43a0083 (diff)
drm/ttm: add file mmap validationdrm-vma-manager
should block mmaps on wrong files
-rw-r--r--drivers/gpu/drm/drm_vma_offset_man.c43
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c7
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c15
-rw-r--r--include/drm/drm_vma_offset_man.h18
5 files changed, 86 insertions, 4 deletions
diff --git a/drivers/gpu/drm/drm_vma_offset_man.c b/drivers/gpu/drm/drm_vma_offset_man.c
index 745689282c8a..fb303f7ce364 100644
--- a/drivers/gpu/drm/drm_vma_offset_man.c
+++ b/drivers/gpu/drm/drm_vma_offset_man.c
@@ -91,6 +91,7 @@ int drm_vma_offset_setup(struct drm_vma_offset_manager *man,
{
int ret;
+ list_init(&node->flist);
retry_pre_get:
ret = drm_mm_pre_get(&man->addr_space_mm);
if (unlikely(ret != 0))
@@ -158,3 +159,45 @@ void drm_vma_offset_man_fini(struct drm_vma_offset_manager *man)
write_unlock(&man->vm_lock);
}
EXPORT_SYMBOL(drm_vma_offset_man_fini);
+
+int drm_vma_offset_node_add_file(struct drm_vma_offset_node *node,
+ struct file *filp)
+{
+ struct drm_vma_offset_node_per_file *fnode;
+
+ fnode = kmalloc(sizeof(*fnode), GFP_KERNEL);
+ if (!fnode)
+ return -ENOMEM;
+
+ fnode->filp = filp;
+ list_add(&fnode->lhead, &node->flist);
+ return 0;
+}
+EXPORT_SYMBOL(drm_vma_offset_node_add_file);
+
+void drm_vma_offset_node_remove_file(struct drm_vma_offset_node *node,
+ struct file *filp)
+{
+ struct drm_vma_offset_node_per_file *fnode, *temp;
+
+ list_for_each_entry_safe(fnode, temp, &node->flist, lhead) {
+ if (fnode->filp == filp) {
+ list_del(&fnode->lhead);
+ kfree(fnode);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL(drm_vma_offset_node_remove_file);
+
+bool drm_vma_offset_node_valid_file(struct drm_vma_offset_node *node,
+ struct file *filp)
+{
+ struct drm_vma_offset_node_per_file *fnode;
+ list_for_each_entry(fnode, &node->flist, lhead) {
+ if (fnode->filp == filp)
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(drm_vma_offset_node_valid_file);
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 6be9249c2436..8281f5c9a603 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -74,6 +74,11 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
struct nouveau_vma *vma;
int ret;
+ ret = drm_vma_offset_node_add_file(&nvbo->bo.vma_offset,
+ file_priv->filp);
+ if (ret)
+ return ret;
+
if (!cli->base.vm)
return 0;
@@ -111,6 +116,8 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
struct nouveau_vma *vma;
int ret;
+ drm_vma_offset_node_remove_file(&nvbo->bo.vma_offset, file_priv->filp);
+
if (!cli->base.vm)
return;
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index fe5c1f6b7957..daba96588cfe 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -146,6 +146,12 @@ int radeon_gem_object_open(struct drm_gem_object *obj, struct drm_file *file_pri
struct radeon_bo_va *bo_va;
int r;
+ /* allocate a file to bo */
+ r = drm_vma_offset_node_add_file(&rbo->tbo.vma_offset,
+ file_priv->filp);
+ if (r)
+ return r;
+
if (rdev->family < CHIP_CAYMAN) {
return 0;
}
@@ -176,6 +182,7 @@ void radeon_gem_object_close(struct drm_gem_object *obj,
struct radeon_bo_va *bo_va;
int r;
+ drm_vma_offset_node_remove_file(&rbo->tbo.vma_offset, file_priv->filp);
if (rdev->family < CHIP_CAYMAN) {
return;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 3e52e25dbf7f..d111d3d6e655 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -218,7 +218,8 @@ static const struct vm_operations_struct ttm_bo_vm_ops = {
.close = ttm_bo_vm_close
};
-static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
+static struct ttm_buffer_object *ttm_bo_vm_lookup(struct file *filp,
+ struct ttm_bo_device *bdev,
unsigned long dev_offset,
unsigned long num_pages)
{
@@ -236,10 +237,18 @@ static struct ttm_buffer_object *ttm_bo_vm_lookup(struct ttm_bo_device *bdev,
bo = container_of(node, struct ttm_buffer_object, vma_offset);
ttm_bo_reference(bo);
+ if (drm_vma_offset_node_valid_file(&bo->vma_offset, filp) == false) {
+ bo = NULL;
+ pr_err("Invalid buffer object for this file descriptor\n");
+ goto out;
+ }
+
if (!kref_get_unless_zero(&bo->kref)) {
bo = NULL;
pr_err("Could not find buffer object to map\n");
}
+
+out:
read_unlock(&bdev->vm_lock);
return bo;
}
@@ -251,7 +260,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
struct ttm_buffer_object *bo;
int ret;
- bo = ttm_bo_vm_lookup(bdev,
+ bo = ttm_bo_vm_lookup(filp, bdev,
vma->vm_pgoff,
(vma->vm_end - vma->vm_start) >> PAGE_SHIFT);
if (unlikely(bo == NULL))
@@ -313,7 +322,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
bool no_wait = false;
bool dummy;
- bo = ttm_bo_vm_lookup(bdev, dev_offset, 1);
+ bo = ttm_bo_vm_lookup(filp, bdev, dev_offset, 1);
if (unlikely(bo == NULL))
return -EFAULT;
diff --git a/include/drm/drm_vma_offset_man.h b/include/drm/drm_vma_offset_man.h
index b8ef8453d942..6a297f265696 100644
--- a/include/drm/drm_vma_offset_man.h
+++ b/include/drm/drm_vma_offset_man.h
@@ -2,13 +2,23 @@
#ifndef DRM_VMA_OFFSET_MAN
#define DRM_VMA_OFFSET_MAN
+#include <linux/fs.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <drm/drm_mm.h>
-struct drm_mm_node;
+/* store a linked list of file privs this object is valid on,
+ the bust a move in mmap */
+struct drm_vma_offset_node_per_file {
+ struct list_head lhead;
+ struct file *filp;
+};
+
+/* you'd want a linked list of file privs per node */
struct drm_vma_offset_node {
+ struct list_head flist;
+
struct drm_mm_node *vm_node;
struct rb_node vm_rb;
uint64_t num_pages;
@@ -58,4 +68,10 @@ static inline uint64_t drm_vma_node_offset_addr(struct drm_vma_offset_node *node
return ((uint64_t) node->vm_node->start) << PAGE_SHIFT;
}
+int drm_vma_offset_node_add_file(struct drm_vma_offset_node *node,
+ struct file *filp);
+void drm_vma_offset_node_remove_file(struct drm_vma_offset_node *node,
+ struct file *filp);
+bool drm_vma_offset_node_valid_file(struct drm_vma_offset_node *node,
+ struct file *filp);
#endif