diff mbox series

[v5,34/38] kmsan: dma: unpoison memory mapped by dma_direct_map_page()

Message ID 20200325161249.55095-35-glider@google.com (mailing list archive)
State New, archived
Headers show
Series Add KernelMemorySanitizer infrastructure | expand

Commit Message

Alexander Potapenko March 25, 2020, 4:12 p.m. UTC
KMSAN doesn't know about DMA memory writes performed by devices.
We unpoison such memory when it's mapped to avoid false positive
reports.

Signed-off-by: Alexander Potapenko <glider@google.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Marek Szyprowski <m.szyprowski@samsung.com>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Vegard Nossum <vegard.nossum@oracle.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Marco Elver <elver@google.com>
Cc: Andrey Konovalov <andreyknvl@google.com>
Cc: linux-mm@kvack.org
---

Change-Id: Ib1019ed531fea69f88b5cdec3d1e27403f2f3d64
---
 kernel/dma/direct.c | 1 +
 1 file changed, 1 insertion(+)

Comments

Christoph Hellwig March 25, 2020, 4:19 p.m. UTC | #1
On Wed, Mar 25, 2020 at 05:12:45PM +0100, glider@google.com wrote:
> diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> index a8560052a915f..63dc1a594964a 100644
> --- a/kernel/dma/direct.c
> +++ b/kernel/dma/direct.c
> @@ -367,6 +367,7 @@ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
>  			     &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
>  		return DMA_MAPPING_ERROR;
>  	}
> +	kmsan_handle_dma(page_address(page) + offset, size, dir);

This needs to go into dma_map_page so that it also covers IOMMUs.
dma_map_sg_atttrs will also need similar treatment.  Also the page
doesn't have to be mapped into kernel address space, you probably
want to pass the page to kmsan_handle_dma and throw in a highmem
check there.
Alexander Potapenko March 27, 2020, 5:03 p.m. UTC | #2
On Wed, Mar 25, 2020 at 5:19 PM Christoph Hellwig <hch@lst.de> wrote:
>
> On Wed, Mar 25, 2020 at 05:12:45PM +0100, glider@google.com wrote:
> > diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> > index a8560052a915f..63dc1a594964a 100644
> > --- a/kernel/dma/direct.c
> > +++ b/kernel/dma/direct.c
> > @@ -367,6 +367,7 @@ dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
> >                            &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
> >               return DMA_MAPPING_ERROR;
> >       }
> > +     kmsan_handle_dma(page_address(page) + offset, size, dir);
>
> This needs to go into dma_map_page so that it also covers IOMMUs.
> dma_map_sg_atttrs will also need similar treatment.

Thanks, will be done in v6!

> Also the page
> doesn't have to be mapped into kernel address space, you probably
> want to pass the page to kmsan_handle_dma and throw in a highmem
> check there.

Do you mean comparing the address to TASK_SIZE, or is there a more
portable way to check that?
Christoph Hellwig March 27, 2020, 5:06 p.m. UTC | #3
On Fri, Mar 27, 2020 at 06:03:32PM +0100, Alexander Potapenko wrote:
> > Also the page
> > doesn't have to be mapped into kernel address space, you probably
> > want to pass the page to kmsan_handle_dma and throw in a highmem
> > check there.
> 
> Do you mean comparing the address to TASK_SIZE, or is there a more
> portable way to check that?

!PageHighMem(page) implies the page has a kernel direct mapping.
Alexander Potapenko March 27, 2020, 6:46 p.m. UTC | #4
On Fri, Mar 27, 2020 at 6:06 PM Christoph Hellwig <hch@lst.de> wrote:
>
> On Fri, Mar 27, 2020 at 06:03:32PM +0100, Alexander Potapenko wrote:
> > > Also the page
> > > doesn't have to be mapped into kernel address space, you probably
> > > want to pass the page to kmsan_handle_dma and throw in a highmem
> > > check there.
> >
> > Do you mean comparing the address to TASK_SIZE, or is there a more
> > portable way to check that?
>
> !PageHighMem(page) implies the page has a kernel direct mapping.

I tried adding this check and started seeing false positives because
the virtio_ring driver actually uses highmem pages for DMA, and data
from those pages is later copied to the kernel.
Guess it's easier to just allow handling highmem pages? What problems
do you anticipate?
Christoph Hellwig March 28, 2020, 8:52 a.m. UTC | #5
On Fri, Mar 27, 2020 at 07:46:08PM +0100, Alexander Potapenko wrote:
> > > Do you mean comparing the address to TASK_SIZE, or is there a more
> > > portable way to check that?
> >
> > !PageHighMem(page) implies the page has a kernel direct mapping.
> 
> I tried adding this check and started seeing false positives because
> the virtio_ring driver actually uses highmem pages for DMA, and data
> from those pages is later copied to the kernel.
> Guess it's easier to just allow handling highmem pages? What problems
> do you anticipate?

For PageHighMem(page), page_address(page) is not actually valid, so І'm
not sure how your code in this patch even worked at all.  Note that
all drivers (well, except for a few buggy legacy ones with workarounds)
can DMA from/to highmem.
Alexander Potapenko April 14, 2020, 3:26 p.m. UTC | #6
On Sat, Mar 28, 2020 at 9:52 AM Christoph Hellwig <hch@lst.de> wrote:
>
> On Fri, Mar 27, 2020 at 07:46:08PM +0100, Alexander Potapenko wrote:
> > > > Do you mean comparing the address to TASK_SIZE, or is there a more
> > > > portable way to check that?
> > >
> > > !PageHighMem(page) implies the page has a kernel direct mapping.
> >
> > I tried adding this check and started seeing false positives because
> > the virtio_ring driver actually uses highmem pages for DMA, and data
> > from those pages is later copied to the kernel.
> > Guess it's easier to just allow handling highmem pages? What problems
> > do you anticipate?
>
> For PageHighMem(page), page_address(page) is not actually valid, so І'm
> not sure how your code in this patch even worked at all.  Note that
> all drivers (well, except for a few buggy legacy ones with workarounds)
> can DMA from/to highmem.

Hm, skipping PageHighMem pages works now, I must've been doing something wrong.
Thanks, will add that check to v6.

I found a bunch of other places in mm/kmsan/kmsan_shadow.c where we're
using page_address(), but metadata pages are not supposed to reside in
high memory.
diff mbox series

Patch

diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index a8560052a915f..63dc1a594964a 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -367,6 +367,7 @@  dma_addr_t dma_direct_map_page(struct device *dev, struct page *page,
 			     &dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
 		return DMA_MAPPING_ERROR;
 	}
+	kmsan_handle_dma(page_address(page) + offset, size, dir);
 
 	if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
 		arch_sync_dma_for_device(phys, size, dir);