]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Universal/Disk/UdfDxe/FileSystemOperations.c
MdeModulePkg/UdfDxe: Refine boundary checks for file/path name string
[mirror_edk2.git] / MdeModulePkg / Universal / Disk / UdfDxe / FileSystemOperations.c
index e048d95d3120db85d27ef8e3e1e4117bccdffb83..b3ac673cac4952518d65cbe6635566abc2a605f4 100644 (file)
@@ -2,6 +2,7 @@
   Handle on-disk format and volume structures in UDF/ECMA-167 file systems.\r
 \r
   Copyright (C) 2014-2017 Paulo Alcantara <pcacjr@zytor.com>\r
+  Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>\r
 \r
   This program and the accompanying materials are licensed and made available\r
   under the terms and conditions of the BSD License which accompanies this\r
@@ -271,26 +272,39 @@ GetPdFromLongAd (
 /**\r
   Return logical sector number of a given Long Allocation Descriptor.\r
 \r
-  @param[in]  Volume              Volume information pointer.\r
-  @param[in]  LongAd              Long Allocation Descriptor pointer.\r
+  @param[in]   Volume             Volume information pointer.\r
+  @param[in]   LongAd             Long Allocation Descriptor pointer.\r
+  @param[out]  Lsn                Logical sector number pointer.\r
 \r
-  @return The logical sector number of a given Long Allocation Descriptor.\r
+  @retval EFI_SUCCESS             Logical sector number successfully returned.\r
+  @retval EFI_UNSUPPORTED         Logical sector number is not returned due to\r
+                                  unrecognized format.\r
 \r
 **/\r
-UINT64\r
+EFI_STATUS\r
 GetLongAdLsn (\r
-  IN UDF_VOLUME_INFO                 *Volume,\r
-  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd\r
+  IN  UDF_VOLUME_INFO                 *Volume,\r
+  IN  UDF_LONG_ALLOCATION_DESCRIPTOR  *LongAd,\r
+  OUT UINT64                          *Lsn\r
   )\r
 {\r
   UDF_PARTITION_DESCRIPTOR *PartitionDesc;\r
 \r
   PartitionDesc = GetPdFromLongAd (Volume, LongAd);\r
-  ASSERT (PartitionDesc != NULL);\r
+  if (PartitionDesc == NULL) {\r
+    DEBUG ((\r
+      DEBUG_ERROR,\r
+      "%a: Fail to get the Partition Descriptor from the given Long Allocation Descriptor.\n",\r
+      __FUNCTION__\r
+      ));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
 \r
-  return (UINT64)PartitionDesc->PartitionStartingLocation -\r
-    Volume->MainVdsStartLocation +\r
-    LongAd->ExtentLocation.LogicalBlockNumber;\r
+  *Lsn = (UINT64)PartitionDesc->PartitionStartingLocation -\r
+         Volume->MainVdsStartLocation +\r
+         LongAd->ExtentLocation.LogicalBlockNumber;\r
+\r
+  return EFI_SUCCESS;\r
 }\r
 \r
 /**\r
@@ -342,7 +356,10 @@ FindFileSetDescriptor (
   UDF_DESCRIPTOR_TAG             *DescriptorTag;\r
 \r
   LogicalVolDesc = &Volume->LogicalVolDesc;\r
-  Lsn = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse);\r
+  Status = GetLongAdLsn (Volume, &LogicalVolDesc->LogicalVolumeContentsUse, &Lsn);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
   //\r
   // As per UDF 2.60 specification:\r
@@ -468,8 +485,6 @@ DuplicateFid (
   *NewFileIdentifierDesc =\r
     (UDF_FILE_IDENTIFIER_DESCRIPTOR *)AllocateCopyPool (\r
       (UINTN) GetFidDescriptorLength (FileIdentifierDesc), FileIdentifierDesc);\r
-\r
-  ASSERT (*NewFileIdentifierDesc != NULL);\r
 }\r
 \r
 /**\r
@@ -490,8 +505,6 @@ DuplicateFe (
   )\r
 {\r
   *NewFileEntry = AllocateCopyPool (Volume->FileEntrySize, FileEntry);\r
-\r
-  ASSERT (*NewFileEntry != NULL);\r
 }\r
 \r
 /**\r
@@ -726,6 +739,10 @@ GetAllocationDescriptor (
       );\r
   }\r
 \r
+  //\r
+  // Code should never reach here.\r
+  //\r
+  ASSERT (FALSE);\r
   return EFI_DEVICE_ERROR;\r
 }\r
 \r
@@ -736,34 +753,47 @@ GetAllocationDescriptor (
   @param[in]  Volume              Volume information pointer.\r
   @param[in]  ParentIcb           Long Allocation Descriptor pointer.\r
   @param[in]  Ad                  Allocation Descriptor pointer.\r
+  @param[out] Lsn                 Logical sector number pointer.\r
 \r
-  @return The logical sector number of the given Allocation Descriptor.\r
+  @retval EFI_SUCCESS             Logical sector number of the given Allocation\r
+                                  Descriptor successfully returned.\r
+  @retval EFI_UNSUPPORTED         Logical sector number of the given Allocation\r
+                                  Descriptor is not returned due to unrecognized\r
+                                  format.\r
 \r
 **/\r
-UINT64\r
+EFI_STATUS\r
 GetAllocationDescriptorLsn (\r
-  IN UDF_FE_RECORDING_FLAGS          RecordingFlags,\r
-  IN UDF_VOLUME_INFO                 *Volume,\r
-  IN UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,\r
-  IN VOID                            *Ad\r
+  IN  UDF_FE_RECORDING_FLAGS          RecordingFlags,\r
+  IN  UDF_VOLUME_INFO                 *Volume,\r
+  IN  UDF_LONG_ALLOCATION_DESCRIPTOR  *ParentIcb,\r
+  IN  VOID                            *Ad,\r
+  OUT UINT64                          *Lsn\r
   )\r
 {\r
   UDF_PARTITION_DESCRIPTOR *PartitionDesc;\r
 \r
   if (RecordingFlags == LongAdsSequence) {\r
-    return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad);\r
+    return GetLongAdLsn (Volume, (UDF_LONG_ALLOCATION_DESCRIPTOR *)Ad, Lsn);\r
   } else if (RecordingFlags == ShortAdsSequence) {\r
     PartitionDesc = GetPdFromLongAd (Volume, ParentIcb);\r
-    ASSERT (PartitionDesc != NULL);\r
+    if (PartitionDesc == NULL) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
 \r
-    return GetShortAdLsn (\r
-      Volume,\r
-      PartitionDesc,\r
-      (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad\r
-      );\r
+    *Lsn = GetShortAdLsn (\r
+             Volume,\r
+             PartitionDesc,\r
+             (UDF_SHORT_ALLOCATION_DESCRIPTOR *)Ad\r
+             );\r
+    return EFI_SUCCESS;\r
   }\r
 \r
-  return 0;\r
+  //\r
+  // Code should never reach here.\r
+  //\r
+  ASSERT (FALSE);\r
+  return EFI_UNSUPPORTED;\r
 }\r
 \r
 /**\r
@@ -808,10 +838,14 @@ GetAedAdsOffset (
   UDF_DESCRIPTOR_TAG                *DescriptorTag;\r
 \r
   ExtentLength  = GET_EXTENT_LENGTH (RecordingFlags, Ad);\r
-  Lsn           = GetAllocationDescriptorLsn (RecordingFlags,\r
+  Status        = GetAllocationDescriptorLsn (RecordingFlags,\r
                                               Volume,\r
                                               ParentIcb,\r
-                                              Ad);\r
+                                              Ad,\r
+                                              &Lsn);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
   Data = AllocatePool (ExtentLength);\r
   if (Data == NULL) {\r
@@ -1160,10 +1194,14 @@ ReadFile (
 \r
       ExtentLength = GET_EXTENT_LENGTH (RecordingFlags, Ad);\r
 \r
-      Lsn = GetAllocationDescriptorLsn (RecordingFlags,\r
-                                        Volume,\r
-                                        ParentIcb,\r
-                                        Ad);\r
+      Status = GetAllocationDescriptorLsn (RecordingFlags,\r
+                                           Volume,\r
+                                           ParentIcb,\r
+                                           Ad,\r
+                                           &Lsn);\r
+      if (EFI_ERROR (Status)) {\r
+        goto Done;\r
+      }\r
 \r
       switch (ReadFileInfo->Flags) {\r
       case ReadFileGetFileSize:\r
@@ -1370,7 +1408,15 @@ InternalFindFile (
     }\r
 \r
     DuplicateFe (BlockIo, Volume, Parent->FileEntry, &File->FileEntry);\r
+    if (File->FileEntry == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
     DuplicateFid (Parent->FileIdentifierDesc, &File->FileIdentifierDesc);\r
+    if (File->FileIdentifierDesc == NULL) {\r
+      FreePool (File->FileEntry);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
 \r
     return EFI_SUCCESS;\r
   }\r
@@ -1400,6 +1446,15 @@ InternalFindFile (
 \r
       break;\r
     }\r
+    //\r
+    // After calling function ReadDirectoryEntry(), if 'FileIdentifierDesc' is\r
+    // NULL, then the 'Status' must be EFI_OUT_OF_RESOURCES. Hence, if the code\r
+    // reaches here, 'FileIdentifierDesc' must be not NULL.\r
+    //\r
+    // The ASSERT here is for addressing a false positive NULL pointer\r
+    // dereference issue raised from static analysis.\r
+    //\r
+    ASSERT (FileIdentifierDesc != NULL);\r
 \r
     if (FileIdentifierDesc->FileCharacteristics & PARENT_FILE) {\r
       //\r
@@ -1412,7 +1467,7 @@ InternalFindFile (
         break;\r
       }\r
     } else {\r
-      Status = GetFileNameFromFid (FileIdentifierDesc, FoundFileName);\r
+      Status = GetFileNameFromFid (FileIdentifierDesc, ARRAY_SIZE (FoundFileName), FoundFileName);\r
       if (EFI_ERROR (Status)) {\r
         break;\r
       }\r
@@ -1615,12 +1670,17 @@ FindFileEntry (
   UINT64              Lsn;\r
   UINT32              LogicalBlockSize;\r
   UDF_DESCRIPTOR_TAG  *DescriptorTag;\r
+  VOID                *ReadBuffer;\r
+\r
+  Status = GetLongAdLsn (Volume, Icb, &Lsn);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
 \r
-  Lsn               = GetLongAdLsn (Volume, Icb);\r
   LogicalBlockSize  = Volume->LogicalVolDesc.LogicalBlockSize;\r
 \r
-  *FileEntry = AllocateZeroPool (Volume->FileEntrySize);\r
-  if (*FileEntry == NULL) {\r
+  ReadBuffer = AllocateZeroPool (Volume->FileEntrySize);\r
+  if (ReadBuffer == NULL) {\r
     return EFI_OUT_OF_RESOURCES;\r
   }\r
 \r
@@ -1632,13 +1692,13 @@ FindFileEntry (
     BlockIo->Media->MediaId,\r
     MultU64x32 (Lsn, LogicalBlockSize),\r
     Volume->FileEntrySize,\r
-    *FileEntry\r
+    ReadBuffer\r
     );\r
   if (EFI_ERROR (Status)) {\r
     goto Error_Read_Disk_Blk;\r
   }\r
 \r
-  DescriptorTag = *FileEntry;\r
+  DescriptorTag = ReadBuffer;\r
 \r
   //\r
   // Check if the read extent contains a valid Tag Identifier for the expected\r
@@ -1650,11 +1710,12 @@ FindFileEntry (
     goto Error_Invalid_Fe;\r
   }\r
 \r
+  *FileEntry = ReadBuffer;\r
   return EFI_SUCCESS;\r
 \r
 Error_Invalid_Fe:\r
 Error_Read_Disk_Blk:\r
-  FreePool (*FileEntry);\r
+  FreePool (ReadBuffer);\r
 \r
   return Status;\r
 }\r
@@ -1703,6 +1764,11 @@ FindFile (
   while (*FilePath != L'\0') {\r
     FileNamePointer = FileName;\r
     while (*FilePath != L'\0' && *FilePath != L'\\') {\r
+      if ((((UINTN)FileNamePointer - (UINTN)FileName) / sizeof (CHAR16)) >=\r
+          (ARRAY_SIZE (FileName) - 1)) {\r
+        return EFI_NOT_FOUND;\r
+      }\r
+\r
       *FileNamePointer++ = *FilePath++;\r
     }\r
 \r
@@ -1730,9 +1796,20 @@ FindFile (
         // We've already a file pointer (Root) for the root directory. Duplicate\r
         // its FE/EFE and FID descriptors.\r
         //\r
-        DuplicateFe (BlockIo, Volume, Root->FileEntry, &File->FileEntry);\r
-        DuplicateFid (Root->FileIdentifierDesc, &File->FileIdentifierDesc);\r
         Status = EFI_SUCCESS;\r
+        DuplicateFe (BlockIo, Volume, Root->FileEntry, &File->FileEntry);\r
+        if (File->FileEntry == NULL) {\r
+          Status = EFI_OUT_OF_RESOURCES;\r
+        } else {\r
+          //\r
+          // File->FileEntry is not NULL.\r
+          //\r
+          DuplicateFid (Root->FileIdentifierDesc, &File->FileIdentifierDesc);\r
+          if (File->FileIdentifierDesc == NULL) {\r
+            FreePool (File->FileEntry);\r
+            Status = EFI_OUT_OF_RESOURCES;\r
+          }\r
+        }\r
       }\r
     } else {\r
       //\r
@@ -1872,6 +1949,9 @@ ReadDirectoryEntry (
   } while (FileIdentifierDesc->FileCharacteristics & DELETED_FILE);\r
 \r
   DuplicateFid (FileIdentifierDesc, FoundFid);\r
+  if (*FoundFid == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -1880,22 +1960,38 @@ ReadDirectoryEntry (
   Get a filename (encoded in OSTA-compressed format) from a File Identifier\r
   Descriptor on an UDF volume.\r
 \r
+  @attention This is boundary function that may receive untrusted input.\r
+  @attention The input is from FileSystem.\r
+\r
+  The File Identifier Descriptor is external input, so this routine will do\r
+  basic validation for File Identifier Descriptor and report status.\r
+\r
   @param[in]   FileIdentifierDesc  File Identifier Descriptor pointer.\r
+  @param[in]   CharMax             The maximum number of FileName Unicode char,\r
+                                   including terminating null char.\r
   @param[out]  FileName            Decoded filename.\r
 \r
   @retval EFI_SUCCESS           Filename decoded and read.\r
   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.\r
+  @retval EFI_BUFFER_TOO_SMALL  The string buffer FileName cannot hold the\r
+                                decoded filename.\r
 **/\r
 EFI_STATUS\r
 GetFileNameFromFid (\r
   IN   UDF_FILE_IDENTIFIER_DESCRIPTOR  *FileIdentifierDesc,\r
+  IN   UINTN                           CharMax,\r
   OUT  CHAR16                          *FileName\r
   )\r
 {\r
-  UINT8 *OstaCompressed;\r
-  UINT8 CompressionId;\r
-  UINT8 Length;\r
-  UINTN Index;\r
+  UINT8  *OstaCompressed;\r
+  UINT8  CompressionId;\r
+  UINT8  Length;\r
+  UINTN  Index;\r
+  CHAR16 *FileNameBak;\r
+\r
+  if (CharMax == 0) {\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
 \r
   OstaCompressed =\r
     (UINT8 *)(\r
@@ -1908,10 +2004,22 @@ GetFileNameFromFid (
     return EFI_VOLUME_CORRUPTED;\r
   }\r
 \r
+  FileNameBak = FileName;\r
+\r
   //\r
   // Decode filename.\r
   //\r
   Length = FileIdentifierDesc->LengthOfFileIdentifier;\r
+  if (CompressionId == 16) {\r
+    if (((UINTN)Length >> 1) > CharMax) {\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+  } else {\r
+    if ((Length != 0) && ((UINTN)Length - 1 > CharMax)) {\r
+      return EFI_BUFFER_TOO_SMALL;\r
+    }\r
+  }\r
+\r
   for (Index = 1; Index < Length; Index++) {\r
     if (CompressionId == 16) {\r
       *FileName = OstaCompressed[Index++] << 8;\r
@@ -1926,7 +2034,11 @@ GetFileNameFromFid (
     FileName++;\r
   }\r
 \r
-  *FileName = L'\0';\r
+  Index = ((UINTN)FileName - (UINTN)FileNameBak) / sizeof (CHAR16);\r
+  if (Index > CharMax - 1) {\r
+    Index = CharMax - 1;\r
+  }\r
+  FileNameBak[Index] = L'\0';\r
 \r
   return EFI_SUCCESS;\r
 }\r
@@ -1934,6 +2046,12 @@ GetFileNameFromFid (
 /**\r
   Resolve a symlink file on an UDF volume.\r
 \r
+  @attention This is boundary function that may receive untrusted input.\r
+  @attention The input is from FileSystem.\r
+\r
+  The Path Component is external input, so this routine will do basic\r
+  validation for Path Component and report status.\r
+\r
   @param[in]   BlockIo        BlockIo interface.\r
   @param[in]   DiskIo         DiskIo interface.\r
   @param[in]   Volume         UDF volume information structure.\r
@@ -2029,8 +2147,18 @@ ResolveSymlink (
       // "." (current file). Duplicate both FE/EFE and FID of this file.\r
       //\r
       DuplicateFe (BlockIo, Volume, PreviousFile.FileEntry, &File->FileEntry);\r
+      if (File->FileEntry == NULL) {\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Error_Find_File;\r
+      }\r
+\r
       DuplicateFid (PreviousFile.FileIdentifierDesc,\r
                     &File->FileIdentifierDesc);\r
+      if (File->FileIdentifierDesc == NULL) {\r
+        FreePool (File->FileEntry);\r
+        Status = EFI_OUT_OF_RESOURCES;\r
+        goto Error_Find_File;\r
+      }\r
       goto Next_Path_Component;\r
     case 5:\r
       //\r
@@ -2052,6 +2180,9 @@ ResolveSymlink (
                           Index) << 8;\r
           Index++;\r
         } else {\r
+          if (Index > ARRAY_SIZE (FileName)) {\r
+            return EFI_UNSUPPORTED;\r
+          }\r
           *Char = 0;\r
         }\r
 \r
@@ -2062,7 +2193,11 @@ ResolveSymlink (
         Char++;\r
       }\r
 \r
-      *Char = L'\0';\r
+      Index = ((UINTN)Char - (UINTN)FileName) / sizeof (CHAR16);\r
+      if (Index > ARRAY_SIZE (FileName) - 1) {\r
+        Index = ARRAY_SIZE (FileName) - 1;\r
+      }\r
+      FileName[Index] = L'\0';\r
       break;\r
     }\r
 \r