[PATCH] irqchip/gic-v3-its: zero ITS tables in the shared view after decryption

Shanker Donthineni <[email protected]>
Newsgroups org.kernel.vger.stable,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
its_alloc_pages_node() allocates pages, some with __GFP_ZERO, before
calling set_memory_decrypted() to share them with the untrusted
hypervisor. In an Arm CCA Realm, allocator clearing occurs through the
Protected IPA.

set_memory_decrypted() changes the RIPAS of the Protected IPA from
RIPAS_RAM to RIPAS_EMPTY. The RMM architecture requires the backing
DATA Granule to be wiped before it becomes GRAN_UNDELEGATED. Wiping is
a confidentiality operation, not a zero-initialization guarantee: it
may be implemented by changing the MEC or writing random data.
Consequently, zeroes written through the Protected IPA are not
guaranteed to be observed through the corresponding Unprotected IPA
alias.

This is harmless for tables that the guest fully populates before use,
but the GICv3 ITS indirect device table is sparse. The guest writes only
the L1 entries for devices it maps, while both the guest and the
hypervisor's ITS emulation rely on unused entries being zero.
Unspecified nonzero values can make an unpopulated entry appear valid,
causing the guest to skip the required L2 allocation and the hypervisor
to consume a bogus pointer. Interrupt translation then fails,
potentially hanging the guest when it first uses the device. This is
exposed when guest_memfd reuses the same backing Granule for the
Unprotected IPA mapping rather than supplying a fresh Host page.

Strip __GFP_ZERO before allocation and clear the page through the
Unprotected IPA alias after set_memory_decrypted(), so the requested
zeroes are written in Non-secure PAS and are visible to both the guest
and the Host. This also matches dma_direct_alloc(), which clears memory
after dma_set_decrypted().

Fixes: b08e2f42e86b ("irqchip/gic-v3-its: Share ITS tables with a non-trusted hypervisor")
Cc: <[email protected]> # 6.18+
Signed-off-by: Shanker Donthineni <[email protected]>
---
 drivers/irqchip/irq-gic-v3-its.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 6f5811aae59c1..a62abe8bcbde0 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -216,7 +216,17 @@ static struct page *its_alloc_pages_node(int node, gfp_t gfp,
 	struct page *page;
 	int ret = 0;
 
-	page = alloc_pages_node(node, gfp | gfp_flags_quirk, order);
+	/*
+	 * Defer the zeroing requested by __GFP_ZERO until after the page has
+	 * been shared below. Under memory encryption (e.g. an Arm CCA realm),
+	 * any clearing performed by the allocator occurs in the private view
+	 * (the realm MECID/PAS). Once set_memory_decrypted() has run, both the
+	 * hypervisor and the guest access the page through its shared
+	 * (non-secure) alias, where the earlier private zeroing is not visible.
+	 * Strip __GFP_ZERO here and clear the shared view after the transition.
+	 */
+	page = alloc_pages_node(node, (gfp & ~__GFP_ZERO) | gfp_flags_quirk,
+				order);
 
 	if (!page)
 		return NULL;
@@ -231,6 +241,15 @@ static struct page *its_alloc_pages_node(int node, gfp_t gfp,
 	if (ret)
 		return NULL;
 
+	/*
+	 * If the caller requested __GFP_ZERO, clear the page after it has been
+	 * shared. This is required for sparsely populated tables, such as the
+	 * indirect device-table L1: zeroing the shared view ensures that the
+	 * hypervisor observes zero for entries the guest has not written.
+	 */
+	if (gfp & __GFP_ZERO)
+		memset(page_address(page), 0, PAGE_ORDER_TO_SIZE(order));
+
 	return page;
 }
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.