Blob Blame History Raw
From 7fac5971340a13ca9458195305bcfe14df2e52d2 Mon Sep 17 00:00:00 2001
From: Stefano Stabellini <stefano.stabellini@amd.com>
Date: Thu, 17 Aug 2023 13:41:35 +0100
Subject: [PATCH] xen/arm: page: Handle cache flush of an element at the top of
 the address space

The region that needs to be cleaned/invalidated may be at the top
of the address space. This means that 'end' (i.e. 'p + size') will
be 0 and therefore nothing will be cleaned/invalidated as the check
in the loop will always be false.

On Arm64, we only support we only support up to 48-bit Virtual
address space. So this is not a concern there. However, for 32-bit,
the mapcache is using the last 2GB of the address space. Therefore
we may not clean/invalidate properly some pages. This could lead
to memory corruption or data leakage (the scrubbed value may
still sit in the cache when the guest could read directly the memory
and therefore read the old content).

Rework invalidate_dcache_va_range(), clean_dcache_va_range(),
clean_and_invalidate_dcache_va_range() to handle a cache flush
with an element at the top of the address space.

This is CVE-2023-34321 / XSA-437.

Reported-by: Julien Grall <jgrall@amazon.com>
Signed-off-by: Stefano Stabellini <stefano.stabellini@amd.com>
Signed-off-by: Julien Grall <jgrall@amazon.com>
Acked-by: Bertrand Marquis <bertrand.marquis@arm.com>

---
 xen/arch/arm/include/asm/page.h | 33 ++++++++++++++++++++-------------
 1 file changed, 20 insertions(+), 13 deletions(-)

diff --git a/xen/arch/arm/include/asm/page.h b/xen/arch/arm/include/asm/page.h
index e7cd62190c7f..d7fe770a5e49 100644
--- a/xen/arch/arm/include/asm/page.h
+++ b/xen/arch/arm/include/asm/page.h
@@ -160,26 +160,25 @@ static inline size_t read_dcache_line_bytes(void)
 
 static inline int invalidate_dcache_va_range(const void *p, unsigned long size)
 {
-    const void *end = p + size;
     size_t cacheline_mask = dcache_line_bytes - 1;
 
     dsb(sy);           /* So the CPU issues all writes to the range */
 
     if ( (uintptr_t)p & cacheline_mask )
     {
+        size -= dcache_line_bytes - ((uintptr_t)p & cacheline_mask);
         p = (void *)((uintptr_t)p & ~cacheline_mask);
         asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
         p += dcache_line_bytes;
     }
-    if ( (uintptr_t)end & cacheline_mask )
-    {
-        end = (void *)((uintptr_t)end & ~cacheline_mask);
-        asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (end));
-    }
 
-    for ( ; p < end; p += dcache_line_bytes )
+    for ( ; size >= dcache_line_bytes;
+            p += dcache_line_bytes, size -= dcache_line_bytes )
         asm volatile (__invalidate_dcache_one(0) : : "r" (p));
 
+    if ( size > 0 )
+        asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
+
     dsb(sy);           /* So we know the flushes happen before continuing */
 
     return 0;
@@ -187,10 +186,14 @@ static inline int invalidate_dcache_va_range(const void *p, unsigned long size)
 
 static inline int clean_dcache_va_range(const void *p, unsigned long size)
 {
-    const void *end = p + size;
+    size_t cacheline_mask = dcache_line_bytes - 1;
+
     dsb(sy);           /* So the CPU issues all writes to the range */
-    p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1));
-    for ( ; p < end; p += dcache_line_bytes )
+    size += (uintptr_t)p & cacheline_mask;
+    size = (size + cacheline_mask) & ~cacheline_mask;
+    p = (void *)((uintptr_t)p & ~cacheline_mask);
+    for ( ; size >= dcache_line_bytes;
+            p += dcache_line_bytes, size -= dcache_line_bytes )
         asm volatile (__clean_dcache_one(0) : : "r" (p));
     dsb(sy);           /* So we know the flushes happen before continuing */
     /* ARM callers assume that dcache_* functions cannot fail. */
@@ -200,10 +203,14 @@ static inline int clean_dcache_va_range(const void *p, unsigned long size)
 static inline int clean_and_invalidate_dcache_va_range
     (const void *p, unsigned long size)
 {
-    const void *end = p + size;
+    size_t cacheline_mask = dcache_line_bytes - 1;
+
     dsb(sy);         /* So the CPU issues all writes to the range */
-    p = (void *)((uintptr_t)p & ~(dcache_line_bytes - 1));
-    for ( ; p < end; p += dcache_line_bytes )
+    size += (uintptr_t)p & cacheline_mask;
+    size = (size + cacheline_mask) & ~cacheline_mask;
+    p = (void *)((uintptr_t)p & ~cacheline_mask);
+    for ( ; size >= dcache_line_bytes;
+            p += dcache_line_bytes, size -= dcache_line_bytes )
         asm volatile (__clean_and_invalidate_dcache_one(0) : : "r" (p));
     dsb(sy);         /* So we know the flushes happen before continuing */
     /* ARM callers assume that dcache_* functions cannot fail. */
-- 
2.40.1