kernel/dma/direct.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
Clear pages through their page mapping before creating a coherent
remap for dma-direct allocations. Some architectures implement the
coherent remap as uncached memory, where the generic memset() path may
use cache-only zeroing instructions that are not valid for the returned
CPU mapping.
Keep the existing memset() for non-remapped allocations, but avoid
normal memset() on the remapped coherent allocation path.
Assisted-by: Codex:GPT-5.5
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
kernel/dma/direct.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 583c5922bca2..76f7bf43bd28 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -8,6 +8,7 @@
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/dma-map-ops.h>
+#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/pfn.h>
#include <linux/vmalloc.h>
@@ -104,6 +105,15 @@ static void __dma_direct_free_pages(struct device *dev, struct page *page,
dma_free_contiguous(dev, page, size);
}
+static void dma_direct_zero_pages(struct page *page, size_t size)
+{
+ unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
+ unsigned long i;
+
+ for (i = 0; i < count; i++)
+ clear_highpage(page + i);
+}
+
static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
{
struct page *page = swiotlb_alloc(dev, size);
@@ -268,6 +278,13 @@ void *dma_direct_alloc(struct device *dev, size_t size,
if (remap) {
pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
+ /*
+ * Zero via the page mapping before creating a potentially
+ * uncached remap. Some architectures cannot safely run normal
+ * memset on uncached memory.
+ */
+ dma_direct_zero_pages(page, size);
+
if (force_dma_unencrypted(dev))
prot = pgprot_decrypted(prot);
@@ -283,10 +300,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
ret = page_address(page);
if (dma_set_decrypted(dev, ret, size))
goto out_leak_pages;
+ memset(ret, 0, size);
}
- memset(ret, 0, size);
-
if (set_uncached) {
arch_dma_prep_coherent(page, size);
ret = arch_dma_set_uncached(ret, size);
--
2.54.0
On 17/05/2026 5:29 am, Rosen Penev wrote:
> Clear pages through their page mapping before creating a coherent
> remap for dma-direct allocations. Some architectures implement the
> coherent remap as uncached memory, where the generic memset() path may
> use cache-only zeroing instructions that are not valid for the returned
> CPU mapping.
Which architectures? I'd fear they're just broken in that case - there's
every chance that once the caller gets a coherent allocation back, they
could also invoke memset() on it - either explicitly, or automatically
inserted by the compiler for a structure/array initialisation, or if the
buffer subsequently gets mmap()ed to userspace then who knows what. If
that's really a problem, this patch isn't going to solve it.
It's rather the point that we clear the buffer the same way that the
caller is subsequently going to access it (not least to minimise the
chance of any other possible weird coherency side-effects).
Thanks,
Robin.
> Keep the existing memset() for non-remapped allocations, but avoid
> normal memset() on the remapped coherent allocation path.
>
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
> ---
> kernel/dma/direct.c | 20 ++++++++++++++++++--
> 1 file changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> index 583c5922bca2..76f7bf43bd28 100644
> --- a/kernel/dma/direct.c
> +++ b/kernel/dma/direct.c
> @@ -8,6 +8,7 @@
> #include <linux/export.h>
> #include <linux/mm.h>
> #include <linux/dma-map-ops.h>
> +#include <linux/highmem.h>
> #include <linux/scatterlist.h>
> #include <linux/pfn.h>
> #include <linux/vmalloc.h>
> @@ -104,6 +105,15 @@ static void __dma_direct_free_pages(struct device *dev, struct page *page,
> dma_free_contiguous(dev, page, size);
> }
>
> +static void dma_direct_zero_pages(struct page *page, size_t size)
> +{
> + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
> + unsigned long i;
> +
> + for (i = 0; i < count; i++)
> + clear_highpage(page + i);
> +}
> +
> static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
> {
> struct page *page = swiotlb_alloc(dev, size);
> @@ -268,6 +278,13 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> if (remap) {
> pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
>
> + /*
> + * Zero via the page mapping before creating a potentially
> + * uncached remap. Some architectures cannot safely run normal
> + * memset on uncached memory.
> + */
> + dma_direct_zero_pages(page, size);
> +
> if (force_dma_unencrypted(dev))
> prot = pgprot_decrypted(prot);
>
> @@ -283,10 +300,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> ret = page_address(page);
> if (dma_set_decrypted(dev, ret, size))
> goto out_leak_pages;
> + memset(ret, 0, size);
> }
>
> - memset(ret, 0, size);
> -
> if (set_uncached) {
> arch_dma_prep_coherent(page, size);
> ret = arch_dma_set_uncached(ret, size);
On Mon, May 18, 2026 at 5:40 AM Robin Murphy <robin.murphy@arm.com> wrote:
>
> On 17/05/2026 5:29 am, Rosen Penev wrote:
> > Clear pages through their page mapping before creating a coherent
> > remap for dma-direct allocations. Some architectures implement the
> > coherent remap as uncached memory, where the generic memset() path may
> > use cache-only zeroing instructions that are not valid for the returned
> > CPU mapping.
>
> Which architectures?
PowerPC 44x See:
https://lore.kernel.org/all/2e3acfe63d289c6fba366e16973c9ab8369e8b75.1631803922.git.christophe.leroy@csgroup.eu/
Fixing it here exposed similar issues elsewhere. I've submitted
patches for those.
> I'd fear they're just broken in that case - there's
> every chance that once the caller gets a coherent allocation back, they
> could also invoke memset() on it - either explicitly, or automatically
> inserted by the compiler for a structure/array initialisation, or if the
> buffer subsequently gets mmap()ed to userspace then who knows what. If
> that's really a problem, this patch isn't going to solve it.
>
> It's rather the point that we clear the buffer the same way that the
> caller is subsequently going to access it (not least to minimise the
> chance of any other possible weird coherency side-effects).
>
> Thanks,
> Robin.
>
> > Keep the existing memset() for non-remapped allocations, but avoid
> > normal memset() on the remapped coherent allocation path.
> >
> > Assisted-by: Codex:GPT-5.5
> > Signed-off-by: Rosen Penev <rosenp@gmail.com>
> > ---
> > kernel/dma/direct.c | 20 ++++++++++++++++++--
> > 1 file changed, 18 insertions(+), 2 deletions(-)
> >
> > diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> > index 583c5922bca2..76f7bf43bd28 100644
> > --- a/kernel/dma/direct.c
> > +++ b/kernel/dma/direct.c
> > @@ -8,6 +8,7 @@
> > #include <linux/export.h>
> > #include <linux/mm.h>
> > #include <linux/dma-map-ops.h>
> > +#include <linux/highmem.h>
> > #include <linux/scatterlist.h>
> > #include <linux/pfn.h>
> > #include <linux/vmalloc.h>
> > @@ -104,6 +105,15 @@ static void __dma_direct_free_pages(struct device *dev, struct page *page,
> > dma_free_contiguous(dev, page, size);
> > }
> >
> > +static void dma_direct_zero_pages(struct page *page, size_t size)
> > +{
> > + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
> > + unsigned long i;
> > +
> > + for (i = 0; i < count; i++)
> > + clear_highpage(page + i);
> > +}
> > +
> > static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
> > {
> > struct page *page = swiotlb_alloc(dev, size);
> > @@ -268,6 +278,13 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> > if (remap) {
> > pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
> >
> > + /*
> > + * Zero via the page mapping before creating a potentially
> > + * uncached remap. Some architectures cannot safely run normal
> > + * memset on uncached memory.
> > + */
> > + dma_direct_zero_pages(page, size);
> > +
> > if (force_dma_unencrypted(dev))
> > prot = pgprot_decrypted(prot);
> >
> > @@ -283,10 +300,9 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> > ret = page_address(page);
> > if (dma_set_decrypted(dev, ret, size))
> > goto out_leak_pages;
> > + memset(ret, 0, size);
> > }
> >
> > - memset(ret, 0, size);
> > -
> > if (set_uncached) {
> > arch_dma_prep_coherent(page, size);
> > ret = arch_dma_set_uncached(ret, size);
>
On Sat, May 16, 2026 at 09:29:55PM -0700, Rosen Penev wrote:
> Clear pages through their page mapping before creating a coherent
> remap for dma-direct allocations. Some architectures implement the
> coherent remap as uncached memory, where the generic memset() path may
> use cache-only zeroing instructions that are not valid for the returned
> CPU mapping.
>
> Keep the existing memset() for non-remapped allocations, but avoid
> normal memset() on the remapped coherent allocation path.
>
> Assisted-by: Codex:GPT-5.5
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
> ---
> kernel/dma/direct.c | 20 ++++++++++++++++++--
> 1 file changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
> index 583c5922bca2..76f7bf43bd28 100644
> --- a/kernel/dma/direct.c
> +++ b/kernel/dma/direct.c
> @@ -8,6 +8,7 @@
> #include <linux/export.h>
> #include <linux/mm.h>
> #include <linux/dma-map-ops.h>
> +#include <linux/highmem.h>
> #include <linux/scatterlist.h>
> #include <linux/pfn.h>
> #include <linux/vmalloc.h>
> @@ -104,6 +105,15 @@ static void __dma_direct_free_pages(struct device *dev, struct page *page,
> dma_free_contiguous(dev, page, size);
> }
>
> +static void dma_direct_zero_pages(struct page *page, size_t size)
> +{
> + unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
> + unsigned long i;
> +
> + for (i = 0; i < count; i++)
> + clear_highpage(page + i);
> +}
> +
> static struct page *dma_direct_alloc_swiotlb(struct device *dev, size_t size)
> {
> struct page *page = swiotlb_alloc(dev, size);
> @@ -268,6 +278,13 @@ void *dma_direct_alloc(struct device *dev, size_t size,
> if (remap) {
> pgprot_t prot = dma_pgprot(dev, PAGE_KERNEL, attrs);
>
> + /*
> + * Zero via the page mapping before creating a potentially
> + * uncached remap. Some architectures cannot safely run normal
> + * memset on uncached memory.
> + */
I can't say whether this description is accurate, but if it is, I'd expect
all memset callers on such architectures to be affected, not only the remap
path. If this holds, we will need a safe memset() implementation.
Thanks
© 2016 - 2026 Red Hat, Inc.