]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: s390: pci: Validate AIBV and AISB before pinning guest pages
authorFarhan Ali <alifm@linux.ibm.com>
Thu, 23 Jul 2026 22:14:09 +0000 (15:14 -0700)
committerChristian Borntraeger <borntraeger@linux.ibm.com>
Fri, 24 Jul 2026 09:26:54 +0000 (11:26 +0200)
The AIBV holds one bit per MSI-X vector for a given function. The size of
the bit vector is derived from the NOI and the AIBVO. If the size of the
AIBV exceeds a single page boundary, then reject the request as we cannot
safely pin the guest AIBV.

Similarly reject the request if the AISB address is not 8-byte aligned as
the architecture requires doubleword alignment for the summary bit address.
Since the AISBO can address up to 64 bits, the size of the AISB can only be
8 bytes for the function. This also ensures the AISB doesn't exceed a
single page boundary.

Fixes: 3c5a1b6f0a18 ("KVM: s390: pci: provide routines for enabling/disabling interrupt forwarding")
Cc: stable@vger.kernel.org
Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Farhan Ali <alifm@linux.ibm.com>
Tested-by: Matthew Rosato <mjrosato@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
arch/s390/kvm/pci.c

index 50f495bc83038d2c66678d2b5268b7cfd0414dad..50f5ec79600e0dee97d831f436cb3758516feef9 100644 (file)
@@ -244,7 +244,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
                                   bool assist)
 {
        struct page *pages[1], *aibv_page, *aisb_page = NULL;
-       unsigned int msi_vecs, idx;
+       unsigned int msi_vecs, idx, size;
        struct zpci_gaite *gaite;
        unsigned long hva, bit;
        struct kvm *kvm;
@@ -271,6 +271,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
                return gisc;
 
        /* Replace AIBV address */
+       size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long);
+       npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE);
+       /* AIBV cannot span more than 1 page */
+       if (npages > 1) {
+               rc = -EINVAL;
+               goto out;
+       }
+
        idx = srcu_read_lock(&kvm->srcu);
        hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv));
        npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages);
@@ -286,6 +294,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
 
        /* Pin the guest AISB if one was specified */
        if (fib->fmt0.sum == 1) {
+               /* AISB must be dword aligned */
+               if (fib->fmt0.aisb & 0x7) {
+                       rc = -EINVAL;
+                       goto unpin1;
+               }
+
                idx = srcu_read_lock(&kvm->srcu);
                hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb));
                npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM,