Commit message: MdeModulePkg/PartitionDxe: Add check for underlying device block size

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=828

Within FindAnchorVolumeDescriptorPointer():

Add a check for the underlying device block size to ensure it is greater
than the size of an Anchor Volume Descriptor Pointer.

Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Hao Wu <hao.a.wu@intel.com>
Reviewed-by: Paulo Alcantara <palcantara@suse.de>
Acked-by: Star Zeng <star.zeng@intel.com>
CVE: None
Vulnerability description: None

Project: edk2
File: Udf.c

FindAnchorVolumeDescriptorPointer (
  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,
  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,
  OUT  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint,
  OUT  EFI_LBA                               *LastRecordedBlock
  )
{
  EFI_STATUS                            Status;
  UINT32                                BlockSize;
  EFI_LBA                               EndLBA;
  UDF_DESCRIPTOR_TAG                    *DescriptorTag;
  UINTN                                 AvdpsCount;
  UINTN                                 Size;
  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoints;
  INTN                                  Index;
  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPointPtr;
  EFI_LBA                               LastAvdpBlockNum;

  //
  // UDF 2.60, 2.2.3 Anchor Volume Descriptor Pointer
  //
  // An Anchor Volume Descriptor Pointer structure shall be recorded in at
  // least 2 of the following 3 locations on the media: Logical Sector 256,
  // N - 256 or N, where N is the last *addressable* sector of a volume.
  //
  // To figure out what logical sector N is, the SCSI commands READ CAPACITY and
  // READ TRACK INFORMATION are used, however many drives or medias report their
  // "last recorded block" wrongly. Although, READ CAPACITY returns the last
  // readable data block but there might be unwritten blocks, which are located
  // outside any track and therefore AVDP will not be found at block N.
  //
  // That said, we define a magic number of 512 blocks to be used as correction
  // when attempting to find AVDP and define last block number.
  //
  BlockSize = BlockIo->Media->BlockSize;
  EndLBA = BlockIo->Media->LastBlock;
  *LastRecordedBlock = EndLBA;
  AvdpsCount = 0;

  //
  // Check if the block size of the underlying media can hold the data of an
  // Anchor Volume Descriptor Pointer
  //
  if (BlockSize < sizeof (UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER)) {
    DEBUG ((
      DEBUG_ERROR,
      "%a: Media block size 0x%x unable to hold an AVDP.\n",
      __FUNCTION__,
      BlockSize
      ));
    return EFI_UNSUPPORTED;
  }

  //
  // Find AVDP at block 256
  //
  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    MultU64x32 (256, BlockSize),
    sizeof (*AnchorPoint),
    AnchorPoint
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  DescriptorTag = &AnchorPoint->DescriptorTag;

  //
  // Check if read block is a valid AVDP descriptor
  //
  if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
    DEBUG ((DEBUG_INFO, "%a: found AVDP at block %d\n", __FUNCTION__, 256));
    AvdpsCount++;
  }

  //
  // Find AVDP at block N - 256
  //
  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    MultU64x32 ((UINT64)EndLBA - 256, BlockSize),
    sizeof (*AnchorPoint),
    AnchorPoint
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check if read block is a valid AVDP descriptor
  //
  if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer &&
      ++AvdpsCount == 2) {
    DEBUG ((DEBUG_INFO, "%a: found AVDP at block %Ld\n", __FUNCTION__,
            EndLBA - 256));
    return EFI_SUCCESS;
  }

  //
  // Check if at least one AVDP was found in previous locations
  //
  if (AvdpsCount == 0) {
    return EFI_VOLUME_CORRUPTED;
  }

  //
  // Find AVDP at block N
  //
  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    MultU64x32 ((UINT64)EndLBA, BlockSize),
    sizeof (*AnchorPoint),
    AnchorPoint
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check if read block is a valid AVDP descriptor
  //
  if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
    return EFI_SUCCESS;
  }

  //
  // No AVDP found at block N. Possibly drive/media returned bad last recorded
  // block, or it is part of unwritten data blocks and outside any track.
  //
  // Search backwards for an AVDP from block N-1 through
  // N-MAX_CORRECTION_BLOCKS_NUM. If any AVDP is found, then correct last block
  // number for the new UDF partition child handle.
  //
  Size = MAX_CORRECTION_BLOCKS_NUM * BlockSize;

  AnchorPoints = AllocateZeroPool (Size);
  if (AnchorPoints == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Read consecutive MAX_CORRECTION_BLOCKS_NUM disk blocks
  //
  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    MultU64x32 ((UINT64)EndLBA - MAX_CORRECTION_BLOCKS_NUM, BlockSize),
    Size,
    AnchorPoints
    );
  if (EFI_ERROR (Status)) {
    goto Out_Free;
  }

  Status = EFI_VOLUME_CORRUPTED;

  //
  // Search for AVDP from blocks N-1 through N-MAX_CORRECTION_BLOCKS_NUM
  //
  for (Index = MAX_CORRECTION_BLOCKS_NUM - 2; Index >= 0; Index--) {
    AnchorPointPtr = (VOID *)((UINTN)AnchorPoints + Index * BlockSize);

    DescriptorTag = &AnchorPointPtr->DescriptorTag;

    //
    // Check if read block is a valid AVDP descriptor
    //
    if (DescriptorTag->TagIdentifier == UdfAnchorVolumeDescriptorPointer) {
      //
      // Calculate last recorded block number
      //
      LastAvdpBlockNum = EndLBA - (MAX_CORRECTION_BLOCKS_NUM - Index);
      DEBUG ((DEBUG_WARN, "%a: found AVDP at block %Ld\n", __FUNCTION__,
              LastAvdpBlockNum));
      DEBUG ((DEBUG_WARN, "%a: correcting last block from %Ld to %Ld\n",
              __FUNCTION__, EndLBA, LastAvdpBlockNum));
      //
      // Save read AVDP from last block
      //
      CopyMem (AnchorPoint, AnchorPointPtr, sizeof (*AnchorPointPtr));
      //
      // Set last recorded block number
      //
      *LastRecordedBlock = LastAvdpBlockNum;
      Status = EFI_SUCCESS;
      break;
    }
  }

Out_Free:
  FreePool (AnchorPoints);
  return Status;
}

Project: edk2
File: Udf.c

FindLogicalVolumeLocation (
  IN   EFI_BLOCK_IO_PROTOCOL                 *BlockIo,
  IN   EFI_DISK_IO_PROTOCOL                  *DiskIo,
  IN   UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  *AnchorPoint,
  IN   EFI_LBA                               LastRecordedBlock,
  OUT  UINT64                                *MainVdsStartBlock,
  OUT  UINT64                                *MainVdsEndBlock
  )
{
  EFI_STATUS                     Status;
  UINT32                         BlockSize;
  UDF_EXTENT_AD                  *ExtentAd;
  UINT64                         SeqBlocksNum;
  UINT64                         SeqStartBlock;
  UINT64                         GuardMainVdsStartBlock;
  VOID                           *Buffer;
  UINT64                         SeqEndBlock;
  BOOLEAN                        StopSequence;
  UINTN                          LvdsCount;
  UDF_LOGICAL_VOLUME_DESCRIPTOR  *LogicalVolDesc;
  UDF_DESCRIPTOR_TAG             *DescriptorTag;

  BlockSize = BlockIo->Media->BlockSize;
  ExtentAd = &AnchorPoint->MainVolumeDescriptorSequenceExtent;

  //
  // UDF 2.60, 2.2.3.1 struct MainVolumeDescriptorSequenceExtent
  //
  // The Main Volume Descriptor Sequence Extent shall have a minimum length of
  // 16 logical sectors.
  //
  // Also make sure it does not exceed maximum number of blocks in the disk.
  //
  SeqBlocksNum = DivU64x32 ((UINT64)ExtentAd->ExtentLength, BlockSize);
  if (SeqBlocksNum < 16 || (EFI_LBA)SeqBlocksNum > LastRecordedBlock + 1) {
    return EFI_VOLUME_CORRUPTED;
  }

  //
  // Check for valid Volume Descriptor Sequence starting block number
  //
  SeqStartBlock = (UINT64)ExtentAd->ExtentLocation;
  if (SeqStartBlock > LastRecordedBlock ||
      SeqStartBlock + SeqBlocksNum - 1 > LastRecordedBlock) {
    return EFI_VOLUME_CORRUPTED;
  }

  GuardMainVdsStartBlock = SeqStartBlock;

  //
  // Allocate buffer for reading disk blocks
  //
  Buffer = AllocateZeroPool ((UINTN)BlockSize);
  if (Buffer == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  SeqEndBlock = SeqStartBlock + SeqBlocksNum;
  StopSequence = FALSE;
  LvdsCount = 0;
  Status = EFI_VOLUME_CORRUPTED;
  //
  // Start Main Volume Descriptor Sequence
  //
  for (; SeqStartBlock < SeqEndBlock && !StopSequence; SeqStartBlock++) {
    //
    // Read disk block
    //
    Status = BlockIo->ReadBlocks (
      BlockIo,
      BlockIo->Media->MediaId,
      SeqStartBlock,
      BlockSize,
      Buffer
      );
    if (EFI_ERROR (Status)) {
      goto Out_Free;
    }

    DescriptorTag = Buffer;

    //
    // ECMA 167, 8.4.1 Contents of a Volume Descriptor Sequence
    //
    // - A Volume Descriptor Sequence shall contain one or more Primary Volume
    //   Descriptors.
    // - A Volume Descriptor Sequence shall contain zero or more Implementation
    //   Use Volume Descriptors.
    // - A Volume Descriptor Sequence shall contain zero or more Partition
    //   Descriptors.
    // - A Volume Descriptor Sequence shall contain zero or more Logical Volume
    //   Descriptors.
    // - A Volume Descriptor Sequence shall contain zero or more Unallocated
    //   Space Descriptors.
    //
    switch (DescriptorTag->TagIdentifier) {
    case UdfPrimaryVolumeDescriptor:
    case UdfImplemenationUseVolumeDescriptor:
    case UdfPartitionDescriptor:
    case UdfUnallocatedSpaceDescriptor:
      break;

    case UdfLogicalVolumeDescriptor:
      LogicalVolDesc = Buffer;

      //
      // Check for existence of a single LVD and whether it is supported by
      // current EDK2 UDF file system implementation.
      //
      if (++LvdsCount > 1 ||
          !IsLogicalVolumeDescriptorSupported (LogicalVolDesc)) {
        Status = EFI_UNSUPPORTED;
        StopSequence = TRUE;
      }

      break;

    case UdfTerminatingDescriptor:
      //
      // Stop the sequence when we find a Terminating Descriptor
      // (aka Unallocated Sector), se we don't have to walk all the unallocated
      // area unnecessarily.
      //
      StopSequence = TRUE;
      break;

    default:
      //
      // An invalid Volume Descriptor has been found in the sequece. Volume is
      // corrupted.
      //
      Status = EFI_VOLUME_CORRUPTED;
      goto Out_Free;
    }
  }

  //
  // Check if LVD was found
  //
  if (!EFI_ERROR (Status) && LvdsCount == 1) {
    *MainVdsStartBlock = GuardMainVdsStartBlock;
    //
    // We do not need to read either LVD or PD descriptors to know the last
    // valid block in the found UDF file system. It's already
    // LastRecordedBlock.
    //
    *MainVdsEndBlock = LastRecordedBlock;

    Status = EFI_SUCCESS;
  }

Out_Free:
  //
  // Free block read buffer
  //
  FreePool (Buffer);

  return Status;
}

Project: edk2
File: Udf.c

IsLogicalVolumeDescriptorSupported (
  UDF_LOGICAL_VOLUME_DESCRIPTOR *LogicalVolDesc
  )
{
  //
  // Check for a valid UDF revision range
  //
  switch (LogicalVolDesc->DomainIdentifier.Suffix.Domain.UdfRevision) {
  case 0x0102:
  case 0x0150:
  case 0x0200:
  case 0x0201:
  case 0x0250:
  case 0x0260:
    break;
  default:
    return FALSE;
  }

  //
  // Check for a single Partition Map
  //
  if (LogicalVolDesc->NumberOfPartitionMaps > 1) {
    return FALSE;
  }
  //
  // UDF 1.02 revision supports only Type 1 (Physical) partitions, but
  // let's check it any way.
  //
  // PartitionMap[0] -> type
  // PartitionMap[1] -> length (in bytes)
  //
  if (LogicalVolDesc->PartitionMaps[0] != 1 ||
      LogicalVolDesc->PartitionMaps[1] != 6) {
    return FALSE;
  }

  return TRUE;
}

Project: edk2
File: Udf.c

PartitionInstallUdfChildHandles (
  IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN  EFI_HANDLE                   Handle,
  IN  EFI_DISK_IO_PROTOCOL         *DiskIo,
  IN  EFI_DISK_IO2_PROTOCOL        *DiskIo2,
  IN  EFI_BLOCK_IO_PROTOCOL        *BlockIo,
  IN  EFI_BLOCK_IO2_PROTOCOL       *BlockIo2,
  IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
  )
{
  UINT32                       RemainderByMediaBlockSize;
  EFI_STATUS                   Status;
  EFI_BLOCK_IO_MEDIA           *Media;
  EFI_PARTITION_INFO_PROTOCOL  PartitionInfo;
  EFI_LBA                      StartingLBA;
  EFI_LBA                      EndingLBA;
  BOOLEAN                      ChildCreated;

  Media = BlockIo->Media;
  ChildCreated = FALSE;

  //
  // Check if UDF logical block size is multiple of underlying device block size
  //
  DivU64x32Remainder (
    UDF_LOGICAL_SECTOR_SIZE,   // Dividend
    Media->BlockSize,          // Divisor
    &RemainderByMediaBlockSize // Remainder
    );
  if (RemainderByMediaBlockSize != 0) {
    return EFI_NOT_FOUND;
  }

  //
  // Detect El Torito feature first.
  // And always continue to search for UDF.
  //
  Status = PartitionInstallElToritoChildHandles (
             This,
             Handle,
             DiskIo,
             DiskIo2,
             BlockIo,
             BlockIo2,
             DevicePath
             );
  if (!EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "PartitionDxe: El Torito standard found on handle 0x%p.\n", Handle));
    ChildCreated = TRUE;
  }

  //
  // Search for an UDF file system on block device
  //
  Status = FindUdfFileSystem (BlockIo, DiskIo, &StartingLBA, &EndingLBA);
  if (EFI_ERROR (Status)) {
    return (ChildCreated ? EFI_SUCCESS : EFI_NOT_FOUND);
  }

  //
  // Create Partition Info protocol for UDF file system
  //
  ZeroMem (&PartitionInfo, sizeof (EFI_PARTITION_INFO_PROTOCOL));
  PartitionInfo.Revision = EFI_PARTITION_INFO_PROTOCOL_REVISION;
  PartitionInfo.Type = PARTITION_TYPE_OTHER;

  //
  // Install partition child handle for UDF file system
  //
  Status = PartitionInstallChildHandle (
    This,
    Handle,
    DiskIo,
    DiskIo2,
    BlockIo,
    BlockIo2,
    DevicePath,
    (EFI_DEVICE_PATH_PROTOCOL *)&gUdfDevicePath,
    &PartitionInfo,
    StartingLBA,
    EndingLBA,
    Media->BlockSize,
    NULL
    );
  if (EFI_ERROR (Status)) {
    return (ChildCreated ? EFI_SUCCESS : Status);
  }

  return EFI_SUCCESS;
}

Project: edk2
File: Udf.c

FindUdfFileSystem (
  IN EFI_BLOCK_IO_PROTOCOL  *BlockIo,
  IN EFI_DISK_IO_PROTOCOL   *DiskIo,
  OUT EFI_LBA               *StartingLBA,
  OUT EFI_LBA               *EndingLBA
  )
{
  EFI_STATUS                            Status;
  UDF_ANCHOR_VOLUME_DESCRIPTOR_POINTER  AnchorPoint;
  EFI_LBA                               LastRecordedBlock;

  //
  // Find UDF volume identifiers
  //
  Status = FindUdfVolumeIdentifiers (BlockIo, DiskIo);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Find Anchor Volume Descriptor Pointer
  //
  Status = FindAnchorVolumeDescriptorPointer (
    BlockIo,
    DiskIo,
    &AnchorPoint,
    &LastRecordedBlock
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Find Logical Volume location
  //
  Status = FindLogicalVolumeLocation (
    BlockIo,
    DiskIo,
    &AnchorPoint,
    LastRecordedBlock,
    (UINT64 *)StartingLBA,
    (UINT64 *)EndingLBA
    );

  return Status;
}

Project: edk2
File: Udf.c

FindUdfVolumeIdentifiers (
  IN EFI_BLOCK_IO_PROTOCOL  *BlockIo,
  IN EFI_DISK_IO_PROTOCOL   *DiskIo
  )
{
  EFI_STATUS                            Status;
  UINT64                                Offset;
  UINT64                                EndDiskOffset;
  CDROM_VOLUME_DESCRIPTOR               VolDescriptor;
  CDROM_VOLUME_DESCRIPTOR               TerminatingVolDescriptor;

  ZeroMem ((VOID *)&TerminatingVolDescriptor, sizeof (CDROM_VOLUME_DESCRIPTOR));

  //
  // Start Volume Recognition Sequence
  //
  EndDiskOffset = MultU64x32 (BlockIo->Media->LastBlock,
                              BlockIo->Media->BlockSize);

  for (Offset = UDF_VRS_START_OFFSET; Offset < EndDiskOffset;
       Offset += UDF_LOGICAL_SECTOR_SIZE) {
    //
    // Check if block device has a Volume Structure Descriptor and an Extended
    // Area.
    //
    Status = DiskIo->ReadDisk (
      DiskIo,
      BlockIo->Media->MediaId,
      Offset,
      sizeof (CDROM_VOLUME_DESCRIPTOR),
      (VOID *)&VolDescriptor
      );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
                    (VOID *)UDF_BEA_IDENTIFIER,
                    sizeof (VolDescriptor.Unknown.Id)) == 0) {
      break;
    }

    if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
                     (VOID *)CDVOL_ID,
                     sizeof (VolDescriptor.Unknown.Id)) != 0) ||
        (CompareMem ((VOID *)&VolDescriptor,
                     (VOID *)&TerminatingVolDescriptor,
                     sizeof (CDROM_VOLUME_DESCRIPTOR)) == 0)) {
      return EFI_NOT_FOUND;
    }
  }

  //
  // Look for "NSR0{2,3}" identifiers in the Extended Area.
  //
  Offset += UDF_LOGICAL_SECTOR_SIZE;
  if (Offset >= EndDiskOffset) {
    return EFI_NOT_FOUND;
  }

  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    Offset,
    sizeof (CDROM_VOLUME_DESCRIPTOR),
    (VOID *)&VolDescriptor
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if ((CompareMem ((VOID *)VolDescriptor.Unknown.Id,
                   (VOID *)UDF_NSR2_IDENTIFIER,
                   sizeof (VolDescriptor.Unknown.Id)) != 0) &&
      (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
                   (VOID *)UDF_NSR3_IDENTIFIER,
                   sizeof (VolDescriptor.Unknown.Id)) != 0)) {
    return EFI_NOT_FOUND;
  }

  //
  // Look for "TEA01" identifier in the Extended Area
  //
  Offset += UDF_LOGICAL_SECTOR_SIZE;
  if (Offset >= EndDiskOffset) {
    return EFI_NOT_FOUND;
  }

  Status = DiskIo->ReadDisk (
    DiskIo,
    BlockIo->Media->MediaId,
    Offset,
    sizeof (CDROM_VOLUME_DESCRIPTOR),
    (VOID *)&VolDescriptor
    );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (CompareMem ((VOID *)VolDescriptor.Unknown.Id,
                  (VOID *)UDF_TEA_IDENTIFIER,
                  sizeof (VolDescriptor.Unknown.Id)) != 0) {
    return EFI_NOT_FOUND;
  }

  return EFI_SUCCESS;
}