Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 31 Jan 2003 17:15:34 +0100 (CET)
From:      Hartmut Brandt <brandt@fokus.fraunhofer.de>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   kern/47733: bus_dmamap_load_{mbuf,uio} broken if first buffer has zero length
Message-ID:  <200301311615.h0VGFYHC028939@catssrv.fokus.gmd.de>

next in thread | raw e-mail | index | archive | help

>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:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Fri Jan 31 08:20:11 PST 2003
>Closed-Date:
>Last-Modified:
>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:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200301311615.h0VGFYHC028939>