From: Ana Bobillo <[email protected]>

efi_loader_relocate() checks each relocation block and entry against the
end of the relocation table and each target against the image size, but
the table itself is taken from the base relocation data directory as it
is: its address and size are not checked against the image, so the
blocks are read from wherever they point, up to 4 GiB past the image.
The directory is also read when NumberOfRvaAndSizes says the image has
none, in which case its bytes belong to the section table.

Use the base relocation directory only if the image has one, and reject
a relocation table that does not lie within the image, as EDK2's
PeCoffLoaderRelocateImage() does.

This runs before the image is rejected when Secure Boot fails to verify
it.

Fixes: cb149c66342c ("efi_loader: Add PE image loader")
Signed-off-by: Ana Bobillo <[email protected]>
---
 lib/efi_loader/efi_image_loader.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/lib/efi_loader/efi_image_loader.c 
b/lib/efi_loader/efi_image_loader.c
index f9cd2cb274c..c0677b440e8 100644
--- a/lib/efi_loader/efi_image_loader.c
+++ b/lib/efi_loader/efi_image_loader.c
@@ -117,9 +117,17 @@ static efi_status_t efi_loader_relocate(const 
IMAGE_BASE_RELOCATION *rel,
                                        unsigned long virt_size)
 {
        unsigned long delta = (unsigned long)efi_reloc - pref_address;
+       unsigned long rel_off = (const char *)rel - (const char *)efi_reloc;
        const IMAGE_BASE_RELOCATION *end;
        int i;
 
+       /* The relocation table must lie within the image */
+       if (rel_size &&
+           (rel_off > virt_size || rel_size > virt_size - rel_off)) {
+               log_debug("Relocation table exceeds image size\n");
+               return EFI_LOAD_ERROR;
+       }
+
        if (delta == 0)
                return EFI_SUCCESS;
 
@@ -1111,7 +1119,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj 
*handle,
                        goto err;
                }
                handle->entry = efi_reloc + opt->AddressOfEntryPoint;
-               rel_size = opt->DataDirectory[rel_idx].Size;
+               rel_size = opt->NumberOfRvaAndSizes > rel_idx ?
+                          opt->DataDirectory[rel_idx].Size : 0;
                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
        } else if (nt->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
                IMAGE_OPTIONAL_HEADER32 *opt = &nt->OptionalHeader;
@@ -1127,7 +1136,8 @@ efi_status_t efi_load_pe(struct efi_loaded_image_obj 
*handle,
                        goto err;
                }
                handle->entry = efi_reloc + opt->AddressOfEntryPoint;
-               rel_size = opt->DataDirectory[rel_idx].Size;
+               rel_size = opt->NumberOfRvaAndSizes > rel_idx ?
+                          opt->DataDirectory[rel_idx].Size : 0;
                rel = efi_reloc + opt->DataDirectory[rel_idx].VirtualAddress;
        } else {
                log_err("Invalid optional header magic %x\n",

Reply via email to