From cperciva@xps.daemonology.net  Sat Dec  4 02:20:06 2010
Return-Path: <cperciva@xps.daemonology.net>
Received: from mx2.freebsd.org (mx2.freebsd.org [IPv6:2001:4f8:fff6::35])
	by hub.freebsd.org (Postfix) with ESMTP id DF735106566C
	for <FreeBSD-gnats-submit@freebsd.org>; Sat,  4 Dec 2010 02:20:05 +0000 (UTC)
	(envelope-from cperciva@xps.daemonology.net)
Received: from xps.daemonology.net (freefall.freebsd.org [IPv6:2001:4f8:fff6::28])
	by mx2.freebsd.org (Postfix) with SMTP id D10B114DB58
	for <FreeBSD-gnats-submit@freebsd.org>; Sat,  4 Dec 2010 02:20:04 +0000 (UTC)
Received: (qmail 1048 invoked by uid 1001); 4 Dec 2010 02:20:04 -0000
Message-Id: <20101204022004.1047.qmail@xps.daemonology.net>
Date: 4 Dec 2010 02:20:04 -0000
From: Colin Percival <cperciva@freebsd.org>
Reply-To: Colin Percival <cperciva@freebsd.org>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [panic][xen] disk driver data cannot cross a page boundary
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         152818
>Category:       kern
>Synopsis:       [panic][xen] disk driver data cannot cross a page boundary
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-xen
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat Dec 04 02:20:12 UTC 2010
>Closed-Date:    Thu Dec 09 07:30:06 UTC 2010
>Last-Modified:  Thu Dec 09 07:30:06 UTC 2010
>Originator:     Colin Percival
>Release:        FreeBSD HEAD i386/XEN
>Organization:
>Environment:
FreeBSD HEAD (@ 2010-12-02), i386/XEN.
>Description:

The Xen blkfront driver panics with "XEN disk driver data cannot cross a
page boundary" when performing I/O to a buffer which is not sector-aligned
and starts just before a page boundary.

In blkif_queue_cb in blkfront.c, I/O is handled one page at a time, and
lines 1065--1067 attempt to map addresses in memory to sectors, but don't
acknowledge the fact that the memory buffer might not be aligned.

In addition to the panic message, it seems very likely that this could
cause data corruption (due to data being read/written from/to the wrong
part of a page) but I don't understand this code well enough to say.

>How-To-Repeat:

On a system where /dev/da0 is a Xen block device:

#include <fcntl.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char * argv[])
{
        char * buf = malloc(0x3000);
        char * buf2 = (char *)(((uintptr_t)buf + 0xfff) & ~0x1000);
        int fd = open("/dev/da0", O_RDONLY);

        read(fd, &buf2[0xf00], 0x200);

        return (0);
}

>Fix:
>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->freebsd-xen 
Responsible-Changed-By: cperciva 
Responsible-Changed-When: Sat Dec 4 02:22:30 UTC 2010 
Responsible-Changed-Why:  
Assign Xen bug to freebsd-xen for tracking. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152818 

From: Colin Percival <cperciva@freebsd.org>
To: bug-followup@FreeBSD.org, "Justin T. Gibbs" <gibbs@scsiguy.com>
Cc:  
Subject: Re: kern/152818: [panic][xen] disk driver data cannot cross a page
 boundary
Date: Sat, 04 Dec 2010 14:35:57 -0800

 It looks like this bug was fixed in busdma on amd64 by r204214 but never
 merged to i386.  Justin, is there a technical reason why this wasn't merged,
 or should I try to do this myself?
 
 -- 
 Colin Percival
 Security Officer, FreeBSD | freebsd.org | The power to serve
 Founder / author, Tarsnap | tarsnap.com | Online backups for the truly paranoid

From: "Justin T. Gibbs" <gibbs@scsiguy.com>
To: Colin Percival <cperciva@freebsd.org>
Cc: bug-followup@freebsd.org
Subject: Re: kern/152818: [panic][xen] disk driver data cannot cross a page
 boundary
Date: Sat, 04 Dec 2010 15:49:11 -0700

 On 12/4/2010 3:35 PM, Colin Percival wrote:
 > It looks like this bug was fixed in busdma on amd64 by r204214 but never
 > merged to i386.  Justin, is there a technical reason why this wasn't merged,
 > or should I try to do this myself?
 > 
 
 Oh.  That bug.  At the time I had difficulty getting an i386 image up and
 running to test.  If you have a test system, feel free to merge the change.
 
 --
 Justin

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/152818: commit references a PR
Date: Sun,  5 Dec 2010 03:21:00 +0000 (UTC)

 Author: cperciva
 Date: Sun Dec  5 03:20:55 2010
 New Revision: 216194
 URL: http://svn.freebsd.org/changeset/base/216194
 
 Log:
   MFamd64 r204214: Enforce stronger alignment semantics (require that the
   end of segments be aligned, not just the start of segments) in order to
   allow Xen's blkfront driver to operate correctly.
   
   PR:		kern/152818
   MFC after:	3 days
 
 Modified:
   head/sys/i386/i386/busdma_machdep.c
 
 Modified: head/sys/i386/i386/busdma_machdep.c
 ==============================================================================
 --- head/sys/i386/i386/busdma_machdep.c	Sun Dec  5 01:17:53 2010	(r216193)
 +++ head/sys/i386/i386/busdma_machdep.c	Sun Dec  5 03:20:55 2010	(r216194)
 @@ -246,8 +246,7 @@ bus_dma_tag_create(bus_dma_tag_t parent,
  	newtag->alignment = alignment;
  	newtag->boundary = boundary;
  	newtag->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
 -	newtag->highaddr = trunc_page((vm_paddr_t)highaddr) +
 -	    (PAGE_SIZE - 1);
 +	newtag->highaddr = trunc_page((vm_paddr_t)highaddr) + (PAGE_SIZE - 1);
  	newtag->filter = filter;
  	newtag->filterarg = filterarg;
  	newtag->maxsize = maxsize;
 @@ -594,15 +593,19 @@ _bus_dmamap_count_pages(bus_dma_tag_t dm
  		vendaddr = (vm_offset_t)buf + buflen;
  
  		while (vaddr < vendaddr) {
 +			bus_size_t sg_len;
 +
 +			sg_len = PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK);
  			if (pmap)
  				paddr = pmap_extract(pmap, vaddr);
  			else
  				paddr = pmap_kextract(vaddr);
  			if (((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
  			    run_filter(dmat, paddr) != 0) {
 +				sg_len = roundup2(sg_len, dmat->alignment);
  				map->pagesneeded++;
  			}
 -			vaddr += (PAGE_SIZE - ((vm_offset_t)vaddr & PAGE_MASK));
 +			vaddr += sg_len;
  		}
  		CTR1(KTR_BUSDMA, "pagesneeded= %d\n", map->pagesneeded);
  	}
 @@ -669,6 +672,8 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dm
  	bmask = ~(dmat->boundary - 1);
  
  	for (seg = *segp; buflen > 0 ; ) {
 +		bus_size_t max_sgsize;
 +
  		/*
  		 * Get the physical address for this segment.
  		 */
 @@ -680,11 +685,16 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dm
  		/*
  		 * Compute the segment size, and adjust counts.
  		 */
 -		sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
 -		if (sgsize > dmat->maxsegsz)
 -			sgsize = dmat->maxsegsz;
 -		if (buflen < sgsize)
 -			sgsize = buflen;
 +		max_sgsize = MIN(buflen, dmat->maxsegsz);
 +		sgsize = PAGE_SIZE - ((vm_offset_t)curaddr & PAGE_MASK);
 +		if (((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
 +		    map->pagesneeded != 0 && run_filter(dmat, curaddr)) {
 +			sgsize = roundup2(sgsize, dmat->alignment);
 +			sgsize = MIN(sgsize, max_sgsize);
 +			curaddr = add_bounce_page(dmat, map, vaddr, sgsize);
 +		} else {
 +			sgsize = MIN(sgsize, max_sgsize);
 +		}
  
  		/*
  		 * Make sure we don't cross any boundaries.
 @@ -695,10 +705,6 @@ _bus_dmamap_load_buffer(bus_dma_tag_t dm
  				sgsize = (baddr - curaddr);
  		}
  
 -		if (((dmat->flags & BUS_DMA_COULD_BOUNCE) != 0) &&
 -		    map->pagesneeded != 0 && run_filter(dmat, curaddr))
 -			curaddr = add_bounce_page(dmat, map, vaddr, sgsize);
 -
  		/*
  		 * Insert chunk into a segment, coalescing with
  		 * previous segment if possible.
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: cperciva 
State-Changed-When: Thu Dec 9 07:29:48 UTC 2010 
State-Changed-Why:  
Fix committed and MFCed to stable/8. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=152818 
>Unformatted:
