2013-03-11 21:23:38

by Kees Cook

[permalink] [raw]
Subject: [PATCH v2] drm/i915: bounds check execbuffer relocation count

It is possible to wrap the counter used to allocate the buffer for
relocation copies. This could lead to heap writing overflows.

Signed-off-by: Kees Cook <[email protected]>
Reported-by: Pinkie Pie
Cc: [email protected]
---
v2:
- move check into validate_exec_list
---
drivers/gpu/drm/i915/i915_gem_execbuffer.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 752e399..72d7841 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -732,6 +732,7 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
int count)
{
int i;
+ int total = 0;

for (i = 0; i < count; i++) {
char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr;
@@ -744,6 +745,9 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,
if (exec[i].relocation_count >
INT_MAX / sizeof(struct drm_i915_gem_relocation_entry))
return -EINVAL;
+ if (exec[i].relocation_count > INT_MAX - total)
+ return -ENOMEM;
+ total += exec[i].relocation_count;

length = exec[i].relocation_count *
sizeof(struct drm_i915_gem_relocation_entry);
--
1.7.9.5


--
Kees Cook
Chrome OS Security


2013-03-11 22:01:07

by Chris Wilson

[permalink] [raw]
Subject: Re: [PATCH v2] drm/i915: bounds check execbuffer relocation count

On Mon, Mar 11, 2013 at 02:23:29PM -0700, Kees Cook wrote:
> It is possible to wrap the counter used to allocate the buffer for
> relocation copies. This could lead to heap writing overflows.

I'd keep the return value as EINVAL so that we can continue to
distinguish between the user passing garbage and hitting an oom. And
total_relocs is preferrable to total, which also leads us to think more
carefully about the error condition. I think the check should be against
INT_MAX / sizeof(struct reloc_entry) for consistency with our other
guard against overflows whilst allocating.
-Chris

--
Chris Wilson, Intel Open Source Technology Centre

2013-03-11 22:26:01

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v2] drm/i915: bounds check execbuffer relocation count

On Mon, Mar 11, 2013 at 3:00 PM, Chris Wilson <[email protected]> wrote:
> On Mon, Mar 11, 2013 at 02:23:29PM -0700, Kees Cook wrote:
>> It is possible to wrap the counter used to allocate the buffer for
>> relocation copies. This could lead to heap writing overflows.
>
> I'd keep the return value as EINVAL so that we can continue to
> distinguish between the user passing garbage and hitting an oom. And
> total_relocs is preferrable to total, which also leads us to think more
> carefully about the error condition. I think the check should be against
> INT_MAX / sizeof(struct reloc_entry) for consistency with our other
> guard against overflows whilst allocating.

I've ended up with this:

int max_alloc = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry);
...
/* First check for malicious input causing overflow */
if (exec[i].relocation_count > max_alloc)
return -EINVAL;
if (exec[i].relocation_count > max_alloc - total_relocs)
return -EINVAL;
total_relocs += exec[i].relocation_count;

And looking at that, I wonder if we should just eliminate the first if entirely?

-Kees

--
Kees Cook
Chrome OS Security

2013-03-11 22:51:41

by Chris Wilson

[permalink] [raw]
Subject: Re: [PATCH v2] drm/i915: bounds check execbuffer relocation count

On Mon, Mar 11, 2013 at 03:25:59PM -0700, Kees Cook wrote:
> On Mon, Mar 11, 2013 at 3:00 PM, Chris Wilson <[email protected]> wrote:
> > On Mon, Mar 11, 2013 at 02:23:29PM -0700, Kees Cook wrote:
> >> It is possible to wrap the counter used to allocate the buffer for
> >> relocation copies. This could lead to heap writing overflows.
> >
> > I'd keep the return value as EINVAL so that we can continue to
> > distinguish between the user passing garbage and hitting an oom. And
> > total_relocs is preferrable to total, which also leads us to think more
> > carefully about the error condition. I think the check should be against
> > INT_MAX / sizeof(struct reloc_entry) for consistency with our other
> > guard against overflows whilst allocating.
>
> I've ended up with this:
>
> int max_alloc = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry);
> ...
> /* First check for malicious input causing overflow */
> if (exec[i].relocation_count > max_alloc)
> return -EINVAL;
> if (exec[i].relocation_count > max_alloc - total_relocs)
> return -EINVAL;
> total_relocs += exec[i].relocation_count;
>
> And looking at that, I wonder if we should just eliminate the first if entirely?

Aye, seems reasonable. So perhaps,

/* First check for malicious input causing overflow in the worst case
* where we need to allocate the entire relocation tree as a single
* array.
*/
-Chris

--
Chris Wilson, Intel Open Source Technology Centre