From 28186d45660c92b8d98b8b19b5f8e6ff71ea5fba Mon Sep 17 00:00:00 2001 From: ydong10 Date: Tue, 24 Apr 2012 03:00:32 +0000 Subject: [PATCH] Validate some fields in PE image to make sure not access violation for later code. Signed-off-by: Eric Dong Reviewed-by: Liming Gao git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@13211 6f19259b-4bc3-4df7-8a09-765794883524 --- MdeModulePkg/Core/Dxe/Image/Image.c | 10 +- MdePkg/Library/BasePeCoffLib/BasePeCoff.c | 196 +++++++++++++++++- .../DxeImageVerificationLib.c | 69 ++++++ .../DxeImageVerificationLib.h | 1 + .../DxeImageVerificationLib.inf | 3 +- .../DxeTpmMeasureBootLib.c | 33 +++ 6 files changed, 309 insertions(+), 3 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/Image/Image.c b/MdeModulePkg/Core/Dxe/Image/Image.c index abafa222e7..e51a9fe174 100644 --- a/MdeModulePkg/Core/Dxe/Image/Image.c +++ b/MdeModulePkg/Core/Dxe/Image/Image.c @@ -1,7 +1,7 @@ /** @file Core image handling services to load and unload PeImage. -Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2012, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -232,6 +232,14 @@ CoreReadImageFile ( UINTN EndPosition; IMAGE_FILE_HANDLE *FHand; + if (UserHandle == NULL || ReadSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MAX_ADDRESS - Offset < *ReadSize) { + return EFI_INVALID_PARAMETER; + } + FHand = (IMAGE_FILE_HANDLE *)UserHandle; ASSERT (FHand->Signature == IMAGE_FILE_HANDLE_SIGNATURE); diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c index 8b2a78e448..6342a665df 100644 --- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c +++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c @@ -47,7 +47,9 @@ 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. + 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. @@ -66,6 +68,10 @@ PeCoffLoaderGetPeHeader ( EFI_IMAGE_DOS_HEADER DosHdr; UINTN Size; UINT16 Magic; + UINT32 SectionHeaderOffset; + UINT32 Index; + CHAR8 BufferData; + EFI_IMAGE_SECTION_HEADER SectionHeader; // // Read the DOS image header to check for its existence @@ -131,6 +137,74 @@ 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) { + return RETURN_UNSUPPORTED; + } + + if (Hdr.Pe32->FileHeader.SizeOfOptionalHeader != sizeof (EFI_IMAGE_OPTIONAL_HEADER32) - (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES - Hdr.Pe32->OptionalHeader.NumberOfRvaAndSizes) * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { + 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)) { + return RETURN_UNSUPPORTED; + } + } + + // + // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file + // + Size = 1; + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Hdr.Pe32->OptionalHeader.SizeOfHeaders - 1, + &Size, + &BufferData + ); + if (RETURN_ERROR (Status)) { + 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) { + return RETURN_INVALID_PARAMETER; + } + + // + // Read section header from file + // + Size = 1; + 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)) { + return Status; + } + } + } + // // Use PE32 offset // @@ -140,6 +214,74 @@ 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) { + return RETURN_UNSUPPORTED; + } + + if (Hdr.Pe32Plus->FileHeader.SizeOfOptionalHeader != sizeof (EFI_IMAGE_OPTIONAL_HEADER32) - (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES - Hdr.Pe32Plus->OptionalHeader.NumberOfRvaAndSizes) * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { + 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)) { + return RETURN_UNSUPPORTED; + } + } + + // + // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file + // + Size = 1; + Status = ImageContext->ImageRead ( + ImageContext->Handle, + Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - 1, + &Size, + &BufferData + ); + if (RETURN_ERROR (Status)) { + 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) { + return RETURN_INVALID_PARAMETER; + } + + // + // Read section header from file + // + Size = 1; + 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)) { + return Status; + } + } + } + // // Use PE32+ offset // @@ -166,6 +308,55 @@ PeCoffLoaderGetPeHeader ( return RETURN_UNSUPPORTED; } + // + // Check each section field. + // + SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader; + for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { + // + // Read section header from file + // + Size = sizeof (EFI_IMAGE_SECTION_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeaderOffset, + &Size, + &SectionHeader + ); + if (RETURN_ERROR (Status)) { + return Status; + } + + if (SectionHeader.SizeOfRawData > 0) { + // + // Check the member data to avoid overflow. + // + if ((UINT32) (~0) - SectionHeader.PointerToRawData < SectionHeader.SizeOfRawData) { + return RETURN_INVALID_PARAMETER; + } + + // + // 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; + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData - 1, + &Size, + &BufferData + ); + if (RETURN_ERROR (Status)) { + return Status; + } + } + + // + // Check next section. + // + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + return RETURN_SUCCESS; } @@ -185,6 +376,9 @@ PeCoffLoaderGetPeHeader ( The ImageRead and Handle fields of ImageContext structure must be valid prior to invoking this service. + 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. diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c index 19852dd483..6e3e9eea95 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.c @@ -54,6 +54,50 @@ HASH_TABLE mHash[] = { { L"SHA512", 64, &mHashOidValue[40], 9, NULL, NULL, NULL, NULL } }; +/** + Reads contents of a PE/COFF image in memory buffer. + + @param FileHandle Pointer to the file handle to read the PE/COFF image. + @param FileOffset Offset into the PE/COFF image to begin the read operation. + @param ReadSize On input, the size in bytes of the requested read operation. + On output, the number of bytes actually read. + @param Buffer Output buffer that contains the data read from the PE/COFF image. + + @retval EFI_SUCCESS The specified portion of the PE/COFF image was read and the size +**/ +EFI_STATUS +EFIAPI +ImageRead ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN OUT UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + UINTN EndPosition; + + if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MAX_ADDRESS - FileOffset < *ReadSize) { + return EFI_INVALID_PARAMETER; + } + + EndPosition = FileOffset + *ReadSize; + if (EndPosition > mImageSize) { + *ReadSize = (UINT32)(mImageSize - FileOffset); + } + + if (FileOffset >= mImageSize) { + *ReadSize = 0; + } + + CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize); + + return EFI_SUCCESS; +} + /** Get the image type. @@ -422,6 +466,10 @@ HashPeImage ( if (mImageSize > SumOfBytesHashed) { HashBase = mImageBase + SumOfBytesHashed; if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (mImageSize - SumOfBytesHashed < mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { + Status = FALSE; + goto Done; + } // // Use PE32 offset. // @@ -430,6 +478,10 @@ HashPeImage ( mNtHeader.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - SumOfBytesHashed); } else { + if (mImageSize - SumOfBytesHashed < mNtHeader.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { + Status = FALSE; + goto Done; + } // // Use PE32+ offset. // @@ -1130,6 +1182,7 @@ DxeImageVerificationHandler ( WIN_CERTIFICATE *WinCertificate; UINT32 Policy; UINT8 *SecureBootEnable; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; if (File == NULL) { return EFI_INVALID_PARAMETER; @@ -1216,6 +1269,22 @@ DxeImageVerificationHandler ( } mImageBase = (UINT8 *) FileBuffer; mImageSize = FileSize; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = (VOID *) FileBuffer; + ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) ImageRead; + + // + // Get information about the image being loaded + // + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + // + // The information can't be got from the invalid PeImage + // + goto Done; + } + DosHdr = (EFI_IMAGE_DOS_HEADER *) mImageBase; if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { // diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h index 881e4e5c35..55371e90bf 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.h @@ -28,6 +28,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include #include diff --git a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf index 1dda6774fa..860d64ba83 100644 --- a/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf +++ b/SecurityPkg/Library/DxeImageVerificationLib/DxeImageVerificationLib.inf @@ -2,7 +2,7 @@ # The library instance provides security service of image verification. # Image verification Library module supports UEFI2.3.1 # -# Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.
+# Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
# This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at @@ -48,6 +48,7 @@ DevicePathLib BaseCryptLib SecurityManagementLib + PeCoffLib [Protocols] gEfiFirmwareVolume2ProtocolGuid diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c index d3c7bfec62..f0039c8048 100644 --- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c @@ -36,6 +36,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. BOOLEAN mMeasureGptTableFlag = FALSE; EFI_GUID mZeroGuid = {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; UINTN mMeasureGptCount = 0; +VOID *mFileBuffer; +UINTN mImageSize; /** Reads contents of a PE/COFF image in memory buffer. @@ -57,7 +59,27 @@ ImageRead ( OUT VOID *Buffer ) { + UINTN EndPosition; + + if (FileHandle == NULL || ReadSize == NULL || Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MAX_ADDRESS - FileOffset < *ReadSize) { + return EFI_INVALID_PARAMETER; + } + + EndPosition = FileOffset + *ReadSize; + if (EndPosition > mImageSize) { + *ReadSize = (UINT32)(mImageSize - FileOffset); + } + + if (FileOffset >= mImageSize) { + *ReadSize = 0; + } + CopyMem (Buffer, (UINT8 *)((UINTN) FileHandle + FileOffset), *ReadSize); + return EFI_SUCCESS; } @@ -495,6 +517,10 @@ TcgMeasurePeImage ( if (ImageSize > SumOfBytesHashed) { HashBase = (UINT8 *) (UINTN) ImageAddress + SumOfBytesHashed; if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + if (ImageSize - SumOfBytesHashed < Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { + Status = EFI_INVALID_PARAMETER; + goto Finish; + } // // Use PE32 offset // @@ -502,6 +528,10 @@ TcgMeasurePeImage ( Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size - SumOfBytesHashed); } else { + if (ImageSize - SumOfBytesHashed < Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size) { + Status = EFI_INVALID_PARAMETER; + goto Finish; + } // // Use PE32+ offset // @@ -735,6 +765,9 @@ DxeTpmMeasureBootHandler ( goto Finish; } + mImageSize = FileSize; + mFileBuffer = FileBuffer; + // // Measure PE Image // -- 2.39.2