Bug in subr_bus_dma.c's bus_dmamap_load() when multiple dma maps are created from a single tag on x86

Hans Petter Selasky hps at selasky.org
Thu Jun 14 10:53:25 UTC 2018


On 06/14/18 10:59, Pratyush Yadav wrote:
> Hi everyone,
> 
> I was looking at the busdma code for x86 and I notice that the claim
> in the bus_dma(9) man page might not be correct: "Multiple maps can be
> associated with one DMA tag."
> 
> Looking at the code of sys/x86/x86/busdma_bounce.c (which I believe is
> the default busdma implementation for x86), I realize that a map does
> not necessarily use all the segments of the dma tag (specified by the
> parameter nsegments on tag creation).
> 
> So how many segments does a map actually use? Looking at
> busdma_bounce.c:644,690 it seems we can calculate that from the
> pointer segp. It contains the index of starting segment on entrace to
> the load function, and the ending segment on exit.
> 
> Sounds all fine so far. But then if we look at map_complete()
> (busdma_bounce.c:908), it returns the entire segments array of the dma
> *tag*. I emphasize tag because the tag's segments array holds the
> segments of all the maps created with the tag.
> 
> Going a layer up, sys/kern/subr_bus_dma.c:328 simply passes nsegs(=
> -1) in the segp parameter to load, not bothering to check if some
> segments have been used already by other maps. It then calculates the
> number of segments used using the value of nsegs after return and
> calls map_complete() to get the segments array. It then passes the
> segments array to the driver callback with the number of segments,
> nsegs.
> 
> Effectively, to the driver it seems that the map's segments start from
> index 0 and go till nsegs - 1. This is not true if another map has
> been loaded in the meantime with the same tag.
> 
> We are possibly over-writing the previous map's segments.
> busdma_bounce.c:669 simply uses segs[0] if the value of segp is -1,
> which is what bus_dmamap_load() in subr_bus_dma.c:328 passes to
> _bus_dmamap_load_buffer(). segs[0] would also be used by any
> subsequent map's load. This means the previous map's values are
> over-written.
> 
> This might cause problems when the driver is in its callback using the
> segments array and another map is loaded.
> 
> Unfortunately, I do not posses the knowledge or experience with
> FreeBSD's drivers (drivers in general) to test for this bug.
> 
> I have not looked at the code in other architectures. The same problem
> might present itself there as well.
> 
> If I made a mistake somewhere in my reasoning, let me know.


Hi,

Use of bus_dmamap_load() on the same tag must be serialized using a 
mutex. Maybe it is not so clearly documented. Else like you see the 
temporary segs[] list may be overwritten.

--HPS



More information about the freebsd-hackers mailing list