| From foo@baz Thu Dec 21 09:02:40 CET 2017 |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| Date: Wed, 29 Mar 2017 17:12:47 +0100 |
| Subject: ARM: dma-mapping: disallow dma_get_sgtable() for non-kernel managed memory |
| |
| From: Russell King <rmk+kernel@armlinux.org.uk> |
| |
| |
| [ Upstream commit 916a008b4b8ecc02fbd035cfb133773dba1ff3d7 ] |
| |
| dma_get_sgtable() tries to create a scatterlist table containing valid |
| struct page pointers for the coherent memory allocation passed in to it. |
| |
| However, memory can be declared via dma_declare_coherent_memory(), or |
| via other reservation schemes which means that coherent memory is not |
| guaranteed to be backed by struct pages. In such cases, the resulting |
| scatterlist table contains pointers to invalid pages, which causes |
| kernel oops later. |
| |
| This patch adds detection of such memory, and refuses to create a |
| scatterlist table for such memory. |
| |
| Reported-by: Shuah Khan <shuahkhan@gmail.com> |
| Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> |
| Signed-off-by: Sasha Levin <alexander.levin@verizon.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++++- |
| 1 file changed, 19 insertions(+), 1 deletion(-) |
| |
| --- a/arch/arm/mm/dma-mapping.c |
| +++ b/arch/arm/mm/dma-mapping.c |
| @@ -930,13 +930,31 @@ static void arm_coherent_dma_free(struct |
| __arm_dma_free(dev, size, cpu_addr, handle, attrs, true); |
| } |
| |
| +/* |
| + * The whole dma_get_sgtable() idea is fundamentally unsafe - it seems |
| + * that the intention is to allow exporting memory allocated via the |
| + * coherent DMA APIs through the dma_buf API, which only accepts a |
| + * scattertable. This presents a couple of problems: |
| + * 1. Not all memory allocated via the coherent DMA APIs is backed by |
| + * a struct page |
| + * 2. Passing coherent DMA memory into the streaming APIs is not allowed |
| + * as we will try to flush the memory through a different alias to that |
| + * actually being used (and the flushes are redundant.) |
| + */ |
| int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt, |
| void *cpu_addr, dma_addr_t handle, size_t size, |
| unsigned long attrs) |
| { |
| - struct page *page = pfn_to_page(dma_to_pfn(dev, handle)); |
| + unsigned long pfn = dma_to_pfn(dev, handle); |
| + struct page *page; |
| int ret; |
| |
| + /* If the PFN is not valid, we do not have a struct page */ |
| + if (!pfn_valid(pfn)) |
| + return -ENXIO; |
| + |
| + page = pfn_to_page(pfn); |
| + |
| ret = sg_alloc_table(sgt, 1, GFP_KERNEL); |
| if (unlikely(ret)) |
| return ret; |