]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/BasePeCoffLib/BasePeCoff.c
Validate some fields in PE image to make sure not access violation for later code.
[mirror_edk2.git] / MdePkg / Library / BasePeCoffLib / BasePeCoff.c
index af8af8a0a81e766d19dd5b9b015fbb3ed4c4eb91..6342a665df329875aa2d1f983fec681974f7e826 100644 (file)
@@ -2,7 +2,7 @@
   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
+  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
@@ -47,7 +47,9 @@ PeCoffLoaderGetPeHeaderMagicValue (
 \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
+  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
@@ -66,6 +68,10 @@ PeCoffLoaderGetPeHeader (
   EFI_IMAGE_DOS_HEADER  DosHdr;\r
   UINTN                 Size;\r
   UINT16                Magic;\r
+  UINT32                SectionHeaderOffset;\r
+  UINT32                Index;\r
+  CHAR8                 BufferData;\r
+  EFI_IMAGE_SECTION_HEADER  SectionHeader;\r
 \r
   //\r
   // Read the DOS image header to check for its existence\r
@@ -131,6 +137,74 @@ PeCoffLoaderGetPeHeader (
     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
+      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)) {\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
@@ -140,6 +214,74 @@ PeCoffLoaderGetPeHeader (
       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
+      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)) {\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
@@ -166,6 +308,55 @@ PeCoffLoaderGetPeHeader (
     return RETURN_UNSUPPORTED;\r
   }\r
 \r
+  //\r
+  // Check each section field.\r
+  //\r
+  SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader;\r
+  for (Index = 0; Index < Hdr.Pe32->FileHeader.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
@@ -185,6 +376,9 @@ PeCoffLoaderGetPeHeader (
   The ImageRead and Handle fields of ImageContext structure must be valid prior \r
   to invoking this service.\r
 \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
@@ -270,7 +464,7 @@ PeCoffLoaderGetImageInfo (
   // 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
@@ -279,6 +473,14 @@ PeCoffLoaderGetImageInfo (
   } 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
@@ -347,7 +549,7 @@ PeCoffLoaderGetImageInfo (
           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
@@ -434,7 +636,7 @@ PeCoffLoaderGetImageInfo (
         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
@@ -821,6 +1023,7 @@ PeCoffLoaderLoadImage (
   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
@@ -831,7 +1034,7 @@ PeCoffLoaderLoadImage (
   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
@@ -1079,7 +1282,7 @@ PeCoffLoaderLoadImage (
   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
@@ -1201,11 +1404,12 @@ PeCoffLoaderLoadImage (
         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