LOR when running google-earth

Kostik Belousov kostikbel at gmail.com
Tue Jul 17 12:46:20 UTC 2007


On Tue, Jul 17, 2007 at 07:47:03PM +0800, Ganbold wrote:
> Kostik Belousov wrote:
> >On Tue, Jul 17, 2007 at 12:59:56PM +0800, Ganbold wrote:
> >  
> >>Thanks for the patch, Konstantin.
> >>It seems like LOR is gone after the patch.
> >>However I got following when I tried to switch virtual desktops under 
> >>beryl:
> >>
> >>error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> >>error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
> >>error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> >>error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
> >>error: [drm:pid1157:drm_close] *ERROR* can't find authenticator
> >>
> >>On shell where I'm running googleearth it shows:
> >>
> >>DRM_I830_CMDBUFFER:  -22
> >>
> >>and googleearth crashes :(
> >>
> >>Maybe above problems are related to beryl/emerald.
> >>
> >>    
> >Ganbold,
> >does the problems you described show only with patch applied ? Or, they are
> >reproducable without patch, on the stock kernel ?
> >  
> Konstantin,
> 
> I just tested stock i915 module. googleearth crashes with same message:
> 
> error: [drm:pid1:i915_emit_box] *ERROR* Bad box 64485,100..0,774
> error: [drm:pid1:i915_cmdbuffer] *ERROR* i915_dispatch_cmdbuffer failed
> 
> DRM_I830_CMDBUFFER: -22
> 
> So my guess was correct.
> 
> thanks,
> 
> Ganbold

Eric,
could you, please, review some further change to the i915 drm driver.

The LOR reported by Ganbold happens when useracc() checks the range of the
user address space in the dev/drm/i915_dma.c, using the DRM_VERIFYAREA_READ()
macro. It calls useracc(), that locks sx lock for user map, while holding
drm mutex.

lock order reversal: (sleepable after non-sleepable)
 1st 0xc3c06cd8 drm device (drm device) @ 
/usr/src/sys/modules/drm/drm/../../../dev/drm/drm_drv.c:907
 2nd 0xc3e6ea3c user map (user map) @ /usr/src/sys/vm/vm_glue.c:182
KDB: stack backtrace:
db_trace_self_wrapper(c08a3fa4,e6450a44,c066992e,c08a6485,c3e6ea3c,...) 
at db_trace_self_wrapper+0x26
kdb_backtrace(c08a6485,c3e6ea3c,c08be517,c08be517,c08bdfed,...) at 
kdb_backtrace+0x29
witness_checkorder(c3e6ea3c,9,c08bdfed,b6,c435a000,...) at 
witness_checkorder+0x6de
_sx_xlock(c3e6ea3c,0,c08bdfed,b6,e6450aa8,...) at _sx_xlock+0x7d
_vm_map_lock_read(c3e6e9f8,c08bdfed,b6,c435a000,a60,...) at 
_vm_map_lock_read+0x50
useracc(4ce1ede8,8,1,f5,c09ce1bc,...) at useracc+0x65
i915_cmdbuffer(c3bf3800,8018644b,c43814a0,3,c435a000,...) at 
i915_cmdbuffer+0x10f
drm_ioctl(c3bf3800,8018644b,c43814a0,3,c435a000,...) at drm_ioctl+0x357
giant_ioctl(c3bf3800,8018644b,c43814a0,3,c435a000,...) at giant_ioctl+0x56
devfs_ioctl_f(c4cdd1b0,8018644b,c43814a0,c4356700,c435a000,...) at 
devfs_ioctl_f+0xc9
kern_ioctl(c435a000,9,8018644b,c43814a0,450c4c,...) at kern_ioctl+0x243
ioctl(c435a000,e6450cfc,e6450c80,c4022e4a,c435a000,...) at ioctl+0x134
linux_ioctl_drm(c435a000,e6450cfc,c4030c45,a2f,c435a000,...) at 
linux_ioctl_drm+0x2f
linux_ioctl(c435a000,e6450cfc,e6450cf8,e6450d1c,c4032dd0,...) at 
linux_ioctl+0xca
syscall(e6450d38) at syscall+0x2b3
Xint0x80_syscall() at Xint0x80_syscall+0x20

In fact, besides the LOR, there are two bigger problems:
1. the check is racy, since userspace may modity the address space after
   the check is done, but before the actual access.
2. useracc() does not page in the requested address, thus further LOR may
   happen while vm_fault() would handle the page in.

The same LOR could occur due to use of DRM_COPY_FROM_USER_UNCHECKED(), if the
page is swapped out.

Patch below tries to fix the problem for i915 driver (and it looks like these
are all consumers of the DRM_VERIFY_AREA_READ) by wiring the user pages with
the help of vslock(). Also, I eliminated DRM_COPY_FROM_USER_UNCHECKED.

The obvious drawbacks of the patch are:
1. It further changes the drm code, making the FreeBSD fork of it.
2. vslock() causes user map fragmentation by clipping the wiring map entry
   to the wired region.
3. The only vslock() user in the CVS tree is sysctl(). I am not sure whether
   adding another one is good.

diff --git a/sys/dev/drm/i915_dma.c b/sys/dev/drm/i915_dma.c
index dfaf334..c9dd799 100644
--- a/sys/dev/drm/i915_dma.c
+++ b/sys/dev/drm/i915_dma.c
@@ -367,20 +367,14 @@ static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords)
 	for (i = 0; i < dwords;) {
 		int cmd, sz;
 
-	     if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) {
-
-			return DRM_ERR(EINVAL);
-	      }
+		cmd = buffer[i];
 		if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)
 			return DRM_ERR(EINVAL);
 
 		OUT_RING(cmd);
 
 		while (++i, --sz) {
-			if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],
-							 sizeof(cmd))) {
-				return DRM_ERR(EINVAL);
-			}
+			cmd = buffer[i];
 			OUT_RING(cmd);
 		}
 	}
@@ -401,10 +395,7 @@ static int i915_emit_box(drm_device_t * dev,
 	drm_clip_rect_t box;
 	RING_LOCALS;
 
-	if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
-		return EFAULT;
-	}
-
+	box = boxes[i];
 	if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {
 		DRM_ERROR("Bad box %d,%d..%d,%d\n",
 			  box.x1, box.y1, box.x2, box.y2);
@@ -604,6 +595,7 @@ static int i915_batchbuffer(DRM_IOCTL_ARGS)
 	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
 	    dev_priv->sarea_priv;
 	drm_i915_batchbuffer_t batch;
+	size_t cliplen;
 	int ret;
 
 	if (!dev_priv->allow_batchbuffer) {
@@ -619,14 +611,25 @@ static int i915_batchbuffer(DRM_IOCTL_ARGS)
 
 	LOCK_TEST_WITH_RETURN(dev, filp);
 
+	DRM_UNLOCK();
+	cliplen = batch.num_cliprects * sizeof(drm_clip_rect_t);	
 	if (batch.num_cliprects && DRM_VERIFYAREA_READ(batch.cliprects,
-						       batch.num_cliprects *
-						       sizeof(drm_clip_rect_t)))
+						       cliplen)) {
+		DRM_LOCK();
 		return DRM_ERR(EFAULT);
-
+	}
+	ret = vslock(batch.cliprects, cliplen);
+	if (ret) {
+		DRM_ERROR("Fault wiring cliprects\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
+	}
+	DRM_LOCK();
 	ret = i915_dispatch_batchbuffer(dev, &batch);
-
 	sarea_priv->last_dispatch = (int)hw_status[5];
+	DRM_UNLOCK();
+	vsunlock(batch.cliprects, cliplen);
+	DRM_LOCK();
 	return ret;
 }
 
@@ -638,6 +641,7 @@ static int i915_cmdbuffer(DRM_IOCTL_ARGS)
 	drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *)
 	    dev_priv->sarea_priv;
 	drm_i915_cmdbuffer_t cmdbuf;
+	size_t cliplen;
 	int ret;
 
 	DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_i915_cmdbuffer_t __user *) data,
@@ -648,22 +652,38 @@ static int i915_cmdbuffer(DRM_IOCTL_ARGS)
 
 	LOCK_TEST_WITH_RETURN(dev, filp);
 
+	DRM_UNLOCK();
+	cliplen = cmdbuf.num_cliprects * sizeof(drm_clip_rect_t);
 	if (cmdbuf.num_cliprects &&
-	    DRM_VERIFYAREA_READ(cmdbuf.cliprects,
-				cmdbuf.num_cliprects *
-				sizeof(drm_clip_rect_t))) {
+	    DRM_VERIFYAREA_READ(cmdbuf.cliprects, cliplen)) {
 		DRM_ERROR("Fault accessing cliprects\n");
+		DRM_LOCK();
 		return DRM_ERR(EFAULT);
 	}
-
-	ret = i915_dispatch_cmdbuffer(dev, &cmdbuf);
+	ret = vslock(cmdbuf.cliprects, cliplen);
 	if (ret) {
-		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
-		return ret;
+		DRM_ERROR("Fault wiring cliprects\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
 	}
-
-	sarea_priv->last_dispatch = (int)hw_status[5];
-	return 0;
+	ret = vslock(cmdbuf.buf, cmdbuf.sz);
+	if (ret) {
+		vsunlock(cmdbuf.cliprects, cliplen);
+		DRM_ERROR("Fault wiring cmds\n");
+		DRM_LOCK();
+		return DRM_ERR(EFAULT);
+	}
+	DRM_LOCK();
+	ret = i915_dispatch_cmdbuffer(dev, &cmdbuf);
+	if (ret == 0)
+		sarea_priv->last_dispatch = (int)hw_status[5];
+	else
+		DRM_ERROR("i915_dispatch_cmdbuffer failed\n");
+	DRM_UNLOCK();
+	vsunlock(cmdbuf.buf, cmdbuf.sz);
+	vsunlock(cmdbuf.cliprects, cliplen);
+	DRM_LOCK();
+	return (ret);
 }
 
 static int i915_do_cleanup_pageflip(drm_device_t * dev)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 187 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-x11/attachments/20070717/4a8b5b38/attachment.pgp


More information about the freebsd-x11 mailing list