Commit 4b5d2381 authored by Dave Airlie's avatar Dave Airlie

drm: fix race condition in radeon driver

Close a race which could allow for privilege escalation by users with DRI
privileges on Radeon hardware.  Essentially, a malicious program could submit
a packet containing an offset (possibly in main memory) to be rendered from/to,
while a separate thread switched that offset in userspace rapidly between a
valid value and an invalid one.  radeon_check_and_fixup_offset() would pull the
offset in from user space, check it, and spit it back out to user space to be
copied in later by the emit code.  It would sometimes catch the bad value, but
sometimes the malicious program could modify it after the check and get an
invalid offset rendered from/to.

Fix this by allocating a temporary buffer and copying the data in at once.
While here, make the cliprects stuff not do the VERIFYAREA_READ and
COPY_FROM_USER_UNCHECKED gymnastics, avoiding a lock order reversal on FreeBSD.
Performance impact is negligible  -- no difference on r200 to ~1% improvement on
rv200 in quake3 tests (P4 1Ghz, demofour at 1024x768, n=4 or 5)

From: Eric Anholt <anholt@freebsd.org>
Signed-off-by: default avatarDave Airlie <airlied@linux.ie>
parent 149d5cef
......@@ -96,9 +96,6 @@ static __inline__ int mtrr_del (int reg, unsigned long base,
__copy_to_user(arg1, arg2, arg3)
#define DRM_GET_USER_UNCHECKED(val, uaddr) \
__get_user(val, uaddr)
#define DRM_PUT_USER_UNCHECKED(uaddr, val) \
__put_user(val, uaddr)
#define DRM_GET_PRIV_WITH_RETURN(_priv, _filp) _priv = _filp->private_data
......
......@@ -1027,25 +1027,27 @@ do { \
} while (0)
#define OUT_RING_USER_TABLE( tab, sz ) do { \
#define OUT_RING_TABLE( tab, sz ) do { \
int _size = (sz); \
int __user *_tab = (tab); \
int *_tab = (int *)(tab); \
\
if (write + _size > mask) { \
int i = (mask+1) - write; \
if (DRM_COPY_FROM_USER_UNCHECKED( (int *)(ring+write), \
_tab, i*4 )) \
return DRM_ERR(EFAULT); \
int _i = (mask+1) - write; \
_size -= _i; \
while (_i > 0 ) { \
*(int *)(ring + write) = *_tab++; \
write++; \
_i--; \
} \
write = 0; \
_size -= i; \
_tab += i; \
_tab += _i; \
} \
\
if (_size && DRM_COPY_FROM_USER_UNCHECKED( (int *)(ring+write), \
_tab, _size*4 )) \
return DRM_ERR(EFAULT); \
\
write += _size; \
while (_size > 0) { \
*(ring + write) = *_tab++; \
write++; \
_size--; \
} \
write &= mask; \
} while (0)
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment