From: Ana Bobillo <[email protected]>
efi_capsule_update_firmware() reads the FMP capsule header, its item
offset list and each image header without checking them against the
size of the capsule:
- the capsule may end before the end of the FMP capsule header;
- the item offset list is indexed up to embedded_driver_count +
payload_item_count - 1, which may run past the end of the capsule;
- the check of an item offset adds the image header size to a 64-bit
offset and can wrap around, letting an offset close to U64_MAX
through;
- update_image_size is passed on to the FMP driver without checking
that the image fits in the capsule.
On the capsule on disk path all of this happens before the FMP driver
authenticates the image: a capsule file in \EFI\UpdateCapsule on the
ESP makes U-Boot read outside the capsule buffer the next time capsule
on disk runs.
Check each of them against the capsule size, and check that the vendor
code fits after the image too, along the lines of EDK2's
ValidateFmpCapsule().
Fixes: 8d99026f0697 ("efi_loader: capsule: support firmware update")
Signed-off-by: Ana Bobillo <[email protected]>
---
lib/efi_loader/efi_capsule.c | 45 ++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 12 deletions(-)
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index e8835315e86..a8d57630a6f 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -538,11 +538,13 @@ static efi_status_t efi_capsule_update_firmware(
{
struct efi_firmware_management_capsule_header *capsule;
struct efi_firmware_management_capsule_image_header *image;
- size_t capsule_size, image_binary_size;
+ size_t capsule_size, image_binary_size, remaining;
void *image_binary, *vendor_code;
efi_handle_t *handles;
efi_uintn_t no_handles;
int item;
+ uint item_count;
+ u64 item_offset;
struct efi_firmware_management_protocol *fmp;
u16 *abort_reason;
efi_guid_t *image_type_id;
@@ -588,18 +590,27 @@ static efi_status_t efi_capsule_update_firmware(
return EFI_UNSUPPORTED;
}
- /* sanity check */
- if (capsule_data->header_size < sizeof(*capsule) ||
- capsule_data->header_size >= capsule_data->capsule_image_size)
- return EFI_INVALID_PARAMETER;
-
capsule = (void *)capsule_data + capsule_data->header_size;
capsule_size = capsule_data->capsule_image_size
- capsule_data->header_size;
+ /* sanity check */
+ if (capsule_size < sizeof(*capsule)) {
+ log_err("Capsule does not have enough data\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
if (capsule->version != 0x00000001)
return EFI_UNSUPPORTED;
+ item_count = capsule->embedded_driver_count +
+ capsule->payload_item_count;
+ if (item_count > (capsule_size - sizeof(*capsule)) /
+ sizeof(*capsule->item_offset_list)) {
+ log_err("Capsule does not have enough data\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
handles = NULL;
ret = EFI_CALL(efi_locate_handle_buffer(
BY_PROTOCOL,
@@ -609,24 +620,34 @@ static efi_status_t efi_capsule_update_firmware(
return EFI_UNSUPPORTED;
/* Payload */
- for (item = capsule->embedded_driver_count;
- item < capsule->embedded_driver_count
- + capsule->payload_item_count; item++) {
+ for (item = capsule->embedded_driver_count; item < item_count;
+ item++) {
/* sanity check */
- if ((capsule->item_offset_list[item] + sizeof(*image)
- >= capsule_size)) {
+ item_offset = capsule->item_offset_list[item];
+ if (item_offset >= capsule_size ||
+ capsule_size - item_offset <= sizeof(*image)) {
log_err("Capsule does not have enough data\n");
ret = EFI_INVALID_PARAMETER;
goto out;
}
- image = (void *)capsule + capsule->item_offset_list[item];
+ image = (void *)capsule + item_offset;
if (image->version != 0x00000003) {
ret = EFI_UNSUPPORTED;
goto out;
}
+ /* the image and the vendor code must fit in the capsule */
+ remaining = capsule_size - item_offset - sizeof(*image);
+ if (image->update_image_size > remaining ||
+ image->update_vendor_code_size >
+ remaining - image->update_image_size) {
+ log_err("Capsule does not have enough data\n");
+ ret = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+
/* find a device for update firmware */
fmp = efi_fmp_find(&image->update_image_type_id,
image->update_image_index,