diff options
Diffstat (limited to 'src/md/datablob.inl')
-rw-r--r-- | src/md/datablob.inl | 581 |
1 files changed, 581 insertions, 0 deletions
diff --git a/src/md/datablob.inl b/src/md/datablob.inl new file mode 100644 index 0000000000..5589a083a5 --- /dev/null +++ b/src/md/datablob.inl @@ -0,0 +1,581 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// +// File: DataBlob.inl +// + +// +// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed +// endianess). +// +// ====================================================================================== + +#pragma once + +#include "datablob.h" +#include "compressedinteger.h" + +#include "debug_metadata.h" + +namespace MetaData +{ + +// -------------------------------------------------------------------------------------- +// +// Creates empty memory block. +// +inline +DataBlob::DataBlob() +{ + Clear(); +} // DataBlob::DataBlob + +// -------------------------------------------------------------------------------------- +// +// Creates memory block (pbData, of size cbSize). +// +inline +DataBlob::DataBlob( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBlob::DataBlob + +// -------------------------------------------------------------------------------------- +// +// Creates memory block copy. +// +inline +DataBlob::DataBlob( + const DataBlob &source) +{ + m_pbData = source.m_pbData; + m_cbSize = source.m_cbSize; +} // DataBlob::DataBlob + +#ifdef _WIN64 + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d) +#else //!_WIN64 + #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d) +#endif //!_WIN64 + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to empty data. The object could be already initialzied. +// +inline +void +DataBlob::Clear() +{ + m_cbSize = 0; + // For debugging purposes let's put invalid non-NULL pointer here + INDEBUG_MD(m_pbData = const_pbBadFood); +} // DataBlob::Clear + +#undef const_pbBadFood + +// -------------------------------------------------------------------------------------- +// +// Initializes memory block to data (pbData, of size cbSize). The object should be empty before. +// +inline +void +DataBlob::Init( + __in_bcount(cbSize) BYTE *pbData, + UINT32 cbSize) +{ + m_pbData = pbData; + m_cbSize = cbSize; +} // DataBlob::Init + +// -------------------------------------------------------------------------------------- +// +// #PeekUx_Functions +// +// Reads the U1/U2/U4/U8 from the data blob without skipping the read data. +// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. +// Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read data). +// + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU1(__out BYTE *pnValue) const +{ + if (m_cbSize < sizeof(BYTE)) + { + return FALSE; + } + *pnValue = *m_pbData; + return TRUE; +} // DataBlob::PeekU1 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU2(__out UINT16 *pnValue) const +{ + if (m_cbSize < sizeof(UINT16)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL16(m_pbData); + return TRUE; +} // DataBlob::PeekU2 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU4(__out UINT32 *pnValue) const +{ + if (m_cbSize < sizeof(UINT32)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL32(m_pbData); + return TRUE; +} // DataBlob::PeekU4 + +// -------------------------------------------------------------------------------------- +// +// See code:#PeekUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekU8(__out UINT64 *pnValue) const +{ + if (m_cbSize < sizeof(UINT64)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL64(m_pbData); + return TRUE; +} // DataBlob::PeekU8 + +// -------------------------------------------------------------------------------------- +// +// #GetUx_Functions +// +// Reads the U1/U2/U4/U8 from the data blob and skips the read data. +// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then. +// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. +// + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU1(__out BYTE *pnValue) +{ + if (m_cbSize < sizeof(BYTE)) + { + return FALSE; + } + *pnValue = *m_pbData; + SkipBytes_InternalInsecure(sizeof(BYTE)); + return TRUE; +} // DataBlob::GetU1 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU2(__out UINT16 *pnValue) +{ + if (m_cbSize < sizeof(UINT16)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL16(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT16)); + return TRUE; +} // DataBlob::GetU2 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU4(__out UINT32 *pnValue) +{ + if (m_cbSize < sizeof(UINT32)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL32(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT32)); + return TRUE; +} // DataBlob::GetU4 + +// -------------------------------------------------------------------------------------- +// +// See code:#GetUx_Functions above. +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::GetU8(__out UINT64 *pnValue) +{ + if (m_cbSize < sizeof(UINT64)) + { + return FALSE; + } + *pnValue = GET_UNALIGNED_VAL64(m_pbData); + SkipBytes_InternalInsecure(sizeof(UINT64)); + return TRUE; +} // DataBlob::GetU8 + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob +// and skips the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue then. +// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data. +// +__checkReturn +inline +BOOL +DataBlob::GetCompressedU(__out UINT32 *pnValue) +{ + UINT32 cbCompressedValueSize_Ignore; + return GetCompressedU(pnValue, &cbCompressedValueSize_Ignore); +} // DataBlob::GetCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size +// in *pcbCompressedValueSize) from the data blob without skipping the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value +// *pcbCompressedValueSize then. +// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but +// doesn't move the memory block (doesn't skip the read data). +// +__checkReturn +_Success_(return) +inline +BOOL +DataBlob::PeekCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize) +{ + // This algorithm has to be in sync with code:CompressedInteger#Format encoding definition. + // + // Note that this algorithm accepts technically invalid encodings, e.g. + // encoding of value 0 is accepted as 0000 0000 (0x00, valid) and 1000 0000 0000 000 (0x8000, invalid). + + // Is there at least 1 byte? + if (m_cbSize < 1) + { // The data blob is empty, there's not compressed integer stored + return FALSE; + } + if ((*m_pbData & 0x80) == 0x00) + { // 0??? ???? + // The value is compressed into 1 byte + *pnValue = (UINT32)(*m_pbData); + *pcbCompressedValueSize = 1; + return TRUE; + } + // 1??? ???? + + if ((*m_pbData & 0x40) == 0x00) + { // 10?? ???? + // The value is compressed into 2 bytes + if (m_cbSize < 2) + { // The data blob is too short and doesn't contain 2 bytes needed for storing compressed integer + return FALSE; + } + *pnValue = + ((*m_pbData & 0x3f) << 8) | + *(m_pbData + 1); + *pcbCompressedValueSize = 2; + return TRUE; + } + // 11?? ???? + + if ((*m_pbData & 0x20) == 0x00) + { // 110? ???? + // The value is compressed into 4 bytes + if (m_cbSize < 4) + { // The data blob is too short and doesn't contain 4 bytes needed for storing compressed integer + return FALSE; + } + *pnValue = + ((*m_pbData & 0x1f) << 24) | + (*(m_pbData + 1) << 16) | + (*(m_pbData + 2) << 8) | + *(m_pbData + 3); + *pcbCompressedValueSize = 4; + return TRUE; + } + // 111? ???? + // Invalid encoding of the compressed integer + return FALSE; +} // DataBlob::PeekCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size +// in *pcbCompressedValueSize) from the data blob and skips the read data. +// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte +// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value +// *pcbCompressedValueSize then. +// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves +// the memory block behind the read data. +// +__checkReturn +inline +BOOL +DataBlob::GetCompressedU( + __out UINT32 *pnValue, + __out UINT32 *pcbCompressedValueSize) +{ + // Read the compressed integer from withou skipping the read data + BOOL fReadResult = PeekCompressedU( + pnValue, + pcbCompressedValueSize); + // Was the compressed integer read? + if (fReadResult) + { // The compressed integer was read + // Skip the read data + SkipBytes_InternalInsecure(*pcbCompressedValueSize); + } + // Return the (original) read result + return fReadResult; +} // DataBlob::GetCompressedU + +// -------------------------------------------------------------------------------------- +// +// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as +// *pData). +// Returns FALSE if there's not enough data in the blob, clears *pData then. +// Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the +// "read" data. +// +__checkReturn +inline +BOOL +DataBlob::GetDataOfSize( + UINT32 cbDataSize, + __out DataBlob *pData) +{ + if (m_cbSize < cbDataSize) + { // There's not enough data in the memory block + pData->Clear(); + return FALSE; + } + // Fill the "read" data + pData->Init(m_pbData, cbDataSize); + SkipBytes_InternalInsecure(cbDataSize); + return TRUE; +} // DataBlob::GetDataOfSize + +/* +// -------------------------------------------------------------------------------------- +// +// Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that +// the sum doesn't overflow). +// Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes. +// Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows. +// +inline +BOOL +DataBlob::ContainsData_2Parts( + UINT32 cbDataSize1, + UINT32 cbDataSize2) const +{ + S_UINT32 cbDataSize = S_UINT32(cbDataSize1) + S_UITN32(cbDataSize2); + if (cbDataSize.IsOverflow()) + { + return FALSE; + } + return (cbDataSize.Value() <= m_cbSize); +} // DataBlob::ContainsData +*/ + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer to exact size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size to cbSize. +// +__checkReturn +inline +BOOL +DataBlob::TruncateToExactSize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data to size cbSize + m_cbSize = cbSize; + return TRUE; +} // DataBlob::TruncateToExactSize + +// -------------------------------------------------------------------------------------- +// +// Truncates the buffer by size (cbSize). +// Returns FALSE if there's less than cbSize data represented. +// Returns TRUE otherwise and truncates the represented data size by cbSize. +// +__checkReturn +inline +BOOL +DataBlob::TruncateBySize(UINT32 cbSize) +{ + // Check if there's at least cbSize data present + if (m_cbSize < cbSize) + { // There's less than cbSize data present + // Fail the operation + return FALSE; + } + // Truncate represented data by size cbSize + m_cbSize -= cbSize; + return TRUE; +} // DataBlob::TruncateBySize + +#ifdef _DEBUG +// -------------------------------------------------------------------------------------- +// +// Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented +// data. +// +inline +BYTE +DataBlob::Debug_GetByteAtOffset(UINT32 nOffset) const +{ + _ASSERTE(nOffset < m_cbSize); + return m_pbData[nOffset]; +} // DataBlob::Debug_GetByteAtOffset +#endif //_DEBUG + +// -------------------------------------------------------------------------------------- +// +// Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob +// and skips the written data. +// Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed +// integer (bigger than code:CompressedInteger::const_Max). +// Returns TRUE on success and moves the memory block behind the written data. +// +__checkReturn +inline +BOOL +DataBlob::StoreCompressedU(UINT32 nValue) +{ + if (nValue <= CompressedInteger::const_Max1Byte) + { // The value fits into 1 byte + if (m_cbSize < 1) + { // The data blob is empty, we cannot store compressed integer as 1 byte + return FALSE; + } + *m_pbData = (BYTE)nValue; + SkipBytes_InternalInsecure(1); + return TRUE; + } + if (nValue <= CompressedInteger::const_Max2Bytes) + { // The value fits into 2 bytes + if (m_cbSize < 2) + { // The data blob is too short, we cannot store compressed integer as 2 bytes + return FALSE; + } + *m_pbData = (BYTE)(nValue >> 8) | 0x80; + *(m_pbData + 1) = (BYTE)(nValue & 0xff); + SkipBytes_InternalInsecure(2); + return TRUE; + } + if (nValue <= CompressedInteger::const_Max4Bytes) + { // The value fits into 4 bytes + if (m_cbSize < 4) + { // The data blob is too short, we cannot store compressed integer as 4 bytes + return FALSE; + } + *m_pbData = (BYTE)(nValue >> 24) | 0xC0; + *(m_pbData + 1) = (BYTE)((nValue >> 16) & 0xff); + *(m_pbData + 2) = (BYTE)((nValue >> 8) & 0xff); + *(m_pbData + 3) = (BYTE)(nValue & 0xff); + SkipBytes_InternalInsecure(4); + return TRUE; + } + // The value cannot be encoded as compressed integer + return FALSE; +} // DataBlob::StoreCompressedU + +// -------------------------------------------------------------------------------------- +// +// Writes data from *pSource to the data blob and skips the written data. +// Returns FALSE if there's not enough data in the blob. +// Returns TRUE on success and moves memory block behind the written data. +// +__checkReturn +inline +BOOL +DataBlob::StoreData(__in const DataBlob *pSource) +{ + // Check that we have enough space to store the *pSource data + if (m_cbSize < pSource->m_cbSize) + { // There's not enough space to store *pSource data + return FALSE; + } + // Copy the *pSource data to the data blob + memcpy(m_pbData, pSource->m_pbData, pSource->m_cbSize); + // Move the data blob behind copied/written data *pSource + m_pbData += pSource->m_cbSize; + m_cbSize -= pSource->m_cbSize; + + return TRUE; +} // DataBlob::StoreData + +// -------------------------------------------------------------------------------------- +// +// Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that the +// represented memory block contains at least cbSize bytes, otherwise there will be a security issue. +// Should be used only internally, never call it from outside of this class. +// +inline +void +DataBlob::SkipBytes_InternalInsecure(UINT32 cbSize) +{ + // The caller is responsible for this check, just double check here + _ASSERTE(m_cbSize >= cbSize); + // Move the memory block by 'cbSize' bytes + m_pbData += cbSize; + m_cbSize -= cbSize; +} // DataBlob::SkipBytes_InternalInsecure + +}; // namespace MetaData |