X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdePkg%2FLibrary%2FBasePeCoffLib%2FBasePeCoff.c;h=60036e11be72091a7ac111384fe47fe03c92c818;hp=af8af8a0a81e766d19dd5b9b015fbb3ed4c4eb91;hb=0054ce562f6cb73513988075670956c737b1985a;hpb=2fc59a003ed9104f9feebe0e418f2a04a50f3284
diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
index af8af8a0a8..60036e11be 100644
--- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
+++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
@@ -2,7 +2,20 @@
Base PE/COFF loader supports loading any PE32/PE32+ or TE image, but
only supports relocating IA32, x64, IPF, and EBC images.
- Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
+ Caution: This file requires additional review when modified.
+ This library will have external input - PE/COFF image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+ The basic guideline is that caller need provide ImageContext->ImageRead () with the
+ necessary data range check, to make sure when this library reads PE/COFF image, the
+ PE image buffer is always in valid range.
+ This library will also do some additional check for PE header fields.
+
+ PeCoffLoaderGetPeHeader() routine will do basic check for PE/COFF header.
+ PeCoffLoaderGetImageInfo() routine will do basic check for whole PE/COFF image.
+
+ Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
@@ -47,7 +60,12 @@ PeCoffLoaderGetPeHeaderMagicValue (
/**
- Retrieves the PE or TE Header from a PE/COFF or TE image.
+ Retrieves the PE or TE Header from a PE/COFF or TE image.
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this routine will
+ also done many checks in PE image to make sure PE image DosHeader, PeOptionHeader,
+ SizeOfHeader, Section Data Region and Security Data Region be in PE image range.
@param ImageContext The context of the image being loaded.
@param Hdr The buffer in which to return the PE32, PE32+, or TE header.
@@ -65,20 +83,30 @@ PeCoffLoaderGetPeHeader (
RETURN_STATUS Status;
EFI_IMAGE_DOS_HEADER DosHdr;
UINTN Size;
+ UINTN ReadSize;
UINT16 Magic;
+ UINT32 SectionHeaderOffset;
+ UINT32 Index;
+ CHAR8 BufferData;
+ UINTN NumberOfSections;
+ EFI_IMAGE_SECTION_HEADER SectionHeader;
//
// Read the DOS image header to check for its existence
//
Size = sizeof (EFI_IMAGE_DOS_HEADER);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
0,
&Size,
&DosHdr
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
@@ -98,14 +126,18 @@ PeCoffLoaderGetPeHeader (
// location in both images.
//
Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
ImageContext->PeCoffHeaderOffset,
&Size,
Hdr.Pe32
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
@@ -131,6 +163,83 @@ PeCoffLoaderGetPeHeader (
Magic = PeCoffLoaderGetPeHeaderMagicValue (Hdr);
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ //
+ // 1. Check FileHeader.SizeOfOptionalHeader filed.
+ //
+ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // 2. Check the OptionalHeader.SizeOfHeaders field.
+ // This field will be use like the following mode, so just compare the result.
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.
+ //
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1 < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {
+ if (Hdr.Pe32->OptionalHeader.SizeOfHeaders < (UINT32)((UINT8 *)(&Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) &Hdr)) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+ }
+
+ //
+ // 2.2 Read last byte of Hdr.Pe32.OptionalHeader.SizeOfHeaders from the file.
+ //
+ Size = 1;
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ Hdr.Pe32->OptionalHeader.SizeOfHeaders - 1,
+ &Size,
+ &BufferData
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+
+ //
+ // Check the EFI_IMAGE_DIRECTORY_ENTRY_SECURITY data.
+ // Read the last byte to make sure the data is in the image region.
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.
+ //
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) {
+ if (Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size != 0) {
+ //
+ // Check the member data to avoid overflow.
+ //
+ if ((UINT32) (~0) - Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress <
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Read last byte of section header from file
+ //
+ Size = 1;
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +
+ Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - 1,
+ &Size,
+ &BufferData
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+ }
+ }
+
//
// Use PE32 offset
//
@@ -140,6 +249,83 @@ PeCoffLoaderGetPeHeader (
ImageContext->SizeOfHeaders = Hdr.Pe32->OptionalHeader.SizeOfHeaders;
} else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ //
+ // 1. Check FileHeader.SizeOfOptionalHeader filed.
+ //
+ if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // 2. Check the OptionalHeader.SizeOfHeaders field.
+ // This field will be use like the following mode, so just compare the result.
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.
+ //
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1 < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {
+ if (Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders < (UINT32)((UINT8 *)(&Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - (UINT8 *) &Hdr)) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+ }
+
+ //
+ // 2.2 Read last byte of Hdr.Pe32Plus.OptionalHeader.SizeOfHeaders from the file.
+ //
+ Size = 1;
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - 1,
+ &Size,
+ &BufferData
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+
+ //
+ // Check the EFI_IMAGE_DIRECTORY_ENTRY_SECURITY data.
+ // Read the last byte to make sure the data is in the image region.
+ // The DataDirectory array begin with 1, not 0, so here use < to compare not <=.
+ //
+ if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) {
+ if (Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size != 0) {
+ //
+ // Check the member data to avoid overflow.
+ //
+ if ((UINT32) (~0) - Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress <
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Read last byte of section header from file
+ //
+ Size = 1;
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +
+ Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - 1,
+ &Size,
+ &BufferData
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+ }
+ }
+
//
// Use PE32+ offset
//
@@ -166,6 +352,73 @@ PeCoffLoaderGetPeHeader (
return RETURN_UNSUPPORTED;
}
+ //
+ // Check each section field.
+ //
+ if (ImageContext->IsTeImage) {
+ SectionHeaderOffset = sizeof(EFI_TE_IMAGE_HEADER);
+ NumberOfSections = (UINTN) (Hdr.Te->NumberOfSections);
+ } else {
+ SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader;
+ NumberOfSections = (UINTN) (Hdr.Pe32->FileHeader.NumberOfSections);
+ }
+
+ for (Index = 0; Index < NumberOfSections; Index++) {
+ //
+ // Read section header from file
+ //
+ Size = sizeof (EFI_IMAGE_SECTION_HEADER);
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ SectionHeaderOffset,
+ &Size,
+ &SectionHeader
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+
+ if (SectionHeader.SizeOfRawData > 0) {
+ //
+ // Check the member data to avoid overflow.
+ //
+ if ((UINT32) (~0) - SectionHeader.PointerToRawData < SectionHeader.SizeOfRawData) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
+
+ //
+ // Base on the ImageRead function to check the section data field.
+ // Read the last byte to make sure the data is in the image region.
+ //
+ Size = 1;
+ ReadSize = Size;
+ Status = ImageContext->ImageRead (
+ ImageContext->Handle,
+ SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData - 1,
+ &Size,
+ &BufferData
+ );
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
+ ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
+ return Status;
+ }
+ }
+
+ //
+ // Check next section.
+ //
+ SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
+ }
+
return RETURN_SUCCESS;
}
@@ -185,6 +438,11 @@ PeCoffLoaderGetPeHeader (
The ImageRead and Handle fields of ImageContext structure must be valid prior
to invoking this service.
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this routine will
+ also done many checks in PE image to make sure PE image DosHeader, PeOptionHeader,
+ SizeOfHeader, Section Data Region and Security Data Region be in PE image range.
+
@param ImageContext The pointer to the image context structure that describes the PE/COFF
image that needs to be examined by this function.
@@ -204,6 +462,7 @@ PeCoffLoaderGetImageInfo (
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
EFI_IMAGE_DATA_DIRECTORY *DebugDirectoryEntry;
UINTN Size;
+ UINTN ReadSize;
UINTN Index;
UINTN DebugDirectoryEntryRva;
UINTN DebugDirectoryEntryFileOffset;
@@ -270,7 +529,7 @@ PeCoffLoaderGetImageInfo (
// Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.
//
// Look at the file header to determine if relocations have been stripped, and
- // save this info in the image context for later use.
+ // save this information in the image context for later use.
//
if ((!(ImageContext->IsTeImage)) && ((Hdr.Pe32->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0)) {
ImageContext->RelocationsStripped = TRUE;
@@ -279,6 +538,15 @@ PeCoffLoaderGetImageInfo (
} else {
ImageContext->RelocationsStripped = FALSE;
}
+
+ //
+ // TE Image Relocation Data Directory Entry size is non-zero, but the Relocation Data Directory Virtual Address is zero.
+ // This case is not a valid TE image.
+ //
+ if ((ImageContext->IsTeImage) && (Hdr.Te->DataDirectory[0].Size != 0) && (Hdr.Te->DataDirectory[0].VirtualAddress == 0)) {
+ ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;
+ return RETURN_UNSUPPORTED;
+ }
if (!(ImageContext->IsTeImage)) {
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
@@ -318,14 +586,18 @@ PeCoffLoaderGetImageInfo (
// Read section header from file
//
Size = sizeof (EFI_IMAGE_SECTION_HEADER);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
SectionHeaderOffset,
&Size,
&SectionHeader
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
@@ -345,14 +617,18 @@ PeCoffLoaderGetImageInfo (
// Read next debug directory entry
//
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
- DebugDirectoryEntryFileOffset,
+ DebugDirectoryEntryFileOffset + Index,
&Size,
&DebugEntry
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {
@@ -379,14 +655,18 @@ PeCoffLoaderGetImageInfo (
// Read section header from file
//
Size = sizeof (EFI_IMAGE_SECTION_HEADER);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
SectionHeaderOffset,
&Size,
&SectionHeader
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
@@ -432,14 +712,18 @@ PeCoffLoaderGetImageInfo (
// Read next debug directory entry
//
Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);
+ ReadSize = Size;
Status = ImageContext->ImageRead (
ImageContext->Handle,
- DebugDirectoryEntryFileOffset,
+ DebugDirectoryEntryFileOffset + Index,
&Size,
&DebugEntry
);
- if (RETURN_ERROR (Status)) {
+ if (RETURN_ERROR (Status) || (Size != ReadSize)) {
ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;
+ if (Size != ReadSize) {
+ Status = RETURN_UNSUPPORTED;
+ }
return Status;
}
@@ -821,6 +1105,7 @@ PeCoffLoaderLoadImage (
EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY *ResourceDirectoryEntry;
EFI_IMAGE_RESOURCE_DIRECTORY_STRING *ResourceDirectoryString;
EFI_IMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry;
+ CHAR16 *String;
ASSERT (ImageContext != NULL);
@@ -831,7 +1116,7 @@ PeCoffLoaderLoadImage (
ImageContext->ImageError = IMAGE_ERROR_SUCCESS;
//
- // Copy the provided context info into our local version, get what we
+ // Copy the provided context information into our local version, get what we
// can from the original image, and then use that to make sure everything
// is legit.
//
@@ -1079,7 +1364,7 @@ PeCoffLoaderLoadImage (
ImageContext->FixupData = NULL;
//
- // Load the Codeview info if present
+ // Load the Codeview information if present
//
if (ImageContext->DebugDirectoryEntryRva != 0) {
if (!(ImageContext->IsTeImage)) {
@@ -1200,12 +1485,19 @@ PeCoffLoaderLoadImage (
for (Index = 0; Index < ResourceDirectory->NumberOfNamedEntries; Index++) {
if (ResourceDirectoryEntry->u1.s.NameIsString) {
+ //
+ // Check the ResourceDirectoryEntry->u1.s.NameOffset before use it.
+ //
+ if (ResourceDirectoryEntry->u1.s.NameOffset >= DirectoryEntry->Size) {
+ continue;
+ }
ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (Base + ResourceDirectoryEntry->u1.s.NameOffset);
+ String = &ResourceDirectoryString->String[0];
if (ResourceDirectoryString->Length == 3 &&
- ResourceDirectoryString->String[0] == L'H' &&
- ResourceDirectoryString->String[1] == L'I' &&
- ResourceDirectoryString->String[2] == L'I') {
+ String[0] == L'H' &&
+ String[1] == L'I' &&
+ String[2] == L'I') {
//
// Resource Type "HII" found
//
@@ -1374,6 +1666,15 @@ PeCoffLoaderRelocateImageForRuntime (
//
FixupData = RelocationData;
while (RelocBase < RelocBaseEnd) {
+ //
+ // Add check for RelocBase->SizeOfBlock field.
+ //
+ if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > RelocDir->Size)) {
+ //
+ // Data invalid, cannot continue to relocate the image, just return.
+ //
+ return;
+ }
Reloc = (UINT16 *) ((UINT8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
RelocEnd = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock);
@@ -1467,6 +1768,8 @@ PeCoffLoaderRelocateImageForRuntime (
PE/COFF image starting at byte offset FileOffset into the buffer specified by Buffer.
The size of the buffer actually read is returned in ReadSize.
+ The caller must make sure the FileOffset and ReadSize within the file scope.
+
If FileHandle is NULL, then ASSERT().
If ReadSize is NULL, then ASSERT().
If Buffer is NULL, then ASSERT().