Base PE/COFF loader supports loading any PE32/PE32+ or TE image, but\r
only supports relocating IA32, x64, IPF, and EBC images.\r
\r
- Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>\r
+ Caution: This file requires additional review when modified.\r
+ This library will have external input - PE/COFF image.\r
+ This external input must be validated carefully to avoid security issue like\r
+ buffer overflow, integer overflow.\r
+\r
+ The basic guideline is that caller need provide ImageContext->ImageRead () with the\r
+ necessary data range check, to make sure when this library reads PE/COFF image, the\r
+ PE image buffer is always in valid range.\r
+ This library will also do some additional check for PE header fields.\r
+\r
+ PeCoffLoaderGetPeHeader() routine will do basic check for PE/COFF header.\r
+ PeCoffLoaderGetImageInfo() routine will do basic check for whole PE/COFF image.\r
+\r
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.<BR>\r
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
\r
\r
/**\r
- Retrieves the PE or TE Header from a PE/COFF or TE image.\r
+ Retrieves the PE or TE Header from a PE/COFF or TE image. \r
+\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this routine will \r
+ also done many checks in PE image to make sure PE image DosHeader, PeOptionHeader, \r
+ SizeOfHeader, Section Data Region and Security Data Region be in PE image range. \r
\r
@param ImageContext The context of the image being loaded.\r
@param Hdr The buffer in which to return the PE32, PE32+, or TE header.\r
EFI_IMAGE_DOS_HEADER DosHdr;\r
UINTN Size;\r
UINT16 Magic;\r
+ UINT32 SectionHeaderOffset;\r
+ UINT32 Index;\r
+ CHAR8 BufferData;\r
+ UINTN NumberOfSections;\r
+ EFI_IMAGE_SECTION_HEADER SectionHeader;\r
\r
//\r
// Read the DOS image header to check for its existence\r
Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr);\r
\r
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
+ //\r
+ // 1. Check FileHeader.SizeOfOptionalHeader filed.\r
+ //\r
+ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // 2. Check the OptionalHeader.SizeOfHeaders field.\r
+ // This field will be use like the following mode, so just compare the result.\r
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.\r
+ //\r
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1 < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {\r
+ if (Hdr.Pe32->OptionalHeader.SizeOfHeaders < (UINT32)((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) &Hdr)) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file\r
+ //\r
+ Size = 1;\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ Hdr.Pe32->OptionalHeader.SizeOfHeaders - 1,\r
+ &Size,\r
+ &BufferData\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the EFI_IMAGE_DIRECTORY_ENTRY_SECURITY data.\r
+ // Read the last byte to make sure the data is in the image region.\r
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.\r
+ //\r
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {\r
+ if (Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size != 0) {\r
+ //\r
+ // Check the member data to avoid overflow.\r
+ //\r
+ if ((UINT32) (~0) - Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress <\r
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Read section header from file\r
+ //\r
+ Size = 1;\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +\r
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - 1,\r
+ &Size,\r
+ &BufferData\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+\r
//\r
// Use PE32 offset\r
//\r
ImageContext->SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;\r
\r
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
+ //\r
+ // 1. Check FileHeader.SizeOfOptionalHeader filed.\r
+ //\r
+ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // 2. Check the OptionalHeader.SizeOfHeaders field.\r
+ // This field will be use like the following mode, so just compare the result.\r
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.\r
+ //\r
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1 < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {\r
+ if (Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders < (UINT32)((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) &Hdr)) {\r
+ return RETURN_UNSUPPORTED;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file\r
+ //\r
+ Size = 1;\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - 1,\r
+ &Size,\r
+ &BufferData\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check the EFI_IMAGE_DIRECTORY_ENTRY_SECURITY data.\r
+ // Read the last byte to make sure the data is in the image region.\r
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.\r
+ //\r
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {\r
+ if (Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size != 0) {\r
+ //\r
+ // Check the member data to avoid overflow.\r
+ //\r
+ if ((UINT32) (~0) - Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress <\r
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Read section header from file\r
+ //\r
+ Size = 1;\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +\r
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - 1,\r
+ &Size,\r
+ &BufferData\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+ }\r
+\r
//\r
// Use PE32+ offset\r
//\r
return RETURN_UNSUPPORTED;\r
}\r
\r
+ //\r
+ // Check each section field.\r
+ //\r
+ if (ImageContext->IsTeImage) {\r
+ SectionHeaderOffset = sizeof(EFI_TE_IMAGE_HEADER);\r
+ NumberOfSections = (UINTN) (Hdr.Te->NumberOfSections);\r
+ } else {\r
+ SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader;\r
+ NumberOfSections = (UINTN) (Hdr.Pe32->FileHeader.NumberOfSections);\r
+ }\r
+\r
+ for (Index = 0; Index < NumberOfSections; Index++) {\r
+ //\r
+ // Read section header from file\r
+ //\r
+ Size = sizeof (EFI_IMAGE_SECTION_HEADER);\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ SectionHeaderOffset,\r
+ &Size,\r
+ &SectionHeader\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (SectionHeader.SizeOfRawData > 0) {\r
+ //\r
+ // Check the member data to avoid overflow.\r
+ //\r
+ if ((UINT32) (~0) - SectionHeader.PointerToRawData < SectionHeader.SizeOfRawData) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Base on the ImageRead function to check the section data field.\r
+ // Read the last byte to make sure the data is in the image region.\r
+ //\r
+ Size = 1;\r
+ Status = ImageContext->ImageRead (\r
+ ImageContext->Handle,\r
+ SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData - 1,\r
+ &Size,\r
+ &BufferData\r
+ );\r
+ if (RETURN_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check next section.\r
+ //\r
+ SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);\r
+ }\r
+\r
return RETURN_SUCCESS;\r
}\r
\r
The ImageRead and Handle fields of ImageContext structure must be valid prior \r
to invoking this service.\r
\r
+ Caution: This function may receive untrusted input.\r
+ PE/COFF image is external input, so this routine will \r
+ also done many checks in PE image to make sure PE image DosHeader, PeOptionHeader, \r
+ SizeOfHeader, Section Data Region and Security Data Region be in PE image range. \r
+\r
@param ImageContext The pointer to the image context structure that describes the PE/COFF\r
image that needs to be examined by this function.\r
\r
// Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.\r
//\r
// Look at the file header to determine if relocations have been stripped, and\r
- // save this info in the image context for later use.\r
+ // save this information in the image context for later use.\r
//\r
if ((!(ImageContext->IsTeImage)) && ((Hdr.Pe32->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) {\r
ImageContext->RelocationsStripped = TRUE;\r
} else {\r
ImageContext->RelocationsStripped = FALSE;\r
}\r
+ \r
+ //\r
+ // TE Image Relocation Data Directory Entry size is non-zero, but the Relocation Data Directory Virtual Address is zero.\r
+ // This case is not a valid TE image. \r
+ //\r
+ if ((ImageContext->IsTeImage) && (Hdr.Te->DataDirectory[0].Size != 0) && (Hdr.Te->DataDirectory[0].VirtualAddress == 0)) {\r
+ return RETURN_INVALID_PARAMETER;\r
+ }\r
\r
if (!(ImageContext->IsTeImage)) {\r
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {\r
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);\r
Status = ImageContext->ImageRead (\r
ImageContext->Handle,\r
- DebugDirectoryEntryFileOffset,\r
+ DebugDirectoryEntryFileOffset + Index,\r
&Size,\r
&DebugEntry\r
);\r
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);\r
Status = ImageContext->ImageRead (\r
ImageContext->Handle,\r
- DebugDirectoryEntryFileOffset,\r
+ DebugDirectoryEntryFileOffset + Index,\r
&Size,\r
&DebugEntry\r
);\r
EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *ResourceDirectoryEntry;\r
EFI_IMAGE_RESOURCE_DIRECTORY_STRING *ResourceDirectoryString;\r
EFI_IMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry;\r
+ CHAR16 *String;\r
\r
\r
ASSERT (ImageContext != NULL);\r
ImageContext->ImageError = IMAGE_ERROR_SUCCESS;\r
\r
//\r
- // Copy the provided context info into our local version, get what we\r
+ // Copy the provided context information into our local version, get what we\r
// can from the original image, and then use that to make sure everything\r
// is legit.\r
//\r
ImageContext->FixupData = NULL;\r
\r
//\r
- // Load the Codeview info if present\r
+ // Load the Codeview information if present\r
//\r
if (ImageContext->DebugDirectoryEntryRva != 0) {\r
if (!(ImageContext->IsTeImage)) {\r
for (Index = 0; Index < ResourceDirectory->NumberOfNamedEntries; Index++) {\r
if (ResourceDirectoryEntry->u1.s.NameIsString) {\r
ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (Base + ResourceDirectoryEntry->u1.s.NameOffset);\r
+ String = &ResourceDirectoryString->String[0];\r
\r
if (ResourceDirectoryString->Length == 3 &&\r
- ResourceDirectoryString->String[0] == L'H' &&\r
- ResourceDirectoryString->String[1] == L'I' &&\r
- ResourceDirectoryString->String[2] == L'I') {\r
+ String[0] == L'H' &&\r
+ String[1] == L'I' &&\r
+ String[2] == L'I') {\r
//\r
// Resource Type "HII" found\r
//\r