Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fd567467 authored by Thomas Hellstrom's avatar Thomas Hellstrom
Browse files

drm/vmwgfx: Protect from excessive execbuf kernel memory allocations v3



With the new validation code, a malicious user-space app could
potentially submit command streams with enough buffer-object and resource
references in them to have the resulting allocated validion nodes and
relocations make the kernel run out of GFP_KERNEL memory.

Protect from this by having the validation code reserve TTM graphics
memory when allocating.

Signed-off-by: default avatarThomas Hellstrom <thellstrom@vmware.com>
Reviewed-by: default avatarDeepak Rawat <drawat@vmware.com>
---
v2: Removed leftover debug printouts
parent e06d45d8
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@

#define VMWGFX_REPO "In Tree"

#define VMWGFX_VALIDATION_MEM_GRAN (16*PAGE_SIZE)


/**
 * Fully encoded drm commands. Might move to vmw_drm.h
@@ -918,7 +920,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
		spin_unlock(&dev_priv->cap_lock);
	}


	vmw_validation_mem_init_ttm(dev_priv, VMWGFX_VALIDATION_MEM_GRAN);
	ret = vmw_kms_init(dev_priv);
	if (unlikely(ret != 0))
		goto out_no_kms;
+5 −0
Original line number Diff line number Diff line
@@ -606,6 +606,9 @@ struct vmw_private {

	struct vmw_cmdbuf_man *cman;
	DECLARE_BITMAP(irqthread_pending, VMW_IRQTHREAD_MAX);

	/* Validation memory reservation */
	struct vmw_validation_mem vvm;
};

static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res)
@@ -846,6 +849,8 @@ extern int vmw_ttm_global_init(struct vmw_private *dev_priv);
extern void vmw_ttm_global_release(struct vmw_private *dev_priv);
extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);

extern void vmw_validation_mem_init_ttm(struct vmw_private *dev_priv,
					size_t gran);
/**
 * TTM buffer object driver - vmwgfx_ttm_buffer.c
 */
+2 −0
Original line number Diff line number Diff line
@@ -3835,6 +3835,8 @@ int vmw_execbuf_process(struct drm_file *file_priv,
	struct sync_file *sync_file = NULL;
	DECLARE_VAL_CONTEXT(val_ctx, &sw_context->res_ht, 1);

	vmw_validation_set_val_mem(&val_ctx, &dev_priv->vvm);

	if (flags & DRM_VMW_EXECBUF_FLAG_EXPORT_FENCE_FD) {
		out_fence_fd = get_unused_fd_flags(O_CLOEXEC);
		if (out_fence_fd < 0) {
+36 −0
Original line number Diff line number Diff line
@@ -96,3 +96,39 @@ void vmw_ttm_global_release(struct vmw_private *dev_priv)
	drm_global_item_unref(&dev_priv->bo_global_ref.ref);
	drm_global_item_unref(&dev_priv->mem_global_ref);
}

/* struct vmw_validation_mem callback */
static int vmw_vmt_reserve(struct vmw_validation_mem *m, size_t size)
{
	static struct ttm_operation_ctx ctx = {.interruptible = false,
					       .no_wait_gpu = false};
	struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);

	return ttm_mem_global_alloc(vmw_mem_glob(dev_priv), size, &ctx);
}

/* struct vmw_validation_mem callback */
static void vmw_vmt_unreserve(struct vmw_validation_mem *m, size_t size)
{
	struct vmw_private *dev_priv = container_of(m, struct vmw_private, vvm);

	return ttm_mem_global_free(vmw_mem_glob(dev_priv), size);
}

/**
 * vmw_validation_mem_init_ttm - Interface the validation memory tracker
 * to ttm.
 * @dev_priv: Pointer to struct vmw_private. The reason we choose a vmw private
 * rather than a struct vmw_validation_mem is to make sure assumption in the
 * callbacks that struct vmw_private derives from struct vmw_validation_mem
 * holds true.
 * @gran: The recommended allocation granularity
 */
void vmw_validation_mem_init_ttm(struct vmw_private *dev_priv, size_t gran)
{
	struct vmw_validation_mem *vvm = &dev_priv->vvm;

	vvm->reserve_mem = vmw_vmt_reserve;
	vvm->unreserve_mem = vmw_vmt_unreserve;
	vvm->gran = gran;
}
+20 −1
Original line number Diff line number Diff line
@@ -104,11 +104,25 @@ void *vmw_validation_mem_alloc(struct vmw_validation_context *ctx,
		return NULL;

	if (ctx->mem_size_left < size) {
		struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
		struct page *page;

		if (ctx->vm && ctx->vm_size_left < PAGE_SIZE) {
			int ret = ctx->vm->reserve_mem(ctx->vm, ctx->vm->gran);

			if (ret)
				return NULL;

			ctx->vm_size_left += ctx->vm->gran;
			ctx->total_mem += ctx->vm->gran;
		}

		page = alloc_page(GFP_KERNEL | __GFP_ZERO);
		if (!page)
			return NULL;

		if (ctx->vm)
			ctx->vm_size_left -= PAGE_SIZE;

		list_add_tail(&page->lru, &ctx->page_list);
		ctx->page_address = page_address(page);
		ctx->mem_size_left = PAGE_SIZE;
@@ -138,6 +152,11 @@ static void vmw_validation_mem_free(struct vmw_validation_context *ctx)
	}

	ctx->mem_size_left = 0;
	if (ctx->vm && ctx->total_mem) {
		ctx->vm->unreserve_mem(ctx->vm, ctx->total_mem);
		ctx->total_mem = 0;
		ctx->vm_size_left = 0;
	}
}

/**
Loading