]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: vgic-its: Add a data length check in vgic_its_save_*
authorJing Zhang <jingzhangos@google.com>
Thu, 7 Nov 2024 21:41:34 +0000 (13:41 -0800)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 11 Nov 2024 19:50:14 +0000 (19:50 +0000)
In all the vgic_its_save_*() functinos, they do not check whether
the data length is 8 bytes before calling vgic_write_guest_lock.
This patch adds the check. To prevent the kernel from being blown up
when the fault occurs, KVM_BUG_ON() is used. And the other BUG_ON()s
are replaced together.

Cc: stable@vger.kernel.org
Signed-off-by: Kunkun Jiang <jiangkunkun@huawei.com>
[Jing: Update with the new entry read/write helpers]
Signed-off-by: Jing Zhang <jingzhangos@google.com>
Link: https://lore.kernel.org/r/20241107214137.428439-4-jingzhangos@google.com
Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
arch/arm64/kvm/vgic/vgic-its.c
arch/arm64/kvm/vgic/vgic.h

index ba945ba78cc7d7e749af5ae932d63a4bfcf6f643..68ba7e2453cd0d4f6ac845f589ce84059d2c9b51 100644 (file)
@@ -2086,7 +2086,6 @@ static int scan_its_table(struct vgic_its *its, gpa_t base, int size, u32 esz,
 static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
                              struct its_ite *ite, gpa_t gpa, int ite_esz)
 {
-       struct kvm *kvm = its->dev->kvm;
        u32 next_offset;
        u64 val;
 
@@ -2095,7 +2094,8 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
               ((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) |
                ite->collection->collection_id;
        val = cpu_to_le64(val);
-       return vgic_write_guest_lock(kvm, gpa, &val, ite_esz);
+
+       return vgic_its_write_entry_lock(its, gpa, val, ite_esz);
 }
 
 /**
@@ -2239,7 +2239,6 @@ static int vgic_its_restore_itt(struct vgic_its *its, struct its_device *dev)
 static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
                             gpa_t ptr, int dte_esz)
 {
-       struct kvm *kvm = its->dev->kvm;
        u64 val, itt_addr_field;
        u32 next_offset;
 
@@ -2250,7 +2249,8 @@ static int vgic_its_save_dte(struct vgic_its *its, struct its_device *dev,
               (itt_addr_field << KVM_ITS_DTE_ITTADDR_SHIFT) |
                (dev->num_eventid_bits - 1));
        val = cpu_to_le64(val);
-       return vgic_write_guest_lock(kvm, ptr, &val, dte_esz);
+
+       return vgic_its_write_entry_lock(its, ptr, val, dte_esz);
 }
 
 /**
@@ -2437,7 +2437,8 @@ static int vgic_its_save_cte(struct vgic_its *its,
               ((u64)collection->target_addr << KVM_ITS_CTE_RDBASE_SHIFT) |
               collection->collection_id);
        val = cpu_to_le64(val);
-       return vgic_write_guest_lock(its->dev->kvm, gpa, &val, esz);
+
+       return vgic_its_write_entry_lock(its, gpa, val, esz);
 }
 
 /*
@@ -2453,8 +2454,7 @@ static int vgic_its_restore_cte(struct vgic_its *its, gpa_t gpa, int esz)
        u64 val;
        int ret;
 
-       BUG_ON(esz > sizeof(val));
-       ret = kvm_read_guest_lock(kvm, gpa, &val, esz);
+       ret = vgic_its_read_entry_lock(its, gpa, &val, esz);
        if (ret)
                return ret;
        val = le64_to_cpu(val);
@@ -2492,7 +2492,6 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
        u64 baser = its->baser_coll_table;
        gpa_t gpa = GITS_BASER_ADDR_48_to_52(baser);
        struct its_collection *collection;
-       u64 val;
        size_t max_size, filled = 0;
        int ret, cte_esz = abi->cte_esz;
 
@@ -2516,10 +2515,7 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
         * table is not fully filled, add a last dummy element
         * with valid bit unset
         */
-       val = 0;
-       BUG_ON(cte_esz > sizeof(val));
-       ret = vgic_write_guest_lock(its->dev->kvm, gpa, &val, cte_esz);
-       return ret;
+       return vgic_its_write_entry_lock(its, gpa, 0, cte_esz);
 }
 
 /*
index f2486b4d9f95667338e58c20b0327348574509fc..309295f5e1b0744f5ce88e1bbb0674008ace4094 100644 (file)
@@ -146,6 +146,29 @@ static inline int vgic_write_guest_lock(struct kvm *kvm, gpa_t gpa,
        return ret;
 }
 
+static inline int vgic_its_read_entry_lock(struct vgic_its *its, gpa_t eaddr,
+                                          u64 *eval, unsigned long esize)
+{
+       struct kvm *kvm = its->dev->kvm;
+
+       if (KVM_BUG_ON(esize != sizeof(*eval), kvm))
+               return -EINVAL;
+
+       return kvm_read_guest_lock(kvm, eaddr, eval, esize);
+
+}
+
+static inline int vgic_its_write_entry_lock(struct vgic_its *its, gpa_t eaddr,
+                                           u64 eval, unsigned long esize)
+{
+       struct kvm *kvm = its->dev->kvm;
+
+       if (KVM_BUG_ON(esize != sizeof(eval), kvm))
+               return -EINVAL;
+
+       return vgic_write_guest_lock(kvm, eaddr, &eval, esize);
+}
+
 /*
  * This struct provides an intermediate representation of the fields contained
  * in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC