From jeremyp@gsmx07.alcatel.com.au  Thu Jan  6 14:58:51 2000
Return-Path: <jeremyp@gsmx07.alcatel.com.au>
Received: from alcanet.com.au (border.alcanet.com.au [203.62.196.10])
	by hub.freebsd.org (Postfix) with ESMTP id 16CDC156FC
	for <FreeBSD-gnats-submit@FreeBSD.ORG>; Thu,  6 Jan 2000 14:58:44 -0800 (PST)
	(envelope-from jeremyp@gsmx07.alcatel.com.au)
Received: by border.alcanet.com.au id <40327>; Fri, 7 Jan 2000 09:51:15 +1100
Message-Id: <00Jan7.095115est.40327@border.alcanet.com.au>
Date: Fri, 7 Jan 2000 09:51:14 +1100
From: peter.jeremy@alcatel.com.au
Sender: jeremyp@gsmx07.alcatel.com.au
To: freefall-gnats@gsmx07.alcatel.com.au
Subject: Off-by-1 error in diskstrategy() triggers bug in ATA
X-Send-Pr-Version: 3.2

>Number:         15956
>Category:       kern
>Synopsis:       Off-by-1 error in diskstrategy()
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jan  6 15:00:01 PST 2000
>Closed-Date:    Mon Jan 10 04:21:47 PST 2000
>Last-Modified:  Mon Jan 10 04:21:59 PST 2000
>Originator:     Peter Jeremy
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
>Environment:

	cvs-cur 5961

>Description:

	The I/O request range check in /sys/kern/subr_disk.c does not
	correctly handle an EOF return from dscheck() - instead of
	terminating the I/O (returning EOF to the caller), it passes a
	zero-length request to the underlying d_strategy() - which
	at least for the ATA device can't handle it.

>How-To-Repeat:

	# dd if=/dev/rad0c of=/dev/null bs=128k
	will report an error at the end of the slice (even if the slice
	is a multiple of 128k).  The problem does not occur with the
	old wd driver (and it shouldn't appear with the wfd, ida or vn
	drivers), but appears to affect all other disk devices.

>Fix:

	1) Don't pass zero-length requests to the underlying device:
Index: subr_disk.c
===================================================================
RCS file: /home/CVSROOT/src/sys/kern/subr_disk.c,v
retrieving revision 1.16
diff -u -r1.16 subr_disk.c
--- subr_disk.c	1999/12/19 12:36:41	1.16
+++ subr_disk.c	2000/01/06 21:39:34
@@ -193,7 +193,7 @@
 		return;
 	}
 
-	if (dscheck(bp, dp->d_slice) < 0) {
+	if (dscheck(bp, dp->d_slice) <= 0) {
 		biodone(bp);
 		return;
 	}

	2) Add belt-and-braces checks to ATA device (this code compiles
	   and links, but I haven't booted from the resultant kernel):
Index: ata-disk.c
===================================================================
RCS file: /home/CVSROOT/src/sys/dev/ata/ata-disk.c,v
retrieving revision 1.47
diff -u -r1.47 ata-disk.c
--- ata-disk.c	1999/12/21 20:18:55	1.47
+++ ata-disk.c	2000/01/06 22:13:09
@@ -301,6 +301,9 @@
     struct ad_softc *adp = bp->b_dev->si_drv1;
     int32_t s;
 
+    if (!bp || !bp->b_bcount)
+	return;
+
     s = splbio();
     bufqdisksort(&adp->queue, bp);
     ad_start(adp);
@@ -375,7 +378,7 @@
     struct buf *bp = bufq_first(&adp->queue);
     struct ad_request *request;
 
-    if (!bp)
+    if (!bp || !bp->b_bcount)
 	return;
 
     if (!(request = malloc(sizeof(struct ad_request), M_AD, M_NOWAIT))) {

>Release-Note:
>Audit-Trail:

From: Poul-Henning Kamp <phk@critter.freebsd.dk>
To: peter.jeremy@alcatel.com.au
Cc: freefall-gnats@gsmx07.alcatel.com.au
Subject: Re: kern/15956: Off-by-1 error in diskstrategy() triggers bug in ATA
Date: Fri, 7 Jan 2000 22:57:35 +1100

 In message <00Jan7.095115est.40327@border.alcanet.com.au>, peter.jeremy@alcatel
 .com.au writes:
 
 >Index: subr_disk.c
 >===================================================================
 >RCS file: /home/CVSROOT/src/sys/kern/subr_disk.c,v
 >retrieving revision 1.16
 >diff -u -r1.16 subr_disk.c
 >--- subr_disk.c	1999/12/19 12:36:41	1.16
 >+++ subr_disk.c	2000/01/06 21:39:34
 >@@ -193,7 +193,7 @@
 > 		return;
 > 	}
 > 
 >-	if (dscheck(bp, dp->d_slice) < 0) {
 >+	if (dscheck(bp, dp->d_slice) <= 0) {
 > 		biodone(bp);
 > 		return;
 > 	}
 >
 >	2) Add belt-and-braces checks to ATA device (this code compiles
 >	   and links, but I haven't booted from the resultant kernel):
 
 This was actually done that way deliberately, but not for any
 specific reason.
 
 Input welcome...
 
 --
 Poul-Henning Kamp             FreeBSD coreteam member
 phk@FreeBSD.ORG               "Real hackers run -current on their laptop."
 FreeBSD -- It will take a long time before progress goes too far!
 

From: Peter Jeremy <peter.jeremy@alcatel.com.au>
To: Poul-Henning Kamp <phk@critter.freebsd.dk>
Cc: FreeBSD-gnats-submit@FreeBSD.ORG
Subject: Re: kern/15956: Off-by-1 error in diskstrategy() triggers bug in ATA
Date: Mon, 10 Jan 2000 10:39:40 +1100

 On 2000-Jan-07 22:57:11 +1100, Poul-Henning Kamp <phk@critter.freebsd.dk> wrote:
 >>-	if (dscheck(bp, dp->d_slice) < 0) {
 >>+	if (dscheck(bp, dp->d_slice) <= 0) {
 ...
 >This was actually done that way deliberately, but not for any
 >specific reason.
 
 I wasn't sure about the reasons.  What I did notice was that the other
 references to dscheck() all do a <= check:
 
 /sys/dev/ida/ida_disk.c:205:        if (dscheck(bp, drv->slices) <= 0)
 /sys/dev/vn/vn.c:305:               if (vn->sc_slices != NULL && dscheck(bp, vn->sc_slices) <= 0) {
 /sys/i386/isa/wd.c:581:     if (dscheck(bp, du->dk_slices) <= 0)
 /sys/i386/isa/wfd.c:419:    if (dscheck(bp, t->dk_slices) <= 0) {
 /sys/pc98/pc98/wd.c:675:    if (dscheck(bp, du->dk_slices) <= 0)
 
 Also, I can't see any point in passing zero-length I/O requests into
 the low level drivers - bouncing them back out as quick as possible
 will (if anything) improve performance.
 
 I notice Soren has since added a (correct, unlike mine) patch to
 the ata drivers to catch zero-length requests.
 
 Peter
 
State-Changed-From-To: open->closed 
State-Changed-By: phk 
State-Changed-When: Mon Jan 10 04:21:47 PST 2000 
State-Changed-Why:  
fixed. 
>Unformatted:
