]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: arm64: Only update XN attr when requested during S2 relaxation
authorOliver Upton <oupton@kernel.org>
Wed, 1 Jul 2026 23:16:20 +0000 (16:16 -0700)
committerMarc Zyngier <maz@kernel.org>
Mon, 6 Jul 2026 16:17:38 +0000 (17:17 +0100)
On systems without DIC, KVM lazily grants execute permission to stage-2
translations after taking an instruction abort due to a permission
fault, allowing it to defer I-cache invalidations to the point they're
absolutely required.

If a data abort happens later down the line to such a translation, KVM
will not request execute permissions as part of the S2 relaxation on the
assumption that kvm_pgtable_stage2_relax_perms() does exactly what the
name implies and adds the requested permissions to the pre-existing
ones.

Avoid taking unintended execute permission faults by only preparing the
XN attribute if KVM_PGTABLE_PROT_X is set.

Fixes: 2608563b466b ("KVM: arm64: Add support for FEAT_XNX stage-2 permissions")
Signed-off-by: Oliver Upton <oupton@kernel.org>
Reviewed-by: Wei-Lin Chang <weilin.chang@arm.com>
Link: https://patch.msgid.link/20260701231620.3300204-3-oupton@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/hyp/pgtable.c

index 386a7468136a2259479f3a6d03e8ba08b69c2fb2..8754c99c22f2fe8d9faced156f636c44298244f8 100644 (file)
@@ -1368,12 +1368,14 @@ int kvm_pgtable_stage2_relax_perms(struct kvm_pgtable *pgt, u64 addr,
        if (prot & KVM_PGTABLE_PROT_W)
                set |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_W;
 
-       ret = stage2_set_xn_attr(prot, &xn);
-       if (ret)
-               return ret;
+       if (prot & KVM_PGTABLE_PROT_X) {
+               ret = stage2_set_xn_attr(prot, &xn);
+               if (ret)
+                       return ret;
 
-       set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
-       clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+               set |= xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+               clr |= ~xn & KVM_PTE_LEAF_ATTR_HI_S2_XN;
+       }
 
        ret = stage2_update_leaf_attrs(pgt, addr, 1, set, clr, NULL, &level, flags);
        if (!ret || ret == -EAGAIN)