fix(ff-a): check that hypervisor's TX buffer is set-up
The `api_get_rxtx_description` function reads from the hypervisor's TX
buffer in the case of a forwarded RXTX_MAP call, but it is possible to
make a forwarded RXTX_MAP call without first setting up the hypervisor's
RX/TX buffers. This would result in a null-pointer dereference.
This patch fixes this issue by returning FFA_INVALID_PARAMETERS if the
hypervisor's TX buffer is not set-up.
Change-Id: Iecd7fb39a266270465fc627908acd5b811c80ddc
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/src/api.c b/src/api.c
index 9d56f54..df5ae98 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1700,38 +1700,64 @@
return ret;
}
-static void api_get_rxtx_description(struct vm *current_vm, ipaddr_t *send,
- ipaddr_t *recv, uint32_t *page_count,
- ffa_id_t *owner_vm_id)
+/**
+ * Read the buffer addresses and VM ID of an FFA_RXTX_MAP request. Handles
+ * forwarded messages by reading from the hypervisor's TX buffer.
+ *
+ * Returns the VM/SP ID on success.
+ *
+ * Returns `HF_INVALID_VM_ID` when the arguments provided via the ABI
+ * FFA_RXTX_MAP indicate the SPMC should retrieve the RXTX description from the
+ * Hypervisor RXTX buffers, and the hypervisor hasn't given its own RXTX buffers
+ * for the SPMC to map.
+ */
+static ffa_id_t api_get_rxtx_description(struct vm *current_vm, ipaddr_t *send,
+ ipaddr_t *recv, uint32_t *page_count)
{
+ bool forwarded;
+ struct vm_locked vm_locked;
+ ffa_id_t owner_vm_id;
+ struct ffa_endpoint_rx_tx_descriptor *endpoint_desc;
+ struct ffa_composite_memory_region *rx_region;
+ struct ffa_composite_memory_region *tx_region;
+
/*
* If the message has been forwarded the effective addresses are in
* hypervisor's TX buffer.
*/
- bool forwarded = (current_vm->id == HF_OTHER_WORLD_ID) &&
- (ipa_addr(*send) == 0) && (ipa_addr(*recv) == 0) &&
- (*page_count == 0);
+ forwarded = (current_vm->id == HF_OTHER_WORLD_ID) &&
+ (ipa_addr(*send) == 0) && (ipa_addr(*recv) == 0) &&
+ (*page_count == 0);
if (forwarded) {
- struct vm_locked vm_locked = vm_lock(current_vm);
- struct ffa_endpoint_rx_tx_descriptor *endpoint_desc =
- (struct ffa_endpoint_rx_tx_descriptor *)
- vm_locked.vm->mailbox.send;
- struct ffa_composite_memory_region *rx_region =
- ffa_enpoint_get_rx_memory_region(endpoint_desc);
- struct ffa_composite_memory_region *tx_region =
- ffa_enpoint_get_tx_memory_region(endpoint_desc);
+ vm_locked = vm_lock(current_vm);
+ endpoint_desc = (struct ffa_endpoint_rx_tx_descriptor *)
+ vm_locked.vm->mailbox.send;
- *owner_vm_id = endpoint_desc->endpoint_id;
+ if (endpoint_desc == NULL) {
+ dlog_error(
+ "Trying to access RXTX description, but "
+ "hypervisor has not provided RXTX buffers\n");
+ vm_unlock(&vm_locked);
+ return HF_INVALID_VM_ID;
+ }
+
+ rx_region = ffa_endpoint_get_rx_memory_region(endpoint_desc);
+ tx_region = ffa_endpoint_get_tx_memory_region(endpoint_desc);
+
+ owner_vm_id = endpoint_desc->endpoint_id;
*recv = ipa_init(rx_region->constituents[0].address);
*send = ipa_init(tx_region->constituents[0].address);
*page_count = rx_region->constituents[0].page_count;
vm_unlock(&vm_locked);
} else {
- *owner_vm_id = current_vm->id;
+ owner_vm_id = current_vm->id;
}
+
+ return owner_vm_id;
}
+
/**
* Configures the VM to send/receive data through the specified pages. The pages
* must not be shared. Locking of the page tables combined with a local memory
@@ -1761,8 +1787,11 @@
* Get the original buffer addresses and VM ID in case of forwarded
* message.
*/
- api_get_rxtx_description(current->vm, &send, &recv, &page_count,
- &owner_vm_id);
+ owner_vm_id = api_get_rxtx_description(current->vm, &send, &recv,
+ &page_count);
+ if (owner_vm_id == HF_INVALID_VM_ID) {
+ return ffa_error(FFA_INVALID_PARAMETERS);
+ }
owner_vm_locked = plat_ffa_vm_find_locked_create(owner_vm_id);
if (owner_vm_locked.vm == NULL) {