The LzmaUefiDecompressGetInfo() function [MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaDecompress.c] currently silently truncates the UINT64 "DecodedSize" property of the compressed blob to the UINT32 "DestinationSize" output parameter. If "DecodedSize" is 0x1_0000_0100, for example, then the subsequent memory allocation (for decompression) will likely succeed (allocating 0x100 bytes only), but then the LzmaUefiDecompress() function (which re-fetches the uncompressed buffer size from the same LZMA header into a "SizeT" variable) will overwrite the buffer. Catch (DecodedSize > MAX_UINT32) in LzmaUefiDecompressGetInfo() at once. This should not be a practical limitation. (The issue cannot be fixed for 32-bit systems without spec modifications anyway, given that the "OutputSize" output parameter of EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL.ExtractSection() has type UINTN, not UINT64.) Cc: Dandan Bi <dandan.bi@intel.com> Cc: Hao A Wu <hao.a.wu@intel.com> Cc: Jian J Wang <jian.j.wang@intel.com> Cc: Liming Gao <gaoliming@byosoft.com.cn> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1816 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Liming Gao <gaoliming@byosoft.com.cn> Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com> Message-Id: <20201119115034.12897-2-lersek@redhat.com>
		
			
				
	
	
		
			222 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  LZMA Decompress interfaces
 | 
						|
 | 
						|
  Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
 | 
						|
#include "LzmaDecompressLibInternal.h"
 | 
						|
#include "Sdk/C/7zTypes.h"
 | 
						|
#include "Sdk/C/7zVersion.h"
 | 
						|
#include "Sdk/C/LzmaDec.h"
 | 
						|
 | 
						|
#define SCRATCH_BUFFER_REQUEST_SIZE SIZE_64KB
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  ISzAlloc Functions;
 | 
						|
  VOID     *Buffer;
 | 
						|
  UINTN    BufferSize;
 | 
						|
} ISzAllocWithData;
 | 
						|
 | 
						|
/**
 | 
						|
  Allocation routine used by LZMA decompression.
 | 
						|
 | 
						|
  @param P                Pointer to the ISzAlloc instance
 | 
						|
  @param Size             The size in bytes to be allocated
 | 
						|
 | 
						|
  @return The allocated pointer address, or NULL on failure
 | 
						|
**/
 | 
						|
VOID *
 | 
						|
SzAlloc (
 | 
						|
  CONST ISzAlloc *P,
 | 
						|
  size_t Size
 | 
						|
  )
 | 
						|
{
 | 
						|
  VOID *Addr;
 | 
						|
  ISzAllocWithData *Private;
 | 
						|
 | 
						|
  Private = (ISzAllocWithData*) P;
 | 
						|
 | 
						|
  if (Private->BufferSize >= Size) {
 | 
						|
    Addr = Private->Buffer;
 | 
						|
    Private->Buffer = (VOID*) ((UINT8*)Addr + Size);
 | 
						|
    Private->BufferSize -= Size;
 | 
						|
    return Addr;
 | 
						|
  } else {
 | 
						|
    ASSERT (FALSE);
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Free routine used by LZMA decompression.
 | 
						|
 | 
						|
  @param P                Pointer to the ISzAlloc instance
 | 
						|
  @param Address          The address to be freed
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SzFree (
 | 
						|
  CONST ISzAlloc *P,
 | 
						|
  VOID *Address
 | 
						|
  )
 | 
						|
{
 | 
						|
  //
 | 
						|
  // We use the 'scratch buffer' for allocations, so there is no free
 | 
						|
  // operation required.  The scratch buffer will be freed by the caller
 | 
						|
  // of the decompression code.
 | 
						|
  //
 | 
						|
}
 | 
						|
 | 
						|
#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + 8)
 | 
						|
 | 
						|
/**
 | 
						|
  Get the size of the uncompressed buffer by parsing EncodeData header.
 | 
						|
 | 
						|
  @param EncodedData  Pointer to the compressed data.
 | 
						|
 | 
						|
  @return The size of the uncompressed buffer.
 | 
						|
**/
 | 
						|
UINT64
 | 
						|
GetDecodedSizeOfBuf(
 | 
						|
  UINT8 *EncodedData
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT64 DecodedSize;
 | 
						|
  INTN   Index;
 | 
						|
 | 
						|
  /* Parse header */
 | 
						|
  DecodedSize = 0;
 | 
						|
  for (Index = LZMA_PROPS_SIZE + 7; Index >= LZMA_PROPS_SIZE; Index--)
 | 
						|
    DecodedSize = LShiftU64(DecodedSize, 8) + EncodedData[Index];
 | 
						|
 | 
						|
  return DecodedSize;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// LZMA functions and data as defined in local LzmaDecompressLibInternal.h
 | 
						|
//
 | 
						|
 | 
						|
/**
 | 
						|
  Given a Lzma compressed source buffer, this function retrieves the size of
 | 
						|
  the uncompressed buffer and the size of the scratch buffer required
 | 
						|
  to decompress the compressed source buffer.
 | 
						|
 | 
						|
  Retrieves the size of the uncompressed buffer and the temporary scratch buffer
 | 
						|
  required to decompress the buffer specified by Source and SourceSize.
 | 
						|
  The size of the uncompressed buffer is returned in DestinationSize,
 | 
						|
  the size of the scratch buffer is returned in ScratchSize, and RETURN_SUCCESS is returned.
 | 
						|
  This function does not have scratch buffer available to perform a thorough
 | 
						|
  checking of the validity of the source data. It just retrieves the "Original Size"
 | 
						|
  field from the LZMA_HEADER_SIZE beginning bytes of the source data and output it as DestinationSize.
 | 
						|
  And ScratchSize is specific to the decompression implementation.
 | 
						|
 | 
						|
  If SourceSize is less than LZMA_HEADER_SIZE, then ASSERT().
 | 
						|
 | 
						|
  @param  Source          The source buffer containing the compressed data.
 | 
						|
  @param  SourceSize      The size, in bytes, of the source buffer.
 | 
						|
  @param  DestinationSize A pointer to the size, in bytes, of the uncompressed buffer
 | 
						|
                          that will be generated when the compressed buffer specified
 | 
						|
                          by Source and SourceSize is decompressed.
 | 
						|
  @param  ScratchSize     A pointer to the size, in bytes, of the scratch buffer that
 | 
						|
                          is required to decompress the compressed buffer specified
 | 
						|
                          by Source and SourceSize.
 | 
						|
 | 
						|
  @retval  RETURN_SUCCESS The size of the uncompressed data was returned
 | 
						|
                          in DestinationSize and the size of the scratch
 | 
						|
                          buffer was returned in ScratchSize.
 | 
						|
 | 
						|
  @retval RETURN_UNSUPPORTED  DestinationSize cannot be output because the
 | 
						|
                              uncompressed buffer size (in bytes) does not fit
 | 
						|
                              in a UINT32. Output parameters have not been
 | 
						|
                              modified.
 | 
						|
**/
 | 
						|
RETURN_STATUS
 | 
						|
EFIAPI
 | 
						|
LzmaUefiDecompressGetInfo (
 | 
						|
  IN  CONST VOID  *Source,
 | 
						|
  IN  UINT32      SourceSize,
 | 
						|
  OUT UINT32      *DestinationSize,
 | 
						|
  OUT UINT32      *ScratchSize
 | 
						|
  )
 | 
						|
{
 | 
						|
  UInt64  DecodedSize;
 | 
						|
 | 
						|
  ASSERT(SourceSize >= LZMA_HEADER_SIZE);
 | 
						|
 | 
						|
  DecodedSize = GetDecodedSizeOfBuf((UINT8*)Source);
 | 
						|
  if (DecodedSize > MAX_UINT32) {
 | 
						|
    return RETURN_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *DestinationSize = (UINT32)DecodedSize;
 | 
						|
  *ScratchSize = SCRATCH_BUFFER_REQUEST_SIZE;
 | 
						|
  return RETURN_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  Decompresses a Lzma compressed source buffer.
 | 
						|
 | 
						|
  Extracts decompressed data to its original form.
 | 
						|
  If the compressed source data specified by Source is successfully decompressed
 | 
						|
  into Destination, then RETURN_SUCCESS is returned.  If the compressed source data
 | 
						|
  specified by Source is not in a valid compressed data format,
 | 
						|
  then RETURN_INVALID_PARAMETER is returned.
 | 
						|
 | 
						|
  @param  Source      The source buffer containing the compressed data.
 | 
						|
  @param  SourceSize  The size of source buffer.
 | 
						|
  @param  Destination The destination buffer to store the decompressed data
 | 
						|
  @param  Scratch     A temporary scratch buffer that is used to perform the decompression.
 | 
						|
                      This is an optional parameter that may be NULL if the
 | 
						|
                      required scratch buffer size is 0.
 | 
						|
 | 
						|
  @retval  RETURN_SUCCESS Decompression completed successfully, and
 | 
						|
                          the uncompressed buffer is returned in Destination.
 | 
						|
  @retval  RETURN_INVALID_PARAMETER
 | 
						|
                          The source buffer specified by Source is corrupted
 | 
						|
                          (not in a valid compressed format).
 | 
						|
**/
 | 
						|
RETURN_STATUS
 | 
						|
EFIAPI
 | 
						|
LzmaUefiDecompress (
 | 
						|
  IN CONST VOID  *Source,
 | 
						|
  IN UINTN       SourceSize,
 | 
						|
  IN OUT VOID    *Destination,
 | 
						|
  IN OUT VOID    *Scratch
 | 
						|
  )
 | 
						|
{
 | 
						|
  SRes              LzmaResult;
 | 
						|
  ELzmaStatus       Status;
 | 
						|
  SizeT             DecodedBufSize;
 | 
						|
  SizeT             EncodedDataSize;
 | 
						|
  ISzAllocWithData  AllocFuncs;
 | 
						|
 | 
						|
  AllocFuncs.Functions.Alloc  = SzAlloc;
 | 
						|
  AllocFuncs.Functions.Free   = SzFree;
 | 
						|
  AllocFuncs.Buffer           = Scratch;
 | 
						|
  AllocFuncs.BufferSize       = SCRATCH_BUFFER_REQUEST_SIZE;
 | 
						|
 | 
						|
  DecodedBufSize = (SizeT)GetDecodedSizeOfBuf((UINT8*)Source);
 | 
						|
  EncodedDataSize = (SizeT) (SourceSize - LZMA_HEADER_SIZE);
 | 
						|
 | 
						|
  LzmaResult = LzmaDecode(
 | 
						|
    Destination,
 | 
						|
    &DecodedBufSize,
 | 
						|
    (Byte*)((UINT8*)Source + LZMA_HEADER_SIZE),
 | 
						|
    &EncodedDataSize,
 | 
						|
    Source,
 | 
						|
    LZMA_PROPS_SIZE,
 | 
						|
    LZMA_FINISH_END,
 | 
						|
    &Status,
 | 
						|
    &(AllocFuncs.Functions)
 | 
						|
    );
 | 
						|
 | 
						|
  if (LzmaResult == SZ_OK) {
 | 
						|
    return RETURN_SUCCESS;
 | 
						|
  } else {
 | 
						|
    return RETURN_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
}
 | 
						|
 |