]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdePkg/Library/BasePeCoffLib/BasePeCoff.c
Enhance the error code info.
[mirror_edk2.git] / MdePkg / Library / BasePeCoffLib / BasePeCoff.c
index 6342a665df329875aa2d1f983fec681974f7e826..60036e11be72091a7ac111384fe47fe03c92c818 100644 (file)
@@ -2,6 +2,19 @@
   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
+  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
@@ -48,7 +61,10 @@ PeCoffLoaderGetPeHeaderMagicValue (
 \r
 /**\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
+\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
@@ -67,24 +83,30 @@ PeCoffLoaderGetPeHeader (
   RETURN_STATUS         Status;\r
   EFI_IMAGE_DOS_HEADER  DosHdr;\r
   UINTN                 Size;\r
+  UINTN                 ReadSize;\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
   //\r
   Size = sizeof (EFI_IMAGE_DOS_HEADER);\r
+  ReadSize = Size;\r
   Status = ImageContext->ImageRead (\r
                            ImageContext->Handle,\r
                            0,\r
                            &Size,\r
                            &DosHdr\r
                            );\r
-  if (RETURN_ERROR (Status)) {\r
+  if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
     ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+    if (Size != ReadSize) {\r
+      Status = RETURN_UNSUPPORTED;\r
+    }\r
     return Status;\r
   }\r
 \r
@@ -104,14 +126,18 @@ PeCoffLoaderGetPeHeader (
   // location in both images.\r
   //\r
   Size = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);\r
+  ReadSize = Size;\r
   Status = ImageContext->ImageRead (\r
                            ImageContext->Handle,\r
                            ImageContext->PeCoffHeaderOffset,\r
                            &Size,\r
                            Hdr.Pe32\r
                            );\r
-  if (RETURN_ERROR (Status)) {\r
+  if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
     ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+    if (Size != ReadSize) {\r
+      Status = RETURN_UNSUPPORTED;\r
+    }\r
     return Status;\r
   }\r
 \r
@@ -141,10 +167,7 @@ PeCoffLoaderGetPeHeader (
       // 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
+        ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
         return RETURN_UNSUPPORTED;\r
       }\r
 \r
@@ -155,21 +178,27 @@ PeCoffLoaderGetPeHeader (
       //\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
+          ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
           return RETURN_UNSUPPORTED;\r
         }\r
       }\r
 \r
       //\r
-      // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file\r
+      // 2.2 Read last byte of Hdr.Pe32.OptionalHeader.SizeOfHeaders from the file.\r
       //\r
       Size = 1;\r
+      ReadSize = Size;\r
       Status = ImageContext->ImageRead (\r
                                ImageContext->Handle,\r
                                Hdr.Pe32->OptionalHeader.SizeOfHeaders - 1,\r
                                &Size,\r
                                &BufferData\r
                                );\r
-      if (RETURN_ERROR (Status)) {\r
+      if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+        ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+        if (Size != ReadSize) {\r
+          Status = RETURN_UNSUPPORTED;\r
+        }\r
         return Status;\r
       }\r
 \r
@@ -185,13 +214,15 @@ PeCoffLoaderGetPeHeader (
           //\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
+            ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
+            return RETURN_UNSUPPORTED;\r
           }\r
 \r
           //\r
-          // Read section header from file\r
+          // Read last byte of section header from file\r
           //\r
           Size = 1;\r
+          ReadSize = Size;\r
           Status = ImageContext->ImageRead (\r
                                    ImageContext->Handle,\r
                                    Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +\r
@@ -199,7 +230,11 @@ PeCoffLoaderGetPeHeader (
                                    &Size,\r
                                    &BufferData\r
                                    );\r
-          if (RETURN_ERROR (Status)) {\r
+          if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+            ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+            if (Size != ReadSize) {\r
+              Status = RETURN_UNSUPPORTED;\r
+            }\r
             return Status;\r
           }\r
         }\r
@@ -218,10 +253,7 @@ PeCoffLoaderGetPeHeader (
       // 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
+        ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
         return RETURN_UNSUPPORTED;\r
       }\r
 \r
@@ -232,21 +264,27 @@ PeCoffLoaderGetPeHeader (
       //\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
+          ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
           return RETURN_UNSUPPORTED;\r
         }\r
       }\r
 \r
       //\r
-      // Read Hdr.Pe32.OptionalHeader.SizeOfHeaders data from file\r
+      // 2.2 Read last byte of Hdr.Pe32Plus.OptionalHeader.SizeOfHeaders from the file.\r
       //\r
       Size = 1;\r
+      ReadSize = Size;\r
       Status = ImageContext->ImageRead (\r
                                ImageContext->Handle,\r
                                Hdr.Pe32Plus->OptionalHeader.SizeOfHeaders - 1,\r
                                &Size,\r
                                &BufferData\r
                                );\r
-      if (RETURN_ERROR (Status)) {\r
+      if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+        ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+        if (Size != ReadSize) {\r
+          Status = RETURN_UNSUPPORTED;\r
+        }\r
         return Status;\r
       }\r
 \r
@@ -262,13 +300,15 @@ PeCoffLoaderGetPeHeader (
           //\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
+            ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
+            return RETURN_UNSUPPORTED;\r
           }\r
 \r
           //\r
-          // Read section header from file\r
+          // Read last byte of section header from file\r
           //\r
           Size = 1;\r
+          ReadSize = Size;\r
           Status = ImageContext->ImageRead (\r
                                    ImageContext->Handle,\r
                                    Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress +\r
@@ -276,7 +316,11 @@ PeCoffLoaderGetPeHeader (
                                    &Size,\r
                                    &BufferData\r
                                    );\r
-          if (RETURN_ERROR (Status)) {\r
+          if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+            ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+            if (Size != ReadSize) {\r
+              Status = RETURN_UNSUPPORTED;\r
+            }\r
             return Status;\r
           }\r
         }\r
@@ -311,19 +355,31 @@ PeCoffLoaderGetPeHeader (
   //\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
+  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
+    ReadSize = Size;\r
     Status = ImageContext->ImageRead (\r
                              ImageContext->Handle,\r
                              SectionHeaderOffset,\r
                              &Size,\r
                              &SectionHeader\r
                              );\r
-    if (RETURN_ERROR (Status)) {\r
+    if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+      ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+      if (Size != ReadSize) {\r
+        Status = RETURN_UNSUPPORTED;\r
+      }\r
       return Status;\r
     }\r
 \r
@@ -332,7 +388,8 @@ PeCoffLoaderGetPeHeader (
       // Check the member data to avoid overflow.\r
       //\r
       if ((UINT32) (~0) - SectionHeader.PointerToRawData < SectionHeader.SizeOfRawData) {\r
-        return RETURN_INVALID_PARAMETER;\r
+        ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
+        return RETURN_UNSUPPORTED;\r
       }\r
 \r
       //\r
@@ -340,13 +397,18 @@ PeCoffLoaderGetPeHeader (
       // Read the last byte to make sure the data is in the image region.\r
       //\r
       Size = 1;\r
+      ReadSize = Size;\r
       Status = ImageContext->ImageRead (\r
                                ImageContext->Handle,\r
                                SectionHeader.PointerToRawData + SectionHeader.SizeOfRawData - 1,\r
                                &Size,\r
                                &BufferData\r
                                );\r
-      if (RETURN_ERROR (Status)) {\r
+      if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
+        ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+        if (Size != ReadSize) {\r
+          Status = RETURN_UNSUPPORTED;\r
+        }\r
         return Status;\r
       }\r
     }\r
@@ -376,7 +438,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
+  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
@@ -398,6 +462,7 @@ PeCoffLoaderGetImageInfo (
   EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr;\r
   EFI_IMAGE_DATA_DIRECTORY              *DebugDirectoryEntry;\r
   UINTN                                 Size;\r
+  UINTN                                 ReadSize;\r
   UINTN                                 Index;\r
   UINTN                                 DebugDirectoryEntryRva;\r
   UINTN                                 DebugDirectoryEntryFileOffset;\r
@@ -479,7 +544,8 @@ PeCoffLoaderGetImageInfo (
   // 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
+    ImageContext->ImageError = IMAGE_ERROR_UNSUPPORTED;\r
+    return RETURN_UNSUPPORTED;\r
   }\r
 \r
   if (!(ImageContext->IsTeImage)) {\r
@@ -520,14 +586,18 @@ PeCoffLoaderGetImageInfo (
         // Read section header from file\r
         //\r
         Size = sizeof (EFI_IMAGE_SECTION_HEADER);\r
+        ReadSize = Size;\r
         Status = ImageContext->ImageRead (\r
                                  ImageContext->Handle,\r
                                  SectionHeaderOffset,\r
                                  &Size,\r
                                  &SectionHeader\r
                                  );\r
-        if (RETURN_ERROR (Status)) {\r
+        if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
           ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+          if (Size != ReadSize) {\r
+            Status = RETURN_UNSUPPORTED;\r
+          }\r
           return Status;\r
         }\r
 \r
@@ -547,14 +617,18 @@ PeCoffLoaderGetImageInfo (
           // Read next debug directory entry\r
           //\r
           Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);\r
+          ReadSize = Size;\r
           Status = ImageContext->ImageRead (\r
                                    ImageContext->Handle,\r
                                    DebugDirectoryEntryFileOffset + Index,\r
                                    &Size,\r
                                    &DebugEntry\r
                                    );\r
-          if (RETURN_ERROR (Status)) {\r
+          if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
             ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+            if (Size != ReadSize) {\r
+              Status = RETURN_UNSUPPORTED;\r
+            }\r
             return Status;\r
           }\r
           if (DebugEntry.Type == EFI_IMAGE_DEBUG_TYPE_CODEVIEW) {\r
@@ -581,14 +655,18 @@ PeCoffLoaderGetImageInfo (
       // Read section header from file\r
       //\r
       Size   = sizeof (EFI_IMAGE_SECTION_HEADER);\r
+      ReadSize = Size;\r
       Status = ImageContext->ImageRead (\r
                                ImageContext->Handle,\r
                                SectionHeaderOffset,\r
                                &Size,\r
                                &SectionHeader\r
                                );\r
-      if (RETURN_ERROR (Status)) {\r
+      if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
         ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+        if (Size != ReadSize) {\r
+          Status = RETURN_UNSUPPORTED;\r
+        }\r
         return Status;\r
       }\r
 \r
@@ -634,14 +712,18 @@ PeCoffLoaderGetImageInfo (
         // Read next debug directory entry\r
         //\r
         Size = sizeof (EFI_IMAGE_DEBUG_DIRECTORY_ENTRY);\r
+        ReadSize = Size;\r
         Status = ImageContext->ImageRead (\r
                                  ImageContext->Handle,\r
                                  DebugDirectoryEntryFileOffset + Index,\r
                                  &Size,\r
                                  &DebugEntry\r
                                  );\r
-        if (RETURN_ERROR (Status)) {\r
+        if (RETURN_ERROR (Status) || (Size != ReadSize)) {\r
           ImageContext->ImageError = IMAGE_ERROR_IMAGE_READ;\r
+          if (Size != ReadSize) {\r
+            Status = RETURN_UNSUPPORTED;\r
+          }\r
           return Status;\r
         }\r
 \r
@@ -1403,6 +1485,12 @@ PeCoffLoaderLoadImage (
 \r
         for (Index = 0; Index < ResourceDirectory->NumberOfNamedEntries; Index++) {\r
           if (ResourceDirectoryEntry->u1.s.NameIsString) {\r
+            //\r
+            // Check the ResourceDirectoryEntry->u1.s.NameOffset before use it.\r
+            //\r
+            if (ResourceDirectoryEntry->u1.s.NameOffset >= DirectoryEntry->Size) {\r
+              continue;\r
+            }\r
             ResourceDirectoryString = (EFI_IMAGE_RESOURCE_DIRECTORY_STRING *) (Base + ResourceDirectoryEntry->u1.s.NameOffset);\r
             String = &ResourceDirectoryString->String[0];\r
 \r
@@ -1578,6 +1666,15 @@ PeCoffLoaderRelocateImageForRuntime (
   //\r
   FixupData = RelocationData;\r
   while (RelocBase < RelocBaseEnd) {\r
+    //\r
+    // Add check for RelocBase->SizeOfBlock field.\r
+    //\r
+    if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > RelocDir->Size)) {\r
+      //\r
+      // Data invalid, cannot continue to relocate the image, just return.\r
+      //\r
+      return;\r
+    }\r
 \r
     Reloc     = (UINT16 *) ((UINT8 *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));\r
     RelocEnd  = (UINT16 *) ((UINT8 *) RelocBase + RelocBase->SizeOfBlock);\r
@@ -1671,6 +1768,8 @@ PeCoffLoaderRelocateImageForRuntime (
   PE/COFF image starting at byte offset FileOffset into the buffer specified by Buffer.  \r
   The size of the buffer actually read is returned in ReadSize.\r
   \r
+  The caller must make sure the FileOffset and ReadSize within the file scope.\r
+\r
   If FileHandle is NULL, then ASSERT().\r
   If ReadSize is NULL, then ASSERT().\r
   If Buffer is NULL, then ASSERT().\r