1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5// File: DataBlob.inl
6//
7
8//
9// Class code:MetaData::DataBlob provides secure access to a block of memory from MetaData (i.e. with fixed
10// endianess).
11//
12// ======================================================================================
13
14#pragma once
15
16#include "datablob.h"
17#include "compressedinteger.h"
18
19#include "debug_metadata.h"
20
21namespace MetaData
22{
23
24// --------------------------------------------------------------------------------------
25//
26// Creates empty memory block.
27//
28inline
29DataBlob::DataBlob()
30{
31 Clear();
32} // DataBlob::DataBlob
33
34// --------------------------------------------------------------------------------------
35//
36// Creates memory block (pbData, of size cbSize).
37//
38inline
39DataBlob::DataBlob(
40 __in_bcount(cbSize) BYTE *pbData,
41 UINT32 cbSize)
42{
43 m_pbData = pbData;
44 m_cbSize = cbSize;
45} // DataBlob::DataBlob
46
47// --------------------------------------------------------------------------------------
48//
49// Creates memory block copy.
50//
51inline
52DataBlob::DataBlob(
53 const DataBlob &source)
54{
55 m_pbData = source.m_pbData;
56 m_cbSize = source.m_cbSize;
57} // DataBlob::DataBlob
58
59#ifdef _WIN64
60 #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00dbaadf00d)
61#else //!_WIN64
62 #define const_pbBadFood (((BYTE *)NULL) + 0xbaadf00d)
63#endif //!_WIN64
64
65// --------------------------------------------------------------------------------------
66//
67// Initializes memory block to empty data. The object could be already initialzied.
68//
69inline
70void
71DataBlob::Clear()
72{
73 m_cbSize = 0;
74 // For debugging purposes let's put invalid non-NULL pointer here
75 INDEBUG_MD(m_pbData = const_pbBadFood);
76} // DataBlob::Clear
77
78#undef const_pbBadFood
79
80// --------------------------------------------------------------------------------------
81//
82// Initializes memory block to data (pbData, of size cbSize). The object should be empty before.
83//
84inline
85void
86DataBlob::Init(
87 __in_bcount(cbSize) BYTE *pbData,
88 UINT32 cbSize)
89{
90 m_pbData = pbData;
91 m_cbSize = cbSize;
92} // DataBlob::Init
93
94// --------------------------------------------------------------------------------------
95//
96// #PeekUx_Functions
97//
98// Reads the U1/U2/U4/U8 from the data blob without skipping the read data.
99// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
100// Returns TRUE otherwise, fills *pnValue, but doesn't move the memory block (doesn't skip the read data).
101//
102
103// --------------------------------------------------------------------------------------
104//
105// See code:#PeekUx_Functions above.
106//
107__checkReturn
108_Success_(return)
109inline
110BOOL
111DataBlob::PeekU1(__out BYTE *pnValue) const
112{
113 if (m_cbSize < sizeof(BYTE))
114 {
115 return FALSE;
116 }
117 *pnValue = *m_pbData;
118 return TRUE;
119} // DataBlob::PeekU1
120
121// --------------------------------------------------------------------------------------
122//
123// See code:#PeekUx_Functions above.
124//
125__checkReturn
126_Success_(return)
127inline
128BOOL
129DataBlob::PeekU2(__out UINT16 *pnValue) const
130{
131 if (m_cbSize < sizeof(UINT16))
132 {
133 return FALSE;
134 }
135 *pnValue = GET_UNALIGNED_VAL16(m_pbData);
136 return TRUE;
137} // DataBlob::PeekU2
138
139// --------------------------------------------------------------------------------------
140//
141// See code:#PeekUx_Functions above.
142//
143__checkReturn
144_Success_(return)
145inline
146BOOL
147DataBlob::PeekU4(__out UINT32 *pnValue) const
148{
149 if (m_cbSize < sizeof(UINT32))
150 {
151 return FALSE;
152 }
153 *pnValue = GET_UNALIGNED_VAL32(m_pbData);
154 return TRUE;
155} // DataBlob::PeekU4
156
157// --------------------------------------------------------------------------------------
158//
159// See code:#PeekUx_Functions above.
160//
161__checkReturn
162_Success_(return)
163inline
164BOOL
165DataBlob::PeekU8(__out UINT64 *pnValue) const
166{
167 if (m_cbSize < sizeof(UINT64))
168 {
169 return FALSE;
170 }
171 *pnValue = GET_UNALIGNED_VAL64(m_pbData);
172 return TRUE;
173} // DataBlob::PeekU8
174
175// --------------------------------------------------------------------------------------
176//
177// #GetUx_Functions
178//
179// Reads the U1/U2/U4/U8 from the data blob and skips the read data.
180// Returns FALSE if there's not enough data in the blob, doesn't initialize the value '*pnValue' then.
181// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
182//
183
184// --------------------------------------------------------------------------------------
185//
186// See code:#GetUx_Functions above.
187//
188__checkReturn
189_Success_(return)
190inline
191BOOL
192DataBlob::GetU1(__out BYTE *pnValue)
193{
194 if (m_cbSize < sizeof(BYTE))
195 {
196 return FALSE;
197 }
198 *pnValue = *m_pbData;
199 SkipBytes_InternalInsecure(sizeof(BYTE));
200 return TRUE;
201} // DataBlob::GetU1
202
203// --------------------------------------------------------------------------------------
204//
205// See code:#GetUx_Functions above.
206//
207__checkReturn
208_Success_(return)
209inline
210BOOL
211DataBlob::GetU2(__out UINT16 *pnValue)
212{
213 if (m_cbSize < sizeof(UINT16))
214 {
215 return FALSE;
216 }
217 *pnValue = GET_UNALIGNED_VAL16(m_pbData);
218 SkipBytes_InternalInsecure(sizeof(UINT16));
219 return TRUE;
220} // DataBlob::GetU2
221
222// --------------------------------------------------------------------------------------
223//
224// See code:#GetUx_Functions above.
225//
226__checkReturn
227_Success_(return)
228inline
229BOOL
230DataBlob::GetU4(__out UINT32 *pnValue)
231{
232 if (m_cbSize < sizeof(UINT32))
233 {
234 return FALSE;
235 }
236 *pnValue = GET_UNALIGNED_VAL32(m_pbData);
237 SkipBytes_InternalInsecure(sizeof(UINT32));
238 return TRUE;
239} // DataBlob::GetU4
240
241// --------------------------------------------------------------------------------------
242//
243// See code:#GetUx_Functions above.
244//
245__checkReturn
246_Success_(return)
247inline
248BOOL
249DataBlob::GetU8(__out UINT64 *pnValue)
250{
251 if (m_cbSize < sizeof(UINT64))
252 {
253 return FALSE;
254 }
255 *pnValue = GET_UNALIGNED_VAL64(m_pbData);
256 SkipBytes_InternalInsecure(sizeof(UINT64));
257 return TRUE;
258} // DataBlob::GetU8
259
260// --------------------------------------------------------------------------------------
261//
262// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) from the data blob
263// and skips the read data.
264// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
265// 111? ????), doesn't initialize the value *pnValue then.
266// Returns TRUE otherwise, fills *pnValue and moves the memory block behind the read data.
267//
268__checkReturn
269inline
270BOOL
271DataBlob::GetCompressedU(__out UINT32 *pnValue)
272{
273 UINT32 cbCompressedValueSize_Ignore;
274 return GetCompressedU(pnValue, &cbCompressedValueSize_Ignore);
275} // DataBlob::GetCompressedU
276
277// --------------------------------------------------------------------------------------
278//
279// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
280// in *pcbCompressedValueSize) from the data blob without skipping the read data.
281// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
282// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
283// *pcbCompressedValueSize then.
284// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4), but
285// doesn't move the memory block (doesn't skip the read data).
286//
287__checkReturn
288_Success_(return)
289inline
290BOOL
291DataBlob::PeekCompressedU(
292 __out UINT32 *pnValue,
293 __out UINT32 *pcbCompressedValueSize)
294{
295 // This algorithm has to be in sync with code:CompressedInteger#Format encoding definition.
296 //
297 // Note that this algorithm accepts technically invalid encodings, e.g.
298 // encoding of value 0 is accepted as 0000 0000 (0x00, valid) and 1000 0000 0000 000 (0x8000, invalid).
299
300 // Is there at least 1 byte?
301 if (m_cbSize < 1)
302 { // The data blob is empty, there's not compressed integer stored
303 return FALSE;
304 }
305 if ((*m_pbData & 0x80) == 0x00)
306 { // 0??? ????
307 // The value is compressed into 1 byte
308 *pnValue = (UINT32)(*m_pbData);
309 *pcbCompressedValueSize = 1;
310 return TRUE;
311 }
312 // 1??? ????
313
314 if ((*m_pbData & 0x40) == 0x00)
315 { // 10?? ????
316 // The value is compressed into 2 bytes
317 if (m_cbSize < 2)
318 { // The data blob is too short and doesn't contain 2 bytes needed for storing compressed integer
319 return FALSE;
320 }
321 *pnValue =
322 ((*m_pbData & 0x3f) << 8) |
323 *(m_pbData + 1);
324 *pcbCompressedValueSize = 2;
325 return TRUE;
326 }
327 // 11?? ????
328
329 if ((*m_pbData & 0x20) == 0x00)
330 { // 110? ????
331 // The value is compressed into 4 bytes
332 if (m_cbSize < 4)
333 { // The data blob is too short and doesn't contain 4 bytes needed for storing compressed integer
334 return FALSE;
335 }
336 *pnValue =
337 ((*m_pbData & 0x1f) << 24) |
338 (*(m_pbData + 1) << 16) |
339 (*(m_pbData + 2) << 8) |
340 *(m_pbData + 3);
341 *pcbCompressedValueSize = 4;
342 return TRUE;
343 }
344 // 111? ????
345 // Invalid encoding of the compressed integer
346 return FALSE;
347} // DataBlob::PeekCompressedU
348
349// --------------------------------------------------------------------------------------
350//
351// Reads compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format - returns the size
352// in *pcbCompressedValueSize) from the data blob and skips the read data.
353// Returns FALSE if there's not enough data in the blob or the compression is invalid (starts with byte
354// 111? ????), doesn't initialize the value *pnValue nor the size of the compressed value
355// *pcbCompressedValueSize then.
356// Returns TRUE otherwise, fills *pnValue and *pcbCompressedValueSize (with number 1,2 or 4) and moves
357// the memory block behind the read data.
358//
359__checkReturn
360inline
361BOOL
362DataBlob::GetCompressedU(
363 __out UINT32 *pnValue,
364 __out UINT32 *pcbCompressedValueSize)
365{
366 // Read the compressed integer from withou skipping the read data
367 BOOL fReadResult = PeekCompressedU(
368 pnValue,
369 pcbCompressedValueSize);
370 // Was the compressed integer read?
371 if (fReadResult)
372 { // The compressed integer was read
373 // Skip the read data
374 SkipBytes_InternalInsecure(*pcbCompressedValueSize);
375 }
376 // Return the (original) read result
377 return fReadResult;
378} // DataBlob::GetCompressedU
379
380// --------------------------------------------------------------------------------------
381//
382// Reads data of size cbDataSize and skips the data (instead of reading the bytes, returns the data as
383// *pData).
384// Returns FALSE if there's not enough data in the blob, clears *pData then.
385// Returns TRUE otherwise, fills *pData with the "read" data and moves the memory block behind the
386// "read" data.
387//
388__checkReturn
389inline
390BOOL
391DataBlob::GetDataOfSize(
392 UINT32 cbDataSize,
393 __out DataBlob *pData)
394{
395 if (m_cbSize < cbDataSize)
396 { // There's not enough data in the memory block
397 pData->Clear();
398 return FALSE;
399 }
400 // Fill the "read" data
401 pData->Init(m_pbData, cbDataSize);
402 SkipBytes_InternalInsecure(cbDataSize);
403 return TRUE;
404} // DataBlob::GetDataOfSize
405
406/*
407// --------------------------------------------------------------------------------------
408//
409// Checks if there's at least cbDataSize1 + cbDataSize2 bytes in the represented memory block (and that
410// the sum doesn't overflow).
411// Returns TRUE if there's >= cbDataSize1 + cbDataSize2 bytes.
412// Returns FALSE otherwise and if cbDataSize1 + cbDataSize2 overflows.
413//
414inline
415BOOL
416DataBlob::ContainsData_2Parts(
417 UINT32 cbDataSize1,
418 UINT32 cbDataSize2) const
419{
420 S_UINT32 cbDataSize = S_UINT32(cbDataSize1) + S_UITN32(cbDataSize2);
421 if (cbDataSize.IsOverflow())
422 {
423 return FALSE;
424 }
425 return (cbDataSize.Value() <= m_cbSize);
426} // DataBlob::ContainsData
427*/
428
429// --------------------------------------------------------------------------------------
430//
431// Truncates the buffer to exact size (cbSize).
432// Returns FALSE if there's less than cbSize data represented.
433// Returns TRUE otherwise and truncates the represented data size to cbSize.
434//
435__checkReturn
436inline
437BOOL
438DataBlob::TruncateToExactSize(UINT32 cbSize)
439{
440 // Check if there's at least cbSize data present
441 if (m_cbSize < cbSize)
442 { // There's less than cbSize data present
443 // Fail the operation
444 return FALSE;
445 }
446 // Truncate represented data to size cbSize
447 m_cbSize = cbSize;
448 return TRUE;
449} // DataBlob::TruncateToExactSize
450
451// --------------------------------------------------------------------------------------
452//
453// Truncates the buffer by size (cbSize).
454// Returns FALSE if there's less than cbSize data represented.
455// Returns TRUE otherwise and truncates the represented data size by cbSize.
456//
457__checkReturn
458inline
459BOOL
460DataBlob::TruncateBySize(UINT32 cbSize)
461{
462 // Check if there's at least cbSize data present
463 if (m_cbSize < cbSize)
464 { // There's less than cbSize data present
465 // Fail the operation
466 return FALSE;
467 }
468 // Truncate represented data by size cbSize
469 m_cbSize -= cbSize;
470 return TRUE;
471} // DataBlob::TruncateBySize
472
473#ifdef _DEBUG
474// --------------------------------------------------------------------------------------
475//
476// Returns U1 value at offset (nOffset). Fires an assert if the offset is behind the end of represented
477// data.
478//
479inline
480BYTE
481DataBlob::Debug_GetByteAtOffset(UINT32 nOffset) const
482{
483 _ASSERTE(nOffset < m_cbSize);
484 return m_pbData[nOffset];
485} // DataBlob::Debug_GetByteAtOffset
486#endif //_DEBUG
487
488// --------------------------------------------------------------------------------------
489//
490// Writes compressed integer (1, 2 or 4 bytes of format code:CompressedInteger#Format) to the data blob
491// and skips the written data.
492// Returns FALSE if there's not enough data in the blob or the value cannot be encoded as compressed
493// integer (bigger than code:CompressedInteger::const_Max).
494// Returns TRUE on success and moves the memory block behind the written data.
495//
496__checkReturn
497inline
498BOOL
499DataBlob::StoreCompressedU(UINT32 nValue)
500{
501 if (nValue <= CompressedInteger::const_Max1Byte)
502 { // The value fits into 1 byte
503 if (m_cbSize < 1)
504 { // The data blob is empty, we cannot store compressed integer as 1 byte
505 return FALSE;
506 }
507 *m_pbData = (BYTE)nValue;
508 SkipBytes_InternalInsecure(1);
509 return TRUE;
510 }
511 if (nValue <= CompressedInteger::const_Max2Bytes)
512 { // The value fits into 2 bytes
513 if (m_cbSize < 2)
514 { // The data blob is too short, we cannot store compressed integer as 2 bytes
515 return FALSE;
516 }
517 *m_pbData = (BYTE)(nValue >> 8) | 0x80;
518 *(m_pbData + 1) = (BYTE)(nValue & 0xff);
519 SkipBytes_InternalInsecure(2);
520 return TRUE;
521 }
522 if (nValue <= CompressedInteger::const_Max4Bytes)
523 { // The value fits into 4 bytes
524 if (m_cbSize < 4)
525 { // The data blob is too short, we cannot store compressed integer as 4 bytes
526 return FALSE;
527 }
528 *m_pbData = (BYTE)(nValue >> 24) | 0xC0;
529 *(m_pbData + 1) = (BYTE)((nValue >> 16) & 0xff);
530 *(m_pbData + 2) = (BYTE)((nValue >> 8) & 0xff);
531 *(m_pbData + 3) = (BYTE)(nValue & 0xff);
532 SkipBytes_InternalInsecure(4);
533 return TRUE;
534 }
535 // The value cannot be encoded as compressed integer
536 return FALSE;
537} // DataBlob::StoreCompressedU
538
539// --------------------------------------------------------------------------------------
540//
541// Writes data from *pSource to the data blob and skips the written data.
542// Returns FALSE if there's not enough data in the blob.
543// Returns TRUE on success and moves memory block behind the written data.
544//
545__checkReturn
546inline
547BOOL
548DataBlob::StoreData(__in const DataBlob *pSource)
549{
550 // Check that we have enough space to store the *pSource data
551 if (m_cbSize < pSource->m_cbSize)
552 { // There's not enough space to store *pSource data
553 return FALSE;
554 }
555 // Copy the *pSource data to the data blob
556 memcpy(m_pbData, pSource->m_pbData, pSource->m_cbSize);
557 // Move the data blob behind copied/written data *pSource
558 m_pbData += pSource->m_cbSize;
559 m_cbSize -= pSource->m_cbSize;
560
561 return TRUE;
562} // DataBlob::StoreData
563
564// --------------------------------------------------------------------------------------
565//
566// Skips cbSize bytes in the represented memory block. The caller is responsible for making sure that the
567// represented memory block contains at least cbSize bytes, otherwise there will be a security issue.
568// Should be used only internally, never call it from outside of this class.
569//
570inline
571void
572DataBlob::SkipBytes_InternalInsecure(UINT32 cbSize)
573{
574 // The caller is responsible for this check, just double check here
575 _ASSERTE(m_cbSize >= cbSize);
576 // Move the memory block by 'cbSize' bytes
577 m_pbData += cbSize;
578 m_cbSize -= cbSize;
579} // DataBlob::SkipBytes_InternalInsecure
580
581}; // namespace MetaData
582