[RFC,4/5] dma/direct: check for overflows in ARM's dma_capable()
diff mbox series

Message ID 20191014183108.24804-5-nsaenzjulienne@suse.de
State New, archived
Headers show
Series
  • ARM: Raspberry Pi 4 DMA support
Related show

Commit Message

Nicolas Saenz Julienne Oct. 14, 2019, 6:31 p.m. UTC
The Raspberry Pi 4 has a 1GB ZONE_DMA area starting at address
0x00000000 and a mapping between physical and DMA memory offset by
0xc0000000.  It transpires that, on non LPAE systems, any attempt to
translate physical addresses outside of ZONE_DMA will result in an
overflow. The resulting DMA addresses will not be detected by arm's
dma_capable() as they still fit in the device's DMA mask.

Fix this by failing to validate a DMA address smaller than the lowest
possible DMA address.

Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
---
 arch/arm/include/asm/dma-direct.h | 6 ++++++
 1 file changed, 6 insertions(+)

Comments

Christoph Hellwig Oct. 15, 2019, 10:23 a.m. UTC | #1
On Mon, Oct 14, 2019 at 08:31:06PM +0200, Nicolas Saenz Julienne wrote:
> The Raspberry Pi 4 has a 1GB ZONE_DMA area starting at address
> 0x00000000 and a mapping between physical and DMA memory offset by
> 0xc0000000.  It transpires that, on non LPAE systems, any attempt to
> translate physical addresses outside of ZONE_DMA will result in an
> overflow. The resulting DMA addresses will not be detected by arm's
> dma_capable() as they still fit in the device's DMA mask.
> 
> Fix this by failing to validate a DMA address smaller than the lowest
> possible DMA address.

I think the main problem here is that arm doesn't respect the
bus_dma_mask.  If you replace the arm version of dma_capable with
the generic one, does that fi the issue for you as well?

We need to untangle the various macros arm uses for the direct mapping
and eventually we should be able to use the linux/dma-direct.h helpers
directly.  Here is a branch with some simple preps I had.  Freshly
rebased, not actually tested:

http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/arm-generic-dma-preps
Nicolas Saenz Julienne Oct. 15, 2019, 1:07 p.m. UTC | #2
On Tue, 2019-10-15 at 03:23 -0700, Christoph Hellwig wrote:
> On Mon, Oct 14, 2019 at 08:31:06PM +0200, Nicolas Saenz Julienne wrote:
> > The Raspberry Pi 4 has a 1GB ZONE_DMA area starting at address
> > 0x00000000 and a mapping between physical and DMA memory offset by
> > 0xc0000000.  It transpires that, on non LPAE systems, any attempt to
> > translate physical addresses outside of ZONE_DMA will result in an
> > overflow. The resulting DMA addresses will not be detected by arm's
> > dma_capable() as they still fit in the device's DMA mask.
> > 
> > Fix this by failing to validate a DMA address smaller than the lowest
> > possible DMA address.
> 
> I think the main problem here is that arm doesn't respect the
> bus_dma_mask.  If you replace the arm version of dma_capable with
> the generic one, does that fi the issue for you as well?

Yeah, that was my fist thought too, but it doesn't help.

So with RPi4's DMA mapping:

soc {
	dma-ranges = <0xc0000000  0x0 0x00000000  0x3c000000>;
	[...]
};

You'll get a 32bit bus dma map (log2i(0xc0000000 + 0x3c000000) + 1 = 32).

Trouble is, taking into account arm's multi_v7_defconfig uses 32bit addresses,
most phys_to_dma() translations are likely to overflow. For example phys
0x60000000 will be translated to DMA 0x20000000, which is no good.

No mask is going to catch this, and both dma_capable() implementations will
fail.

> We need to untangle the various macros arm uses for the direct mapping
> and eventually we should be able to use the linux/dma-direct.h helpers
> directly.  Here is a branch with some simple preps I had.  Freshly
> rebased, not actually tested:
> 
> 
http://git.infradead.org/users/hch/misc.git/shortlog/refs/heads/arm-generic-dma-preps

Noted, looks good to me.

Actually, an alternative to this patch would be to kill all custom
dma_capable() implementations, which are mostly redundant, and add these extra
checks conditional to the DMA address size in the generic version. I'll try to
respin this if I manage to understand what's going on with x86/sta211-fixup.c.

Patch
diff mbox series

diff --git a/arch/arm/include/asm/dma-direct.h b/arch/arm/include/asm/dma-direct.h
index b67e5fc1fe43..ee8ad47a14e3 100644
--- a/arch/arm/include/asm/dma-direct.h
+++ b/arch/arm/include/asm/dma-direct.h
@@ -2,6 +2,8 @@ 
 #ifndef ASM_ARM_DMA_DIRECT_H
 #define ASM_ARM_DMA_DIRECT_H 1
 
+#include <linux/memblock.h>
+
 static inline dma_addr_t __phys_to_dma(struct device *dev, phys_addr_t paddr)
 {
 	unsigned int offset = paddr & ~PAGE_MASK;
@@ -21,6 +23,10 @@  static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
 	if (!dev->dma_mask)
 		return 0;
 
+	/* Check if address overflowed */
+	if (addr < __phys_to_dma(dev, PFN_UP(min_low_pfn)))
+		return 0;
+
 	mask = *dev->dma_mask;
 
 	limit = (mask + 1) & ~mask;