From hbb@catssrv.fokus.gmd.de  Fri Jan 31 08:15:42 2003
Return-Path: <hbb@catssrv.fokus.gmd.de>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id EEBD437B401
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Jan 2003 08:15:42 -0800 (PST)
Received: from mailhub.fokus.gmd.de (mailhub.fokus.gmd.de [193.174.154.14])
	by mx1.FreeBSD.org (Postfix) with ESMTP id AE17743E4A
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Jan 2003 08:15:41 -0800 (PST)
	(envelope-from hbb@catssrv.fokus.gmd.de)
Received: from catssrv.fokus.gmd.de (catssrv [192.168.229.23])
	by mailhub.fokus.gmd.de (8.11.6/8.11.6) with ESMTP id h0VGFYi24994
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Jan 2003 17:15:34 +0100 (MET)
Received: from catssrv.fokus.gmd.de (localhost [127.0.0.1])
	by catssrv.fokus.gmd.de (8.12.6/8.12.6) with ESMTP id h0VGFYRv028940
	for <FreeBSD-gnats-submit@freebsd.org>; Fri, 31 Jan 2003 17:15:34 +0100 (CET)
	(envelope-from hbb@catssrv.fokus.gmd.de)
Received: (from root@localhost)
	by catssrv.fokus.gmd.de (8.12.6/8.12.6/Submit) id h0VGFYHC028939;
	Fri, 31 Jan 2003 17:15:34 +0100 (CET)
	(envelope-from hbb)
Message-Id: <200301311615.h0VGFYHC028939@catssrv.fokus.gmd.de>
Date: Fri, 31 Jan 2003 17:15:34 +0100 (CET)
From: Hartmut Brandt <brandt@fokus.fraunhofer.de>
Reply-To: Hartmut Brandt <brandt@fokus.fraunhofer.de>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: bus_dmamap_load_{mbuf,uio} broken if first buffer has zero length
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         47733
>Category:       kern
>Synopsis:       bus_dmamap_load_{mbuf,uio} broken if first buffer has zero length
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jan 31 08:20:11 PST 2003
>Closed-Date:    Tue Feb 04 08:34:37 PST 2003
>Last-Modified:  Tue Feb 04 08:34:37 PST 2003
>Originator:     Hartmut Brandt
>Release:        FreeBSD 5.0-CURRENT sparc64
>Organization:
Fraunhofer Fokus
>Environment:
System: FreeBSD catssrv.fokus.gmd.de 5.0-CURRENT FreeBSD 5.0-CURRENT #18: Tue Jan 28 11:49:32 CET 2003 hbb@catssrv.fokus.gmd.de:/opt/obj/usr/src/sys/CATSSRV sparc64


	
>Description:

On all platforms bus_dmamap_load_{mbuf,uio} call bus_dmamap_load_buffer in
a loop for each element. They maintain a variable 'first' which is set for
the first element and cleared otherwise. This variable is passed to
_load_buffer and in some circumstances this function instead of allocating
a new DMA segment makes the previous one larger (if this is not the first
element and the physical addresses are adjacent). If the first element
(first mbuf or uio segment) has a zero size 'first' gets cleared although
no segment was allocated by _load_buffer. If the physical addresses in
the first elements are adjacent this results in _load_buffer clobbering
the segment descriptors.

	
>How-To-Repeat:

Pass an mbuf chain where the first mbuf has m_len == 0 to an interface driver
that uses bus_dmamap_load_mbuf. Under some circumstances this results in
a hang or panic or other effects.
	
>Fix:

Apply the attached patches. It appears, that the problem is already fixed
in the sparc/iommu code, but not in sparc/nexus and not on all other platforms.
The i386 patch has been tested. The others not.

Index: sys/alpha/alpha/busdma_machdep.c
===================================================================
RCS file: /home/cvs/freebsd/src/sys/alpha/alpha/busdma_machdep.c,v
retrieving revision 1.25
diff -c -r1.25 busdma_machdep.c
*** sys/alpha/alpha/busdma_machdep.c	21 Jan 2003 08:55:22 -0000	1.25
--- sys/alpha/alpha/busdma_machdep.c	31 Jan 2003 16:05:44 -0000
***************
*** 658,668 ****
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			error = _bus_dmamap_load_buffer(dmat,
! 					dm_segments,
! 					m->m_data, m->m_len,
! 					NULL, flags, &lastaddr, &nsegs, first);
! 			first = 0;
  		}
  	} else {
  		error = EINVAL;
--- 658,671 ----
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			if (m->m_len > 0) {
! 				error = _bus_dmamap_load_buffer(dmat,
! 						dm_segments,
! 						m->m_data, m->m_len,
! 						NULL, flags, &lastaddr,
! 						&nsegs, first);
! 				first = 0;
! 			}
  		}
  	} else {
  		error = EINVAL;
***************
*** 722,734 ****
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		error = _bus_dmamap_load_buffer(dmat,
! 				dm_segments,
! 				addr, minlen,
! 				td, flags, &lastaddr, &nsegs, first);
! 		first = 0;
  
! 		resid -= minlen;
  	}
  
  	if (error) {
--- 725,739 ----
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		if (minlen > 0) {
! 			error = _bus_dmamap_load_buffer(dmat,
! 					dm_segments,
! 					addr, minlen,
! 					td, flags, &lastaddr, &nsegs, first);
! 			first = 0;
  
! 			resid -= minlen;
! 		}
  	}
  
  	if (error) {
Index: sys/i386/i386/busdma_machdep.c
===================================================================
RCS file: /home/cvs/freebsd/src/sys/i386/i386/busdma_machdep.c,v
retrieving revision 1.29
diff -c -r1.29 busdma_machdep.c
*** sys/i386/i386/busdma_machdep.c	21 Jan 2003 08:55:50 -0000	1.29
--- sys/i386/i386/busdma_machdep.c	31 Jan 2003 16:05:44 -0000
***************
*** 616,626 ****
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			error = _bus_dmamap_load_buffer(dmat,
! 					dm_segments,
! 					m->m_data, m->m_len,
! 					NULL, flags, &lastaddr, &nsegs, first);
! 			first = 0;
  		}
  	} else {
  		error = EINVAL;
--- 616,628 ----
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			if (m->m_len > 0) {
! 				error = _bus_dmamap_load_buffer(dmat,
! 						dm_segments,
! 						m->m_data, m->m_len,
! 						NULL, flags, &lastaddr, &nsegs, first);
! 				first = 0;
! 			}
  		}
  	} else {
  		error = EINVAL;
***************
*** 680,692 ****
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		error = _bus_dmamap_load_buffer(dmat,
! 				dm_segments,
! 				addr, minlen,
! 				td, flags, &lastaddr, &nsegs, first);
! 		first = 0;
  
! 		resid -= minlen;
  	}
  
  	if (error) {
--- 682,696 ----
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		if (minlen > 0) {
! 			error = _bus_dmamap_load_buffer(dmat,
! 					dm_segments,
! 					addr, minlen,
! 					td, flags, &lastaddr, &nsegs, first);
! 			first = 0;
  
! 			resid -= minlen;
! 		}
  	}
  
  	if (error) {
Index: sys/powerpc/powerpc/busdma_machdep.c
===================================================================
RCS file: /home/cvs/freebsd/src/sys/powerpc/powerpc/busdma_machdep.c,v
retrieving revision 1.5
diff -c -r1.5 busdma_machdep.c
*** sys/powerpc/powerpc/busdma_machdep.c	27 Jan 2003 04:27:01 -0000	1.5
--- sys/powerpc/powerpc/busdma_machdep.c	31 Jan 2003 16:05:44 -0000
***************
*** 423,432 ****
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			error = bus_dmamap_load_buffer(dmat, dm_segments,
! 			    m->m_data, m->m_len, NULL, flags,
! 			    &lastaddr, &nsegs, first);
! 			first = 0;
  		}
  	} else {
  		error = EINVAL;
--- 423,434 ----
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			if (m->m_len > 0) {
! 				error = bus_dmamap_load_buffer(dmat,
! 				    dm_segments, m->m_data, m->m_len, NULL,
! 				    flags, &lastaddr, &nsegs, first);
! 				first = 0;
! 			}
  		}
  	} else {
  		error = EINVAL;
***************
*** 483,494 ****
  		    resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		error = bus_dmamap_load_buffer(dmat, dm_segments, addr,
! 		    minlen, td, flags, &lastaddr, &nsegs, first);
  
! 		first = 0;
  
! 		resid -= minlen;
  	}
  
  	if (error) {
--- 485,498 ----
  		    resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		if (minlen > 0) {
! 			error = bus_dmamap_load_buffer(dmat, dm_segments, addr,
! 			    minlen, td, flags, &lastaddr, &nsegs, first);
  
! 			first = 0;
  
! 			resid -= minlen;
! 		}
  	}
  
  	if (error) {
Index: sys/sparc64/sparc64/bus_machdep.c
===================================================================
RCS file: /home/cvs/freebsd/src/sys/sparc64/sparc64/bus_machdep.c,v
retrieving revision 1.16
diff -c -r1.16 bus_machdep.c
*** sys/sparc64/sparc64/bus_machdep.c	21 Jan 2003 08:56:14 -0000	1.16
--- sys/sparc64/sparc64/bus_machdep.c	31 Jan 2003 16:05:44 -0000
***************
*** 448,457 ****
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			error = _nexus_dmamap_load_buffer(ddmat,
! 			    dm_segments, m->m_data, m->m_len, NULL, flags,
! 			    &lastaddr, &nsegs, first);
! 			first = 0;
  		}
  	} else {
  		error = EINVAL;
--- 448,459 ----
  		struct mbuf *m;
  
  		for (m = m0; m != NULL && error == 0; m = m->m_next) {
! 			if (m->m_len > 0) {
! 				error = _nexus_dmamap_load_buffer(ddmat,
! 				    dm_segments, m->m_data, m->m_len, NULL,
! 				    flags, &lastaddr, &nsegs, first);
! 				first = 0;
! 			}
  		}
  	} else {
  		error = EINVAL;
***************
*** 508,518 ****
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		error = _nexus_dmamap_load_buffer(ddmat, dm_segments, addr,
! 		    minlen, td, flags, &lastaddr, &nsegs, first);
! 		first = 0;
  
! 		resid -= minlen;
  	}
  
  	if (error) {
--- 510,522 ----
  			resid < iov[i].iov_len ? resid : iov[i].iov_len;
  		caddr_t addr = (caddr_t) iov[i].iov_base;
  
! 		if (minlen > 0) {
! 			error = _nexus_dmamap_load_buffer(ddmat, dm_segments,
! 			    addr, minlen, td, flags, &lastaddr, &nsegs, first);
! 			first = 0;
  
! 			resid -= minlen;
! 		}
  	}
  
  	if (error) {
	


>Release-Note:
>Audit-Trail:
State-Changed-From-To: open->closed 
State-Changed-By: harti 
State-Changed-When: Tue Feb 4 08:33:27 PST 2003 
State-Changed-Why:  
Patch applied. ia64 suffers from the same problem and was also fixed. 

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