Improve the extent tree node validation by validating the number of entries the node advertises against the theoretical max (derived from the size of on-disk structs and the block size (or i_data, if inline extents).
Previously, we did not validate the number of entries. This could be exploited for out-of-bounds reads and crashes. Cc: Marvin Häuser <mhaeu...@posteo.de> Fixes: d9ceedca6c8f ("Ext4Pkg: Add Ext4Dxe driver.") Reported-by: Savva Mitrofanov <savva...@gmail.com> Signed-off-by: Pedro Falcato <pedro.falc...@gmail.com> --- Features/Ext4Pkg/Ext4Dxe/Extents.c | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/Features/Ext4Pkg/Ext4Dxe/Extents.c b/Features/Ext4Pkg/Ext4Dxe/Extents.c index 9e4364e50d99..66d085938549 100644 --- a/Features/Ext4Pkg/Ext4Dxe/Extents.c +++ b/Features/Ext4Pkg/Ext4Dxe/Extents.c @@ -1,7 +1,7 @@ /** @file Extent related routines - Copyright (c) 2021 - 2022 Pedro Falcato All rights reserved. + Copyright (c) 2021 - 2023 Pedro Falcato All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -80,13 +80,15 @@ Ext4GetInoExtentHeader ( /** Checks if an extent header is valid. @param[in] Header Pointer to the EXT4_EXTENT_HEADER structure. + @param[in] MaxEntries Maximum number of entries possible for this tree node. @return TRUE if valid, FALSE if not. **/ STATIC BOOLEAN Ext4ExtentHeaderValid ( - IN CONST EXT4_EXTENT_HEADER *Header + IN CONST EXT4_EXTENT_HEADER *Header, + IN UINT16 MaxEntries ) { if (Header->eh_depth > EXT4_EXTENT_TREE_MAX_DEPTH) { @@ -99,6 +101,18 @@ Ext4ExtentHeaderValid ( return FALSE; } + if ((Header->eh_max > MaxEntries) || (Header->eh_entries > MaxEntries)) { + DEBUG (( + DEBUG_ERROR, + "[ext4] Invalid extent header entries (extent header: %u max," + " %u entries, theoretical max: %u) (larger than permitted)\n", + Header->eh_max, + Header->eh_entries, + MaxEntries + )); + return FALSE; + } + if (Header->eh_max < Header->eh_entries) { DEBUG (( DEBUG_ERROR, @@ -212,6 +226,9 @@ Ext4ExtentIdxLeafBlock ( return LShiftU64 (Index->ei_leaf_hi, 32) | Index->ei_leaf_lo; } +// Results of sizeof(i_data) / sizeof(extent) - 1 = 4 +#define EXT4_NR_INLINE_EXTENTS 4 + /** Retrieves an extent from an EXT4 inode. @param[in] Partition Pointer to the opened EXT4 partition. @@ -237,7 +254,7 @@ Ext4GetExtent ( EXT4_EXTENT_HEADER *ExtHeader; EXT4_EXTENT_INDEX *Index; EFI_STATUS Status; - EXT4_BLOCK_NR BlockNumber; + UINT32 MaxExtentsPerNode; Inode = File->Inode; Ext = NULL; @@ -275,12 +292,17 @@ Ext4GetExtent ( ExtHeader = Ext4GetInoExtentHeader (Inode); - if (!Ext4ExtentHeaderValid (ExtHeader)) { + if (!Ext4ExtentHeaderValid (ExtHeader, EXT4_NR_INLINE_EXTENTS)) { return EFI_VOLUME_CORRUPTED; } CurrentDepth = ExtHeader->eh_depth; + // A single node fits into a single block, so we can only have (BlockSize / sizeof(EXT4_EXTENT)) - 1 + // extents in a single node. Note the -1, because both leaf and internal node headers are 12 bytes, + // and so are individual entries. + MaxExtentsPerNode = (Partition->BlockSize / sizeof (EXT4_EXTENT)) - 1; + while (ExtHeader->eh_depth != 0) { CurrentDepth--; // While depth != 0, we're traversing the tree itself and not any leaves @@ -289,17 +311,7 @@ Ext4GetExtent ( // Therefore, we can use binary search, and it's actually the standard for doing so // (see FreeBSD). - Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock); - BlockNumber = Ext4ExtentIdxLeafBlock (Index); - - // Check that block isn't file hole - if (BlockNumber == EXT4_BLOCK_FILE_HOLE) { - if (Buffer != NULL) { - FreePool (Buffer); - } - - return EFI_VOLUME_CORRUPTED; - } + Index = Ext4BinsearchExtentIndex (ExtHeader, LogicalBlock); if (Buffer == NULL) { Buffer = AllocatePool (Partition->BlockSize); @@ -309,7 +321,8 @@ Ext4GetExtent ( } // Read the leaf block onto the previously-allocated buffer. - Status = Ext4ReadBlocks (Partition, Buffer, 1, BlockNumber); + + Status = Ext4ReadBlocks (Partition, Buffer, 1, Ext4ExtentIdxLeafBlock (Index)); if (EFI_ERROR (Status)) { FreePool (Buffer); return Status; @@ -317,7 +330,7 @@ Ext4GetExtent ( ExtHeader = Buffer; - if (!Ext4ExtentHeaderValid (ExtHeader)) { + if (!Ext4ExtentHeaderValid (ExtHeader, MaxExtentsPerNode)) { FreePool (Buffer); return EFI_VOLUME_CORRUPTED; } -- 2.40.1 -=-=-=-=-=-=-=-=-=-=-=- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#104306): https://edk2.groups.io/g/devel/message/104306 Mute This Topic: https://groups.io/mt/98774550/21656 Group Owner: devel+ow...@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [arch...@mail-archive.com] -=-=-=-=-=-=-=-=-=-=-=-