From: Jan Beulich <jbeulich@suse.com>
Subject: gnttab: move "only local domain may copy by frame" check

gnttab_copy_one() calls gnttab_copy_lock_domain{s,}() only conditionally.
The non-GREF check towards the requestor being DOMID_SELF, however, needs
doing on every batch element. Move the check into gnttab_copy_claim_buf()
to achieve that.

This is CVE-2026-102704, part of XSA-523.

Fixes: d28f42f2703e ("grant-table: defer releasing pages acquired in a grant copy")
Assisted-by: Claude
Reported-by: Stephan Lipp <Stephan@Adalogics.com>
Reported-by: CodeAnt AI Security Research <securityresearch@codeant.ai>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>

diff --git a/xen/common/grant_table.c b/xen/common/grant_table.c
index 842d43684849..03543fc335a9 100644
--- a/xen/common/grant_table.c
+++ b/xen/common/grant_table.c
@@ -2934,13 +2934,8 @@ struct gnttab_copy_buf {
     bool_t have_type;
 };
 
-static int gnttab_copy_lock_domain(domid_t domid, bool is_gref,
-                                   struct gnttab_copy_buf *buf)
+static int gnttab_copy_lock_domain(domid_t domid, struct gnttab_copy_buf *buf)
 {
-    /* Only DOMID_SELF may reference via frame. */
-    if ( domid != DOMID_SELF && !is_gref )
-        return GNTST_permission_denied;
-
     buf->domain = rcu_lock_domain_by_any_id(domid);
 
     if ( !buf->domain )
@@ -2972,12 +2967,10 @@ static int gnttab_copy_lock_domains(const struct gnttab_copy *op,
 {
     int rc;
 
-    rc = gnttab_copy_lock_domain(op->source.domid,
-                                 op->flags & GNTCOPY_source_gref, src);
+    rc = gnttab_copy_lock_domain(op->source.domid, src);
     if ( rc < 0 )
         goto error;
-    rc = gnttab_copy_lock_domain(op->dest.domid,
-                                 op->flags & GNTCOPY_dest_gref, dest);
+    rc = gnttab_copy_lock_domain(op->dest.domid, dest);
     if ( rc < 0 )
         goto error;
 
@@ -3040,6 +3033,9 @@ static int gnttab_copy_claim_buf(const struct gnttab_copy *op,
         buf->ptr.u.ref = ptr->u.ref;
         buf->have_grant = 1;
     }
+    /* Only DOMID_SELF may reference via frame. */
+    else if ( ptr->domid != DOMID_SELF )
+        return GNTST_permission_denied;
     else
     {
         rc = get_paged_frame(ptr->u.gmfn, &buf->mfn, &buf->page,
