From: Andrew Cooper Subject: xen/gnttab: Prevent GNTTABOP_cache_flush from corrupting RO grants Grants can be offered read-only by the owning domain. While a domain mapping a RO grant may have a need to invalidate the cache, it must not modify the other-guests data while doing so. Force-upgrade to a clean operation in this case. This is CVE-2026-102702, part of XSA-519. Fixes: 18e8d22fe750 ("introduce GNTTABOP_cache_flush") Signed-off-by: Andrew Cooper Reviewed-by: Stefano Stabellini diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c index c0ca4f305750..206f1ed67a28 100644 --- a/xen/common/grant_table.c +++ b/xen/common/grant_table.c @@ -983,6 +983,13 @@ static struct active_grant_entry *grant_map_exists(const struct domain *ld, return ERR_PTR(-EINVAL); } +static bool grant_map_is_ro(struct active_grant_entry *act) +{ + ASSERT(spin_is_locked(&act->lock)); + + return (act->pin & (GNTPIN_hstw_mask | GNTPIN_devw_mask)) == 0; +} + union maptrack_node { struct { /* Radix tree slot pointers use two of the bits. */ @@ -3595,7 +3602,7 @@ gnttab_swap_grant_ref(XEN_GUEST_HANDLE_PARAM(gnttab_swap_grant_ref_t) uop, return 0; } -static int _cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) +static int _cache_flush(gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref) { struct domain *d, *owner; struct page_info *page; @@ -3646,6 +3653,24 @@ static int _cache_flush(const gnttab_cache_flush_t *cflush, grant_ref_t *cur_ref put_page(page); return act ? PTR_ERR(act) : 1; } + + /* + * If the grant mapping is read-only, it must be cleaned as well as + * invalidated. Invalidation without cleaning may modify memory the + * granter is expecting not to change. + * + * It is highly unlikely but legal to have multiple grant mappings of + * differing read-only-ness. If a read-write grant mapping is found + * first, then it's fine to invalidate without clean. + * + * If a read-only grant mapping is found first, a later read-write + * mapping could be missed. Assume that the read-only mapping is the + * only mapping. The cost is a clean of up to 4k, while the saving is + * not requiring the search of grant maps to be exhaustive in order to + * spot a corner-case expected not to exist in practice. + */ + if ( grant_map_is_ro(act) ) + cflush->op |= GNTTAB_CACHE_CLEAN; } v = map_domain_page(mfn);