summaryrefslogtreecommitdiff
path: root/pe-relocate.c
diff options
context:
space:
mode:
Diffstat (limited to 'pe-relocate.c')
-rw-r--r--pe-relocate.c127
1 files changed, 119 insertions, 8 deletions
diff --git a/pe-relocate.c b/pe-relocate.c
index bde71729..6f2af4e4 100644
--- a/pe-relocate.c
+++ b/pe-relocate.c
@@ -368,21 +368,28 @@ image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
*/
EFI_STATUS
read_header(void *data, unsigned int datasize,
- PE_COFF_LOADER_IMAGE_CONTEXT *context)
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ bool check_secdir)
{
EFI_IMAGE_DOS_HEADER *DosHdr = data;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
unsigned long FileAlignment = 0;
- UINT16 DllFlags;
size_t dos_sz = 0;
size_t tmpsz0, tmpsz1;
+ /*
+ * It has to be big enough to hold the DOS header; right now we
+ * don't support images without it.
+ */
if (datasize < sizeof (*DosHdr)) {
perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * It must have a valid DOS header
+ */
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
if (DosHdr->e_lfanew < sizeof (*DosHdr) ||
DosHdr->e_lfanew > datasize - 4) {
@@ -394,11 +401,17 @@ read_header(void *data, unsigned int datasize,
PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
}
+ /*
+ * Has to be big enough to hold a PE header
+ */
if (datasize - dos_sz < sizeof (PEHdr->Pe32)) {
perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * If it's 64-bit, it has to hold the PE32+ header
+ */
if (image_is_64_bit(PEHdr) &&
(datasize - dos_sz < sizeof (PEHdr->Pe32Plus))) {
perror(L"Invalid image\n");
@@ -426,6 +439,13 @@ read_header(void *data, unsigned int datasize,
OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
}
+ /*
+ * Set up our file alignment and section alignment expectations to
+ * be mostly sane.
+ *
+ * This probably should have a check for /power/ of two not just
+ * multiple, but in practice it hasn't been an issue.
+ */
if (FileAlignment % 2 != 0) {
perror(L"File Alignment is invalid (%d)\n", FileAlignment);
return EFI_UNSUPPORTED;
@@ -439,11 +459,24 @@ read_header(void *data, unsigned int datasize,
context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
+ /*
+ * Check and make sure the space for data directory entries is as
+ * large as we expect.
+ *
+ * In truth we could set this number smaller if we needed to -
+ * currently it's 16 but we only care about #4 and #5 (the fifth
+ * and sixth ones) - but it hasn't been a problem. If it's too
+ * weird we'll fail trying to allocate it.
+ */
if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
perror(L"Image header too large\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the OptionalHeaderSize and the end of the Data
+ * Directory match up sanely
+ */
if (checked_mul(sizeof(EFI_IMAGE_DATA_DIRECTORY), EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, &tmpsz0) ||
checked_sub(OptHeaderSize, tmpsz0, &HeaderWithoutDataDir) ||
checked_sub((size_t)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, HeaderWithoutDataDir, &tmpsz0) ||
@@ -453,6 +486,9 @@ read_header(void *data, unsigned int datasize,
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the SectionHeaderOffset field is within the image.
+ */
if (checked_add((size_t)DosHdr->e_lfanew, sizeof(UINT32), &tmpsz0) ||
checked_add(tmpsz0, sizeof(EFI_IMAGE_FILE_HEADER), &tmpsz0) ||
checked_add(tmpsz0, PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, &SectionHeaderOffset)) {
@@ -460,18 +496,29 @@ read_header(void *data, unsigned int datasize,
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the sections headers themselves are within the image
+ */
if (checked_sub((size_t)context->ImageSize, SectionHeaderOffset, &tmpsz0) ||
(tmpsz0 / EFI_IMAGE_SIZEOF_SECTION_HEADER <= context->NumberOfSections)) {
perror(L"Image sections overflow image size\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the section headers fit within the total headers
+ */
if (checked_sub((size_t)context->SizeOfHeaders, SectionHeaderOffset, &tmpsz0) ||
(tmpsz0 / EFI_IMAGE_SIZEOF_SECTION_HEADER < (UINT32)context->NumberOfSections)) {
perror(L"Image sections overflow section headers\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the section headers are actually within the data
+ * we've read. Might be duplicative of the ImageSize one, but it
+ * won't hurt.
+ */
if (checked_mul((size_t)context->NumberOfSections, sizeof(EFI_IMAGE_SECTION_HEADER), &tmpsz0) ||
checked_add(tmpsz0, SectionHeaderOffset, &tmpsz0) ||
(tmpsz0 > datasize)) {
@@ -479,6 +526,9 @@ read_header(void *data, unsigned int datasize,
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the optional header fits in the image.
+ */
if (checked_sub((size_t)(uintptr_t)PEHdr, (size_t)(uintptr_t)data, &tmpsz0) ||
checked_add(tmpsz0, sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION), &tmpsz0) ||
(tmpsz0 > datasize)) {
@@ -486,11 +536,17 @@ read_header(void *data, unsigned int datasize,
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that this claims to be a PE binary
+ */
if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
perror(L"Unsupported image type\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that relocations aren't stripped, because that won't work.
+ */
if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
perror(L"Unsupported image - Relocations have been stripped\n");
return EFI_UNSUPPORTED;
@@ -503,27 +559,37 @@ read_header(void *data, unsigned int datasize,
context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- DllFlags = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics;
+ context->DllCharacteristics = PEHdr->Pe32Plus.OptionalHeader.DllCharacteristics;
} else {
context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- DllFlags = PEHdr->Pe32.OptionalHeader.DllCharacteristics;
+ context->DllCharacteristics = PEHdr->Pe32.OptionalHeader.DllCharacteristics;
}
+ /*
+ * If NX_COMPAT is required, check that it's set.
+ */
if ((mok_policy & MOK_POLICY_REQUIRE_NX) &&
- !(DllFlags & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) {
+ !(context->DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT)) {
perror(L"Policy requires NX, but image does not support NX\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the file header fits within the image.
+ */
if (checked_add((size_t)(uintptr_t)PEHdr, PEHdr->Pe32.FileHeader.SizeOfOptionalHeader, &tmpsz0) ||
checked_add(tmpsz0, sizeof(UINT32), &tmpsz0) ||
checked_add(tmpsz0, sizeof(EFI_IMAGE_FILE_HEADER), &tmpsz0)) {
perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
+
+ /*
+ * Check that the first section header is within the image data
+ */
context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)(uintptr_t)tmpsz0;
if ((uint64_t)(uintptr_t)(context->FirstSection)
> (uint64_t)(uintptr_t)data + datasize) {
@@ -531,24 +597,69 @@ read_header(void *data, unsigned int datasize,
return EFI_UNSUPPORTED;
}
+ /*
+ * Check that the headers fit within the image.
+ */
if (context->ImageSize < context->SizeOfHeaders) {
perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
+ /*
+ * check that the data directory fits within the image.
+ */
if (checked_sub((size_t)(uintptr_t)context->SecDir, (size_t)(uintptr_t)data, &tmpsz0) ||
(tmpsz0 > datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
perror(L"Invalid image\n");
return EFI_UNSUPPORTED;
}
- if (context->SecDir->VirtualAddress > datasize ||
- (context->SecDir->VirtualAddress == datasize &&
- context->SecDir->Size > 0)) {
+ /*
+ * Check that the certificate table is within the binary -
+ * "VirtualAddress" is a misnomer here, it's a relative offset to
+ * the image's load address, so compared to datasize it should be
+ * absolute.
+ */
+ if (check_secdir &&
+ (context->SecDir->VirtualAddress > datasize ||
+ (context->SecDir->VirtualAddress == datasize &&
+ context->SecDir->Size > 0))) {
+ dprint(L"context->SecDir->VirtualAddress:0x%llx context->SecDir->Size:0x%llx datasize:0x%llx\n",
+ context->SecDir->VirtualAddress, context->SecDir->Size, datasize);
perror(L"Malformed security header\n");
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
+void
+get_shim_nx_capability(EFI_HANDLE image_handle)
+{
+ EFI_LOADED_IMAGE_PROTOCOL*li = NULL;
+ EFI_STATUS efi_status;
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+
+ efi_status = BS->HandleProtocol(image_handle, &gEfiLoadedImageProtocolGuid, (void **)&li);
+ if (EFI_ERROR(efi_status) || !li) {
+ dprint(L"Could not get loaded image protocol: %r\n", efi_status);
+ return;
+ }
+
+ ZeroMem(&context, sizeof(context));
+ efi_status = read_header(li->ImageBase, li->ImageSize, &context, false);
+ if (EFI_ERROR(efi_status)) {
+ dprint(L"Couldn't parse image header: %r\n", efi_status);
+ return;
+ }
+
+ dprint(L"DllCharacteristics:0x%lx\n", context.DllCharacteristics);
+ if (context.DllCharacteristics & EFI_IMAGE_DLLCHARACTERISTICS_NX_COMPAT) {
+ dprint(L"Setting HSI from %a to %a\n",
+ decode_hsi_bits(hsi_status),
+ decode_hsi_bits(hsi_status | SHIM_HSI_STATUS_NX));
+ hsi_status |= SHIM_HSI_STATUS_NX;
+ }
+}
+
+
// vim:fenc=utf-8:tw=75:noet