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().