Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Revert "swiotlb: rework "fix info leak with DMA_FROM_DEVICE""

This reverts commit aa6f8dcbab473f3a3c7454b74caa46d36cdc5d13.

It turns out this breaks at least the ath9k wireless driver, and
possibly others.

What the ath9k driver does on packet receive is to set up the DMA
transfer with:

int ath_rx_init(..)
..
bf->bf_buf_addr = dma_map_single(sc->dev, skb->data,
common->rx_bufsize,
DMA_FROM_DEVICE);

and then the receive logic (through ath_rx_tasklet()) will fetch
incoming packets

static bool ath_edma_get_buffers(..)
..
dma_sync_single_for_cpu(sc->dev, bf->bf_buf_addr,
common->rx_bufsize, DMA_FROM_DEVICE);

ret = ath9k_hw_process_rxdesc_edma(ah, rs, skb->data);
if (ret == -EINPROGRESS) {
/*let device gain the buffer again*/
dma_sync_single_for_device(sc->dev, bf->bf_buf_addr,
common->rx_bufsize, DMA_FROM_DEVICE);
return false;
}

and it's worth noting how that first DMA sync:

dma_sync_single_for_cpu(..DMA_FROM_DEVICE);

is there to make sure the CPU can read the DMA buffer (possibly by
copying it from the bounce buffer area, or by doing some cache flush).
The iommu correctly turns that into a "copy from bounce bufer" so that
the driver can look at the state of the packets.

In the meantime, the device may continue to write to the DMA buffer, but
we at least have a snapshot of the state due to that first DMA sync.

But that _second_ DMA sync:

dma_sync_single_for_device(..DMA_FROM_DEVICE);

is telling the DMA mapping that the CPU wasn't interested in the area
because the packet wasn't there. In the case of a DMA bounce buffer,
that is a no-op.

Note how it's not a sync for the CPU (the "for_device()" part), and it's
not a sync for data written by the CPU (the "DMA_FROM_DEVICE" part).

Or rather, it _should_ be a no-op. That's what commit aa6f8dcbab47
broke: it made the code bounce the buffer unconditionally, and changed
the DMA_FROM_DEVICE to just unconditionally and illogically be
DMA_TO_DEVICE.

[ Side note: purely within the confines of the swiotlb driver it wasn't
entirely illogical: The reason it did that odd DMA_FROM_DEVICE ->
DMA_TO_DEVICE conversion thing is because inside the swiotlb driver,
it uses just a swiotlb_bounce() helper that doesn't care about the
whole distinction of who the sync is for - only which direction to
bounce.

So it took the "sync for device" to mean that the CPU must have been
the one writing, and thought it meant DMA_TO_DEVICE. ]

Also note how the commentary in that commit was wrong, probably due to
that whole confusion, claiming that the commit makes the swiotlb code

"bounce unconditionally (that is, also
when dir == DMA_TO_DEVICE) in order do avoid synchronising back stale
data from the swiotlb buffer"

which is nonsensical for two reasons:

- that "also when dir == DMA_TO_DEVICE" is nonsensical, as that was
exactly when it always did - and should do - the bounce.

- since this is a sync for the device (not for the CPU), we're clearly
fundamentally not coping back stale data from the bounce buffers at
all, because we'd be copying *to* the bounce buffers.

So that commit was just very confused. It confused the direction of the
synchronization (to the device, not the cpu) with the direction of the
DMA (from the device).

Reported-and-bisected-by: Oleksandr Natalenko <oleksandr@natalenko.name>
Reported-by: Olha Cherevyk <olha.cherevyk@gmail.com>
Cc: Halil Pasic <pasic@linux.ibm.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Kalle Valo <kvalo@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Toke Høiland-Jørgensen <toke@toke.dk>
Cc: Maxime Bizon <mbizon@freebox.fr>
Cc: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+24 -15
+8
Documentation/core-api/dma-attributes.rst
··· 130 130 subsystem that the buffer is fully accessible at the elevated privilege 131 131 level (and ideally inaccessible or at least read-only at the 132 132 lesser-privileged levels). 133 + 134 + DMA_ATTR_OVERWRITE 135 + ------------------ 136 + 137 + This is a hint to the DMA-mapping subsystem that the device is expected to 138 + overwrite the entire mapped size, thus the caller does not require any of the 139 + previous buffer contents to be preserved. This allows bounce-buffering 140 + implementations to optimise DMA_FROM_DEVICE transfers.
+8
include/linux/dma-mapping.h
··· 62 62 #define DMA_ATTR_PRIVILEGED (1UL << 9) 63 63 64 64 /* 65 + * This is a hint to the DMA-mapping subsystem that the device is expected 66 + * to overwrite the entire mapped size, thus the caller does not require any 67 + * of the previous buffer contents to be preserved. This allows 68 + * bounce-buffering implementations to optimise DMA_FROM_DEVICE transfers. 69 + */ 70 + #define DMA_ATTR_OVERWRITE (1UL << 10) 71 + 72 + /* 65 73 * A dma_addr_t can hold any valid DMA or bus address for the platform. It can 66 74 * be given to a device to use as a DMA source or target. It is specific to a 67 75 * given device and there may be a translation between the CPU physical address
+8 -15
kernel/dma/swiotlb.c
··· 627 627 for (i = 0; i < nr_slots(alloc_size + offset); i++) 628 628 mem->slots[index + i].orig_addr = slot_addr(orig_addr, i); 629 629 tlb_addr = slot_addr(mem->start, index) + offset; 630 - /* 631 - * When dir == DMA_FROM_DEVICE we could omit the copy from the orig 632 - * to the tlb buffer, if we knew for sure the device will 633 - * overwirte the entire current content. But we don't. Thus 634 - * unconditional bounce may prevent leaking swiotlb content (i.e. 635 - * kernel memory) to user-space. 636 - */ 637 - swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_TO_DEVICE); 630 + if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && 631 + (!(attrs & DMA_ATTR_OVERWRITE) || dir == DMA_TO_DEVICE || 632 + dir == DMA_BIDIRECTIONAL)) 633 + swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_TO_DEVICE); 638 634 return tlb_addr; 639 635 } 640 636 ··· 697 701 void swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr, 698 702 size_t size, enum dma_data_direction dir) 699 703 { 700 - /* 701 - * Unconditional bounce is necessary to avoid corruption on 702 - * sync_*_for_cpu or dma_ummap_* when the device didn't overwrite 703 - * the whole lengt of the bounce buffer. 704 - */ 705 - swiotlb_bounce(dev, tlb_addr, size, DMA_TO_DEVICE); 706 - BUG_ON(!valid_dma_direction(dir)); 704 + if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL) 705 + swiotlb_bounce(dev, tlb_addr, size, DMA_TO_DEVICE); 706 + else 707 + BUG_ON(dir != DMA_FROM_DEVICE); 707 708 } 708 709 709 710 void swiotlb_sync_single_for_cpu(struct device *dev, phys_addr_t tlb_addr,