KVM: arm64: Add memory length checks and remove inline in do_ffa_mem_xfer
authorSnehal Koukuntla <snehalreddy@google.com>
Mon, 9 Sep 2024 18:01:54 +0000 (18:01 +0000)
committerMarc Zyngier <maz@kernel.org>
Tue, 10 Sep 2024 20:05:29 +0000 (21:05 +0100)
When we share memory through FF-A and the description of the buffers
exceeds the size of the mapped buffer, the fragmentation API is used.
The fragmentation API allows specifying chunks of descriptors in subsequent
FF-A fragment calls and no upper limit has been established for this.
The entire memory region transferred is identified by a handle which can be
used to reclaim the transferred memory.
To be able to reclaim the memory, the description of the buffers has to fit
in the ffa_desc_buf.
Add a bounds check on the FF-A sharing path to prevent the memory reclaim
from failing.

Also do_ffa_mem_xfer() does not need __always_inline, except for the
BUILD_BUG_ON() aspect, which gets moved to a macro.

[maz: fixed the BUILD_BUG_ON() breakage with LLVM, thanks to Wei-Lin Chang
 for the timely report]

Fixes: 634d90cf0ac65 ("KVM: arm64: Handle FFA_MEM_LEND calls from the host")
Cc: stable@vger.kernel.org
Reviewed-by: Sebastian Ene <sebastianene@google.com>
Signed-off-by: Snehal Koukuntla <snehalreddy@google.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20240909180154.3267939-1-snehalreddy@google.com
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/hyp/nvhe/ffa.c

index e715c15..e433dfa 100644 (file)
@@ -426,9 +426,9 @@ out:
        return;
 }
 
-static __always_inline void do_ffa_mem_xfer(const u64 func_id,
-                                           struct arm_smccc_res *res,
-                                           struct kvm_cpu_context *ctxt)
+static void __do_ffa_mem_xfer(const u64 func_id,
+                             struct arm_smccc_res *res,
+                             struct kvm_cpu_context *ctxt)
 {
        DECLARE_REG(u32, len, ctxt, 1);
        DECLARE_REG(u32, fraglen, ctxt, 2);
@@ -440,9 +440,6 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id,
        u32 offset, nr_ranges;
        int ret = 0;
 
-       BUILD_BUG_ON(func_id != FFA_FN64_MEM_SHARE &&
-                    func_id != FFA_FN64_MEM_LEND);
-
        if (addr_mbz || npages_mbz || fraglen > len ||
            fraglen > KVM_FFA_MBOX_NR_PAGES * PAGE_SIZE) {
                ret = FFA_RET_INVALID_PARAMETERS;
@@ -461,6 +458,11 @@ static __always_inline void do_ffa_mem_xfer(const u64 func_id,
                goto out_unlock;
        }
 
+       if (len > ffa_desc_buf.len) {
+               ret = FFA_RET_NO_MEMORY;
+               goto out_unlock;
+       }
+
        buf = hyp_buffers.tx;
        memcpy(buf, host_buffers.tx, fraglen);
 
@@ -512,6 +514,13 @@ err_unshare:
        goto out_unlock;
 }
 
+#define do_ffa_mem_xfer(fid, res, ctxt)                                \
+       do {                                                    \
+               BUILD_BUG_ON((fid) != FFA_FN64_MEM_SHARE &&     \
+                            (fid) != FFA_FN64_MEM_LEND);       \
+               __do_ffa_mem_xfer((fid), (res), (ctxt));        \
+       } while (0);
+
 static void do_ffa_mem_reclaim(struct arm_smccc_res *res,
                               struct kvm_cpu_context *ctxt)
 {