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 | |
21 | namespace MetaData |
22 | { |
23 | |
24 | // -------------------------------------------------------------------------------------- |
25 | // |
26 | // Creates empty memory block. |
27 | // |
28 | inline |
29 | DataBlob::DataBlob() |
30 | { |
31 | Clear(); |
32 | } // DataBlob::DataBlob |
33 | |
34 | // -------------------------------------------------------------------------------------- |
35 | // |
36 | // Creates memory block (pbData, of size cbSize). |
37 | // |
38 | inline |
39 | DataBlob::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 | // |
51 | inline |
52 | DataBlob::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 | // |
69 | inline |
70 | void |
71 | DataBlob::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 | // |
84 | inline |
85 | void |
86 | DataBlob::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) |
109 | inline |
110 | BOOL |
111 | DataBlob::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) |
127 | inline |
128 | BOOL |
129 | DataBlob::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) |
145 | inline |
146 | BOOL |
147 | DataBlob::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) |
163 | inline |
164 | BOOL |
165 | DataBlob::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) |
190 | inline |
191 | BOOL |
192 | DataBlob::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) |
209 | inline |
210 | BOOL |
211 | DataBlob::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) |
228 | inline |
229 | BOOL |
230 | DataBlob::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) |
247 | inline |
248 | BOOL |
249 | DataBlob::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 |
269 | inline |
270 | BOOL |
271 | DataBlob::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) |
289 | inline |
290 | BOOL |
291 | DataBlob::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 |
360 | inline |
361 | BOOL |
362 | DataBlob::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 |
389 | inline |
390 | BOOL |
391 | DataBlob::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 | // |
414 | inline |
415 | BOOL |
416 | DataBlob::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 |
436 | inline |
437 | BOOL |
438 | DataBlob::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 |
458 | inline |
459 | BOOL |
460 | DataBlob::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 | // |
479 | inline |
480 | BYTE |
481 | DataBlob::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 |
497 | inline |
498 | BOOL |
499 | DataBlob::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 |
546 | inline |
547 | BOOL |
548 | DataBlob::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 | // |
570 | inline |
571 | void |
572 | DataBlob::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 | |