diff options
-rw-r--r-- | meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0001.patch | 985 | ||||
-rw-r--r-- | meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0002.patch | 889 | ||||
-rw-r--r-- | meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0003.patch | 55 | ||||
-rw-r--r-- | meta/recipes-core/ovmf/ovmf_git.bb | 3 |
4 files changed, 1932 insertions, 0 deletions
diff --git a/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0001.patch b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0001.patch new file mode 100644 index 0000000000..93cefe7740 --- /dev/null +++ b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0001.patch | |||
@@ -0,0 +1,985 @@ | |||
1 | From 224446543206450ddb5830e6abd026d61d3c7f4b Mon Sep 17 00:00:00 2001 | ||
2 | From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com> | ||
3 | Date: Fri, 12 Jan 2024 02:16:01 +0800 | ||
4 | Subject: [PATCH] SecurityPkg: DxeTpm2MeasureBootLib: SECURITY PATCH 4117 - CVE | ||
5 | 2022-36763 | ||
6 | |||
7 | This commit contains the patch files and tests for DxeTpm2MeasureBootLib | ||
8 | CVE 2022-36763. | ||
9 | |||
10 | Cc: Jiewen Yao <jiewen.yao@intel.com> | ||
11 | |||
12 | Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com> | ||
13 | |||
14 | CVE: CVE-2022-36763 | ||
15 | |||
16 | Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/224446543206450ddb5830e6abd026d61d3c7f4b] | ||
17 | |||
18 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
19 | --- | ||
20 | .../DxeTpm2MeasureBootLib.c | 69 ++-- | ||
21 | .../DxeTpm2MeasureBootLib.inf | 4 +- | ||
22 | .../DxeTpm2MeasureBootLibSanitization.c | 275 ++++++++++++++++ | ||
23 | .../DxeTpm2MeasureBootLibSanitization.h | 113 +++++++ | ||
24 | .../DxeTpm2MeasureBootLibSanitizationTest.c | 303 ++++++++++++++++++ | ||
25 | ...Tpm2MeasureBootLibSanitizationTestHost.inf | 28 ++ | ||
26 | SecurityPkg/SecurityPkg.ci.yaml | 1 + | ||
27 | 7 files changed, 763 insertions(+), 30 deletions(-) | ||
28 | create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c | ||
29 | create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h | ||
30 | create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c | ||
31 | create mode 100644 SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf | ||
32 | |||
33 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c | ||
34 | index 36a256a7af..0475103d6e 100644 | ||
35 | --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c | ||
36 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.c | ||
37 | @@ -20,6 +20,8 @@ Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR> | ||
38 | (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | ||
39 | SPDX-License-Identifier: BSD-2-Clause-Patent | ||
40 | |||
41 | +Copyright (c) Microsoft Corporation.<BR> | ||
42 | +SPDX-License-Identifier: BSD-2-Clause-Patent | ||
43 | **/ | ||
44 | |||
45 | #include <PiDxe.h> | ||
46 | @@ -44,6 +46,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent | ||
47 | #include <Library/HobLib.h> | ||
48 | #include <Protocol/CcMeasurement.h> | ||
49 | |||
50 | +#include "DxeTpm2MeasureBootLibSanitization.h" | ||
51 | + | ||
52 | typedef struct { | ||
53 | EFI_TCG2_PROTOCOL *Tcg2Protocol; | ||
54 | EFI_CC_MEASUREMENT_PROTOCOL *CcProtocol; | ||
55 | @@ -144,10 +148,11 @@ Tcg2MeasureGptTable ( | ||
56 | EFI_TCG2_EVENT *Tcg2Event; | ||
57 | EFI_CC_EVENT *CcEvent; | ||
58 | EFI_GPT_DATA *GptData; | ||
59 | - UINT32 EventSize; | ||
60 | + UINT32 TcgEventSize; | ||
61 | EFI_TCG2_PROTOCOL *Tcg2Protocol; | ||
62 | EFI_CC_MEASUREMENT_PROTOCOL *CcProtocol; | ||
63 | EFI_CC_MR_INDEX MrIndex; | ||
64 | + UINT32 AllocSize; | ||
65 | |||
66 | if (mTcg2MeasureGptCount > 0) { | ||
67 | return EFI_SUCCESS; | ||
68 | @@ -195,25 +200,22 @@ Tcg2MeasureGptTable ( | ||
69 | BlockIo->Media->BlockSize, | ||
70 | (UINT8 *)PrimaryHeader | ||
71 | ); | ||
72 | - if (EFI_ERROR (Status)) { | ||
73 | - DEBUG ((DEBUG_ERROR, "Failed to Read Partition Table Header!\n")); | ||
74 | + if (EFI_ERROR (Status) || EFI_ERROR (SanitizeEfiPartitionTableHeader (PrimaryHeader, BlockIo))) { | ||
75 | + DEBUG ((DEBUG_ERROR, "Failed to read Partition Table Header or invalid Partition Table Header!\n")); | ||
76 | FreePool (PrimaryHeader); | ||
77 | return EFI_DEVICE_ERROR; | ||
78 | } | ||
79 | |||
80 | // | ||
81 | - // PrimaryHeader->SizeOfPartitionEntry should not be zero | ||
82 | + // Read the partition entry. | ||
83 | // | ||
84 | - if (PrimaryHeader->SizeOfPartitionEntry == 0) { | ||
85 | - DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry should not be zero!\n")); | ||
86 | + Status = SanitizePrimaryHeaderAllocationSize (PrimaryHeader, &AllocSize); | ||
87 | + if (EFI_ERROR (Status)) { | ||
88 | FreePool (PrimaryHeader); | ||
89 | return EFI_BAD_BUFFER_SIZE; | ||
90 | } | ||
91 | |||
92 | - // | ||
93 | - // Read the partition entry. | ||
94 | - // | ||
95 | - EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); | ||
96 | + EntryPtr = (UINT8 *)AllocatePool (AllocSize); | ||
97 | if (EntryPtr == NULL) { | ||
98 | FreePool (PrimaryHeader); | ||
99 | return EFI_OUT_OF_RESOURCES; | ||
100 | @@ -223,7 +225,7 @@ Tcg2MeasureGptTable ( | ||
101 | DiskIo, | ||
102 | BlockIo->Media->MediaId, | ||
103 | MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), | ||
104 | - PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry, | ||
105 | + AllocSize, | ||
106 | EntryPtr | ||
107 | ); | ||
108 | if (EFI_ERROR (Status)) { | ||
109 | @@ -248,16 +250,21 @@ Tcg2MeasureGptTable ( | ||
110 | // | ||
111 | // Prepare Data for Measurement (CcProtocol and Tcg2Protocol) | ||
112 | // | ||
113 | - EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) | ||
114 | - + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry); | ||
115 | - EventPtr = (UINT8 *)AllocateZeroPool (EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)); | ||
116 | + Status = SanitizePrimaryHeaderGptEventSize (PrimaryHeader, NumberOfPartition, &TcgEventSize); | ||
117 | + if (EFI_ERROR (Status)) { | ||
118 | + FreePool (PrimaryHeader); | ||
119 | + FreePool (EntryPtr); | ||
120 | + return EFI_DEVICE_ERROR; | ||
121 | + } | ||
122 | + | ||
123 | + EventPtr = (UINT8 *)AllocateZeroPool (TcgEventSize); | ||
124 | if (EventPtr == NULL) { | ||
125 | Status = EFI_OUT_OF_RESOURCES; | ||
126 | goto Exit; | ||
127 | } | ||
128 | |||
129 | Tcg2Event = (EFI_TCG2_EVENT *)EventPtr; | ||
130 | - Tcg2Event->Size = EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event); | ||
131 | + Tcg2Event->Size = TcgEventSize; | ||
132 | Tcg2Event->Header.HeaderSize = sizeof (EFI_TCG2_EVENT_HEADER); | ||
133 | Tcg2Event->Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION; | ||
134 | Tcg2Event->Header.PCRIndex = 5; | ||
135 | @@ -310,7 +317,7 @@ Tcg2MeasureGptTable ( | ||
136 | CcProtocol, | ||
137 | 0, | ||
138 | (EFI_PHYSICAL_ADDRESS)(UINTN)(VOID *)GptData, | ||
139 | - (UINT64)EventSize, | ||
140 | + (UINT64)TcgEventSize - OFFSET_OF (EFI_TCG2_EVENT, Event), | ||
141 | CcEvent | ||
142 | ); | ||
143 | if (!EFI_ERROR (Status)) { | ||
144 | @@ -326,7 +333,7 @@ Tcg2MeasureGptTable ( | ||
145 | Tcg2Protocol, | ||
146 | 0, | ||
147 | (EFI_PHYSICAL_ADDRESS)(UINTN)(VOID *)GptData, | ||
148 | - (UINT64)EventSize, | ||
149 | + (UINT64)TcgEventSize - OFFSET_OF (EFI_TCG2_EVENT, Event), | ||
150 | Tcg2Event | ||
151 | ); | ||
152 | if (!EFI_ERROR (Status)) { | ||
153 | @@ -443,11 +450,13 @@ Tcg2MeasurePeImage ( | ||
154 | Tcg2Event->Header.PCRIndex = 2; | ||
155 | break; | ||
156 | default: | ||
157 | - DEBUG (( | ||
158 | - DEBUG_ERROR, | ||
159 | - "Tcg2MeasurePeImage: Unknown subsystem type %d", | ||
160 | - ImageType | ||
161 | - )); | ||
162 | + DEBUG ( | ||
163 | + ( | ||
164 | + DEBUG_ERROR, | ||
165 | + "Tcg2MeasurePeImage: Unknown subsystem type %d", | ||
166 | + ImageType | ||
167 | + ) | ||
168 | + ); | ||
169 | goto Finish; | ||
170 | } | ||
171 | |||
172 | @@ -515,7 +524,7 @@ Finish: | ||
173 | |||
174 | @param MeasureBootProtocols Pointer to the located measure boot protocol instances. | ||
175 | |||
176 | - @retval EFI_SUCCESS Sucessfully locate the measure boot protocol instances (at least one instance). | ||
177 | + @retval EFI_SUCCESS Successfully locate the measure boot protocol instances (at least one instance). | ||
178 | @retval EFI_UNSUPPORTED Measure boot is not supported. | ||
179 | **/ | ||
180 | EFI_STATUS | ||
181 | @@ -646,12 +655,14 @@ DxeTpm2MeasureBootHandler ( | ||
182 | return EFI_SUCCESS; | ||
183 | } | ||
184 | |||
185 | - DEBUG (( | ||
186 | - DEBUG_INFO, | ||
187 | - "Tcg2Protocol = %p, CcMeasurementProtocol = %p\n", | ||
188 | - MeasureBootProtocols.Tcg2Protocol, | ||
189 | - MeasureBootProtocols.CcProtocol | ||
190 | - )); | ||
191 | + DEBUG ( | ||
192 | + ( | ||
193 | + DEBUG_INFO, | ||
194 | + "Tcg2Protocol = %p, CcMeasurementProtocol = %p\n", | ||
195 | + MeasureBootProtocols.Tcg2Protocol, | ||
196 | + MeasureBootProtocols.CcProtocol | ||
197 | + ) | ||
198 | + ); | ||
199 | |||
200 | // | ||
201 | // Copy File Device Path | ||
202 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf | ||
203 | index 6dca79a20c..28995f438d 100644 | ||
204 | --- a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf | ||
205 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLib.inf | ||
206 | @@ -37,6 +37,8 @@ | ||
207 | |||
208 | [Sources] | ||
209 | DxeTpm2MeasureBootLib.c | ||
210 | + DxeTpm2MeasureBootLibSanitization.c | ||
211 | + DxeTpm2MeasureBootLibSanitization.h | ||
212 | |||
213 | [Packages] | ||
214 | MdePkg/MdePkg.dec | ||
215 | @@ -46,6 +48,7 @@ | ||
216 | |||
217 | [LibraryClasses] | ||
218 | BaseMemoryLib | ||
219 | + SafeIntLib | ||
220 | DebugLib | ||
221 | MemoryAllocationLib | ||
222 | DevicePathLib | ||
223 | @@ -65,4 +68,3 @@ | ||
224 | gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES | ||
225 | gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES | ||
226 | gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES | ||
227 | - | ||
228 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c | ||
229 | new file mode 100644 | ||
230 | index 0000000000..e2309655d3 | ||
231 | --- /dev/null | ||
232 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.c | ||
233 | @@ -0,0 +1,275 @@ | ||
234 | +/** @file | ||
235 | + The library instance provides security service of TPM2 measure boot and | ||
236 | + Confidential Computing (CC) measure boot. | ||
237 | + | ||
238 | + Caution: This file requires additional review when modified. | ||
239 | + This library will have external input - PE/COFF image and GPT partition. | ||
240 | + This external input must be validated carefully to avoid security issue like | ||
241 | + buffer overflow, integer overflow. | ||
242 | + | ||
243 | + This file will pull out the validation logic from the following functions, in an | ||
244 | + attempt to validate the untrusted input in the form of unit tests | ||
245 | + | ||
246 | + These are those functions: | ||
247 | + | ||
248 | + DxeTpm2MeasureBootLibImageRead() function will make sure the PE/COFF image content | ||
249 | + read is within the image buffer. | ||
250 | + | ||
251 | + Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse | ||
252 | + partition data carefully. | ||
253 | + | ||
254 | + Copyright (c) Microsoft Corporation.<BR> | ||
255 | + SPDX-License-Identifier: BSD-2-Clause-Patent | ||
256 | +**/ | ||
257 | +#include <Uefi.h> | ||
258 | +#include <Uefi/UefiSpec.h> | ||
259 | +#include <Library/SafeIntLib.h> | ||
260 | +#include <Library/UefiLib.h> | ||
261 | +#include <Library/DebugLib.h> | ||
262 | +#include <Library/BaseLib.h> | ||
263 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
264 | +#include <Protocol/BlockIo.h> | ||
265 | +#include <Library/MemoryAllocationLib.h> | ||
266 | + | ||
267 | +#include "DxeTpm2MeasureBootLibSanitization.h" | ||
268 | + | ||
269 | +#define GPT_HEADER_REVISION_V1 0x00010000 | ||
270 | + | ||
271 | +/** | ||
272 | + This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse | ||
273 | + However this function will not attempt to verify the validity of the GPT partition | ||
274 | + It will check the following: | ||
275 | + - Signature | ||
276 | + - Revision | ||
277 | + - AlternateLBA | ||
278 | + - FirstUsableLBA | ||
279 | + - LastUsableLBA | ||
280 | + - PartitionEntryLBA | ||
281 | + - NumberOfPartitionEntries | ||
282 | + - SizeOfPartitionEntry | ||
283 | + - BlockIo | ||
284 | + | ||
285 | + @param[in] PrimaryHeader | ||
286 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
287 | + | ||
288 | + @param[in] BlockIo | ||
289 | + Pointer to the EFI_BLOCK_IO_PROTOCOL structure. | ||
290 | + | ||
291 | + @retval EFI_SUCCESS | ||
292 | + The EFI_PARTITION_TABLE_HEADER structure is valid. | ||
293 | + | ||
294 | + @retval EFI_INVALID_PARAMETER | ||
295 | + The EFI_PARTITION_TABLE_HEADER structure is invalid. | ||
296 | +**/ | ||
297 | +EFI_STATUS | ||
298 | +EFIAPI | ||
299 | +SanitizeEfiPartitionTableHeader ( | ||
300 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
301 | + IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo | ||
302 | + ) | ||
303 | +{ | ||
304 | + // | ||
305 | + // Verify that the input parameters are safe to use | ||
306 | + // | ||
307 | + if (PrimaryHeader == NULL) { | ||
308 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n")); | ||
309 | + return EFI_INVALID_PARAMETER; | ||
310 | + } | ||
311 | + | ||
312 | + if ((BlockIo == NULL) || (BlockIo->Media == NULL)) { | ||
313 | + DEBUG ((DEBUG_ERROR, "Invalid BlockIo!\n")); | ||
314 | + return EFI_INVALID_PARAMETER; | ||
315 | + } | ||
316 | + | ||
317 | + // | ||
318 | + // The signature must be EFI_PTAB_HEADER_ID ("EFI PART" in ASCII) | ||
319 | + // | ||
320 | + if (PrimaryHeader->Header.Signature != EFI_PTAB_HEADER_ID) { | ||
321 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n")); | ||
322 | + return EFI_DEVICE_ERROR; | ||
323 | + } | ||
324 | + | ||
325 | + // | ||
326 | + // The version must be GPT_HEADER_REVISION_V1 (0x00010000) | ||
327 | + // | ||
328 | + if (PrimaryHeader->Header.Revision != GPT_HEADER_REVISION_V1) { | ||
329 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header Revision!\n")); | ||
330 | + return EFI_DEVICE_ERROR; | ||
331 | + } | ||
332 | + | ||
333 | + // | ||
334 | + // The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size | ||
335 | + // | ||
336 | + if ((PrimaryHeader->Header.HeaderSize < sizeof (EFI_PARTITION_TABLE_HEADER)) || (PrimaryHeader->Header.HeaderSize > BlockIo->Media->BlockSize)) { | ||
337 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header HeaderSize!\n")); | ||
338 | + return EFI_DEVICE_ERROR; | ||
339 | + } | ||
340 | + | ||
341 | + // | ||
342 | + // The partition entries should all be before the first usable block | ||
343 | + // | ||
344 | + if (PrimaryHeader->FirstUsableLBA <= PrimaryHeader->PartitionEntryLBA) { | ||
345 | + DEBUG ((DEBUG_ERROR, "GPT PartitionEntryLBA is not less than FirstUsableLBA!\n")); | ||
346 | + return EFI_DEVICE_ERROR; | ||
347 | + } | ||
348 | + | ||
349 | + // | ||
350 | + // Check that the PartitionEntryLBA greater than the Max LBA | ||
351 | + // This will be used later for multiplication | ||
352 | + // | ||
353 | + if (PrimaryHeader->PartitionEntryLBA > DivU64x32 (MAX_UINT64, BlockIo->Media->BlockSize)) { | ||
354 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header PartitionEntryLBA!\n")); | ||
355 | + return EFI_DEVICE_ERROR; | ||
356 | + } | ||
357 | + | ||
358 | + // | ||
359 | + // Check that the number of partition entries is greater than zero | ||
360 | + // | ||
361 | + if (PrimaryHeader->NumberOfPartitionEntries == 0) { | ||
362 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n")); | ||
363 | + return EFI_DEVICE_ERROR; | ||
364 | + } | ||
365 | + | ||
366 | + // | ||
367 | + // SizeOfPartitionEntry must be 128, 256, 512... improper size may lead to accessing uninitialized memory | ||
368 | + // | ||
369 | + if ((PrimaryHeader->SizeOfPartitionEntry < 128) || ((PrimaryHeader->SizeOfPartitionEntry & (PrimaryHeader->SizeOfPartitionEntry - 1)) != 0)) { | ||
370 | + DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!\n")); | ||
371 | + return EFI_DEVICE_ERROR; | ||
372 | + } | ||
373 | + | ||
374 | + // | ||
375 | + // This check is to prevent overflow when calculating the allocation size for the partition entries | ||
376 | + // This check will be used later for multiplication | ||
377 | + // | ||
378 | + if (PrimaryHeader->NumberOfPartitionEntries > DivU64x32 (MAX_UINT64, PrimaryHeader->SizeOfPartitionEntry)) { | ||
379 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n")); | ||
380 | + return EFI_DEVICE_ERROR; | ||
381 | + } | ||
382 | + | ||
383 | + return EFI_SUCCESS; | ||
384 | +} | ||
385 | + | ||
386 | +/** | ||
387 | + This function will validate that the allocation size from the primary header is sane | ||
388 | + It will check the following: | ||
389 | + - AllocationSize does not overflow | ||
390 | + | ||
391 | + @param[in] PrimaryHeader | ||
392 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
393 | + | ||
394 | + @param[out] AllocationSize | ||
395 | + Pointer to the allocation size. | ||
396 | + | ||
397 | + @retval EFI_SUCCESS | ||
398 | + The allocation size is valid. | ||
399 | + | ||
400 | + @retval EFI_OUT_OF_RESOURCES | ||
401 | + The allocation size is invalid. | ||
402 | +**/ | ||
403 | +EFI_STATUS | ||
404 | +EFIAPI | ||
405 | +SanitizePrimaryHeaderAllocationSize ( | ||
406 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
407 | + OUT UINT32 *AllocationSize | ||
408 | + ) | ||
409 | +{ | ||
410 | + EFI_STATUS Status; | ||
411 | + | ||
412 | + if (PrimaryHeader == NULL) { | ||
413 | + return EFI_INVALID_PARAMETER; | ||
414 | + } | ||
415 | + | ||
416 | + if (AllocationSize == NULL) { | ||
417 | + return EFI_INVALID_PARAMETER; | ||
418 | + } | ||
419 | + | ||
420 | + // | ||
421 | + // Replacing logic: | ||
422 | + // PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry; | ||
423 | + // | ||
424 | + Status = SafeUint32Mult (PrimaryHeader->NumberOfPartitionEntries, PrimaryHeader->SizeOfPartitionEntry, AllocationSize); | ||
425 | + if (EFI_ERROR (Status)) { | ||
426 | + DEBUG ((DEBUG_ERROR, "Allocation Size would have overflowed!\n")); | ||
427 | + return EFI_BAD_BUFFER_SIZE; | ||
428 | + } | ||
429 | + | ||
430 | + return EFI_SUCCESS; | ||
431 | +} | ||
432 | + | ||
433 | +/** | ||
434 | + This function will validate that the Gpt Event Size calculated from the primary header is sane | ||
435 | + It will check the following: | ||
436 | + - EventSize does not overflow | ||
437 | + | ||
438 | + Important: This function includes the entire length of the allocated space, including | ||
439 | + (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) . When hashing the buffer allocated with this | ||
440 | + size, the caller must subtract the size of the (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) | ||
441 | + from the size of the buffer before hashing. | ||
442 | + | ||
443 | + @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
444 | + @param[in] NumberOfPartition - Number of partitions. | ||
445 | + @param[out] EventSize - Pointer to the event size. | ||
446 | + | ||
447 | + @retval EFI_SUCCESS | ||
448 | + The event size is valid. | ||
449 | + | ||
450 | + @retval EFI_OUT_OF_RESOURCES | ||
451 | + Overflow would have occurred. | ||
452 | + | ||
453 | + @retval EFI_INVALID_PARAMETER | ||
454 | + One of the passed parameters was invalid. | ||
455 | +**/ | ||
456 | +EFI_STATUS | ||
457 | +SanitizePrimaryHeaderGptEventSize ( | ||
458 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
459 | + IN UINTN NumberOfPartition, | ||
460 | + OUT UINT32 *EventSize | ||
461 | + ) | ||
462 | +{ | ||
463 | + EFI_STATUS Status; | ||
464 | + UINT32 SafeNumberOfPartitions; | ||
465 | + | ||
466 | + if (PrimaryHeader == NULL) { | ||
467 | + return EFI_INVALID_PARAMETER; | ||
468 | + } | ||
469 | + | ||
470 | + if (EventSize == NULL) { | ||
471 | + return EFI_INVALID_PARAMETER; | ||
472 | + } | ||
473 | + | ||
474 | + // | ||
475 | + // We shouldn't even attempt to perform the multiplication if the number of partitions is greater than the maximum value of UINT32 | ||
476 | + // | ||
477 | + Status = SafeUintnToUint32 (NumberOfPartition, &SafeNumberOfPartitions); | ||
478 | + if (EFI_ERROR (Status)) { | ||
479 | + DEBUG ((DEBUG_ERROR, "NumberOfPartition would have overflowed!\n")); | ||
480 | + return EFI_INVALID_PARAMETER; | ||
481 | + } | ||
482 | + | ||
483 | + // | ||
484 | + // Replacing logic: | ||
485 | + // (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry); | ||
486 | + // | ||
487 | + Status = SafeUint32Mult (SafeNumberOfPartitions, PrimaryHeader->SizeOfPartitionEntry, EventSize); | ||
488 | + if (EFI_ERROR (Status)) { | ||
489 | + DEBUG ((DEBUG_ERROR, "Event Size would have overflowed!\n")); | ||
490 | + return EFI_BAD_BUFFER_SIZE; | ||
491 | + } | ||
492 | + | ||
493 | + // | ||
494 | + // Replacing logic: | ||
495 | + // *EventSize + sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event); | ||
496 | + // | ||
497 | + Status = SafeUint32Add ( | ||
498 | + OFFSET_OF (EFI_TCG2_EVENT, Event) + OFFSET_OF (EFI_GPT_DATA, Partitions), | ||
499 | + *EventSize, | ||
500 | + EventSize | ||
501 | + ); | ||
502 | + if (EFI_ERROR (Status)) { | ||
503 | + DEBUG ((DEBUG_ERROR, "Event Size would have overflowed because of GPTData!\n")); | ||
504 | + return EFI_BAD_BUFFER_SIZE; | ||
505 | + } | ||
506 | + | ||
507 | + return EFI_SUCCESS; | ||
508 | +} | ||
509 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h | ||
510 | new file mode 100644 | ||
511 | index 0000000000..048b738987 | ||
512 | --- /dev/null | ||
513 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/DxeTpm2MeasureBootLibSanitization.h | ||
514 | @@ -0,0 +1,113 @@ | ||
515 | +/** @file | ||
516 | + This file includes the function prototypes for the sanitization functions. | ||
517 | + | ||
518 | + These are those functions: | ||
519 | + | ||
520 | + DxeTpm2MeasureBootLibImageRead() function will make sure the PE/COFF image content | ||
521 | + read is within the image buffer. | ||
522 | + | ||
523 | + Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse | ||
524 | + partition data carefully. | ||
525 | + | ||
526 | + Copyright (c) Microsoft Corporation.<BR> | ||
527 | + SPDX-License-Identifier: BSD-2-Clause-Patent | ||
528 | + | ||
529 | +**/ | ||
530 | + | ||
531 | +#ifndef DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_ | ||
532 | +#define DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_ | ||
533 | + | ||
534 | +#include <Uefi.h> | ||
535 | +#include <Uefi/UefiSpec.h> | ||
536 | +#include <Protocol/BlockIo.h> | ||
537 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
538 | +#include <Protocol/Tcg2Protocol.h> | ||
539 | + | ||
540 | +/** | ||
541 | + This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse | ||
542 | + However this function will not attempt to verify the validity of the GPT partition | ||
543 | + It will check the following: | ||
544 | + - Signature | ||
545 | + - Revision | ||
546 | + - AlternateLBA | ||
547 | + - FirstUsableLBA | ||
548 | + - LastUsableLBA | ||
549 | + - PartitionEntryLBA | ||
550 | + - NumberOfPartitionEntries | ||
551 | + - SizeOfPartitionEntry | ||
552 | + - BlockIo | ||
553 | + | ||
554 | + @param[in] PrimaryHeader | ||
555 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
556 | + | ||
557 | + @param[in] BlockIo | ||
558 | + Pointer to the EFI_BLOCK_IO_PROTOCOL structure. | ||
559 | + | ||
560 | + @retval EFI_SUCCESS | ||
561 | + The EFI_PARTITION_TABLE_HEADER structure is valid. | ||
562 | + | ||
563 | + @retval EFI_INVALID_PARAMETER | ||
564 | + The EFI_PARTITION_TABLE_HEADER structure is invalid. | ||
565 | +**/ | ||
566 | +EFI_STATUS | ||
567 | +EFIAPI | ||
568 | +SanitizeEfiPartitionTableHeader ( | ||
569 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
570 | + IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo | ||
571 | + ); | ||
572 | + | ||
573 | +/** | ||
574 | + This function will validate that the allocation size from the primary header is sane | ||
575 | + It will check the following: | ||
576 | + - AllocationSize does not overflow | ||
577 | + | ||
578 | + @param[in] PrimaryHeader | ||
579 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
580 | + | ||
581 | + @param[out] AllocationSize | ||
582 | + Pointer to the allocation size. | ||
583 | + | ||
584 | + @retval EFI_SUCCESS | ||
585 | + The allocation size is valid. | ||
586 | + | ||
587 | + @retval EFI_OUT_OF_RESOURCES | ||
588 | + The allocation size is invalid. | ||
589 | +**/ | ||
590 | +EFI_STATUS | ||
591 | +EFIAPI | ||
592 | +SanitizePrimaryHeaderAllocationSize ( | ||
593 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
594 | + OUT UINT32 *AllocationSize | ||
595 | + ); | ||
596 | + | ||
597 | +/** | ||
598 | + This function will validate that the Gpt Event Size calculated from the primary header is sane | ||
599 | + It will check the following: | ||
600 | + - EventSize does not overflow | ||
601 | + | ||
602 | + Important: This function includes the entire length of the allocated space, including | ||
603 | + (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) . When hashing the buffer allocated with this | ||
604 | + size, the caller must subtract the size of the (sizeof (EFI_TCG2_EVENT) - sizeof (Tcg2Event->Event)) | ||
605 | + from the size of the buffer before hashing. | ||
606 | + | ||
607 | + @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
608 | + @param[in] NumberOfPartition - Number of partitions. | ||
609 | + @param[out] EventSize - Pointer to the event size. | ||
610 | + | ||
611 | + @retval EFI_SUCCESS | ||
612 | + The event size is valid. | ||
613 | + | ||
614 | + @retval EFI_OUT_OF_RESOURCES | ||
615 | + Overflow would have occurred. | ||
616 | + | ||
617 | + @retval EFI_INVALID_PARAMETER | ||
618 | + One of the passed parameters was invalid. | ||
619 | +**/ | ||
620 | +EFI_STATUS | ||
621 | +SanitizePrimaryHeaderGptEventSize ( | ||
622 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
623 | + IN UINTN NumberOfPartition, | ||
624 | + OUT UINT32 *EventSize | ||
625 | + ); | ||
626 | + | ||
627 | +#endif // DXE_TPM2_MEASURE_BOOT_LIB_SANITATION_ | ||
628 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c | ||
629 | new file mode 100644 | ||
630 | index 0000000000..3eb9763e3c | ||
631 | --- /dev/null | ||
632 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTest.c | ||
633 | @@ -0,0 +1,303 @@ | ||
634 | +/** @file | ||
635 | + This file includes the unit test cases for the DxeTpm2MeasureBootLibSanitizationTest.c. | ||
636 | + | ||
637 | + Copyright (c) Microsoft Corporation.<BR> | ||
638 | + SPDX-License-Identifier: BSD-2-Clause-Patent | ||
639 | +**/ | ||
640 | + | ||
641 | +#include <Uefi.h> | ||
642 | +#include <Library/UefiLib.h> | ||
643 | +#include <Library/DebugLib.h> | ||
644 | +#include <Library/UnitTestLib.h> | ||
645 | +#include <Protocol/BlockIo.h> | ||
646 | +#include <Library/MemoryAllocationLib.h> | ||
647 | +#include <Library/BaseMemoryLib.h> | ||
648 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
649 | +#include <Protocol/Tcg2Protocol.h> | ||
650 | + | ||
651 | +#include "../DxeTpm2MeasureBootLibSanitization.h" | ||
652 | + | ||
653 | +#define UNIT_TEST_NAME "DxeTpm2MeasureBootLibSanitizationTest" | ||
654 | +#define UNIT_TEST_VERSION "1.0" | ||
655 | + | ||
656 | +#define DEFAULT_PRIMARY_TABLE_HEADER_REVISION 0x00010000 | ||
657 | +#define DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES 1 | ||
658 | +#define DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY 128 | ||
659 | + | ||
660 | +/** | ||
661 | + This function tests the SanitizeEfiPartitionTableHeader function. | ||
662 | + It's intent is to test that a malicious EFI_PARTITION_TABLE_HEADER | ||
663 | + structure will not cause undefined or unexpected behavior. | ||
664 | + | ||
665 | + In general the TPM should still be able to measure the data, but | ||
666 | + be the header should be sanitized to prevent any unexpected behavior. | ||
667 | + | ||
668 | + @param[in] Context The unit test context. | ||
669 | + | ||
670 | + @retval UNIT_TEST_PASSED The test passed. | ||
671 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
672 | +**/ | ||
673 | +UNIT_TEST_STATUS | ||
674 | +EFIAPI | ||
675 | +TestSanitizeEfiPartitionTableHeader ( | ||
676 | + IN UNIT_TEST_CONTEXT Context | ||
677 | + ) | ||
678 | +{ | ||
679 | + EFI_STATUS Status; | ||
680 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
681 | + EFI_BLOCK_IO_PROTOCOL BlockIo; | ||
682 | + EFI_BLOCK_IO_MEDIA BlockMedia; | ||
683 | + | ||
684 | + // Generate EFI_BLOCK_IO_MEDIA test data | ||
685 | + BlockMedia.MediaId = 1; | ||
686 | + BlockMedia.RemovableMedia = FALSE; | ||
687 | + BlockMedia.MediaPresent = TRUE; | ||
688 | + BlockMedia.LogicalPartition = FALSE; | ||
689 | + BlockMedia.ReadOnly = FALSE; | ||
690 | + BlockMedia.WriteCaching = FALSE; | ||
691 | + BlockMedia.BlockSize = 512; | ||
692 | + BlockMedia.IoAlign = 1; | ||
693 | + BlockMedia.LastBlock = 0; | ||
694 | + | ||
695 | + // Generate EFI_BLOCK_IO_PROTOCOL test data | ||
696 | + BlockIo.Revision = 1; | ||
697 | + BlockIo.Media = &BlockMedia; | ||
698 | + BlockIo.Reset = NULL; | ||
699 | + BlockIo.ReadBlocks = NULL; | ||
700 | + BlockIo.WriteBlocks = NULL; | ||
701 | + BlockIo.FlushBlocks = NULL; | ||
702 | + | ||
703 | + // Geneate EFI_PARTITION_TABLE_HEADER test data | ||
704 | + PrimaryHeader.Header.Signature = EFI_PTAB_HEADER_ID; | ||
705 | + PrimaryHeader.Header.Revision = DEFAULT_PRIMARY_TABLE_HEADER_REVISION; | ||
706 | + PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER); | ||
707 | + PrimaryHeader.MyLBA = 1; | ||
708 | + PrimaryHeader.AlternateLBA = 2; | ||
709 | + PrimaryHeader.FirstUsableLBA = 3; | ||
710 | + PrimaryHeader.LastUsableLBA = 4; | ||
711 | + PrimaryHeader.PartitionEntryLBA = 5; | ||
712 | + PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES; | ||
713 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
714 | + PrimaryHeader.PartitionEntryArrayCRC32 = 0; // Purposely invalid | ||
715 | + | ||
716 | + // Calculate the CRC32 of the PrimaryHeader | ||
717 | + PrimaryHeader.Header.CRC32 = CalculateCrc32 ((UINT8 *)&PrimaryHeader, PrimaryHeader.Header.HeaderSize); | ||
718 | + | ||
719 | + // Test that a normal PrimaryHeader passes validation | ||
720 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
721 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
722 | + | ||
723 | + // Test that when number of partition entries is 0, the function returns EFI_DEVICE_ERROR | ||
724 | + // Should print "Invalid Partition Table Header NumberOfPartitionEntries!"" | ||
725 | + PrimaryHeader.NumberOfPartitionEntries = 0; | ||
726 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
727 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
728 | + PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
729 | + | ||
730 | + // Test that when the header size is too small, the function returns EFI_DEVICE_ERROR | ||
731 | + // Should print "Invalid Partition Table Header Size!" | ||
732 | + PrimaryHeader.Header.HeaderSize = 0; | ||
733 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
734 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
735 | + PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER); | ||
736 | + | ||
737 | + // Test that when the SizeOfPartitionEntry is too small, the function returns EFI_DEVICE_ERROR | ||
738 | + // should print: "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!" | ||
739 | + PrimaryHeader.SizeOfPartitionEntry = 1; | ||
740 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
741 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
742 | + | ||
743 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
744 | + | ||
745 | + return UNIT_TEST_PASSED; | ||
746 | +} | ||
747 | + | ||
748 | +/** | ||
749 | + This function tests the SanitizePrimaryHeaderAllocationSize function. | ||
750 | + It's intent is to test that the untrusted input from a EFI_PARTITION_TABLE_HEADER | ||
751 | + structure will not cause an overflow when calculating the allocation size. | ||
752 | + | ||
753 | + @param[in] Context The unit test context. | ||
754 | + | ||
755 | + @retval UNIT_TEST_PASSED The test passed. | ||
756 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
757 | +**/ | ||
758 | +UNIT_TEST_STATUS | ||
759 | +EFIAPI | ||
760 | +TestSanitizePrimaryHeaderAllocationSize ( | ||
761 | + IN UNIT_TEST_CONTEXT Context | ||
762 | + ) | ||
763 | +{ | ||
764 | + UINT32 AllocationSize; | ||
765 | + | ||
766 | + EFI_STATUS Status; | ||
767 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
768 | + | ||
769 | + // Test that a normal PrimaryHeader passes validation | ||
770 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
771 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
772 | + | ||
773 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
774 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
775 | + | ||
776 | + // Test that the allocation size is correct compared to the existing logic | ||
777 | + UT_ASSERT_EQUAL (AllocationSize, PrimaryHeader.NumberOfPartitionEntries * PrimaryHeader.SizeOfPartitionEntry); | ||
778 | + | ||
779 | + // Test that an overflow is detected | ||
780 | + PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32; | ||
781 | + PrimaryHeader.SizeOfPartitionEntry = 5; | ||
782 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
783 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
784 | + | ||
785 | + // Test the inverse | ||
786 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
787 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
788 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
789 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
790 | + | ||
791 | + // Test the worst case scenario | ||
792 | + PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32; | ||
793 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
794 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
795 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
796 | + | ||
797 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
798 | + | ||
799 | + return UNIT_TEST_PASSED; | ||
800 | +} | ||
801 | + | ||
802 | +/** | ||
803 | + This function tests the SanitizePrimaryHeaderGptEventSize function. | ||
804 | + It's intent is to test that the untrusted input from a EFI_GPT_DATA structure | ||
805 | + will not cause an overflow when calculating the event size. | ||
806 | + | ||
807 | + @param[in] Context The unit test context. | ||
808 | + | ||
809 | + @retval UNIT_TEST_PASSED The test passed. | ||
810 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
811 | +**/ | ||
812 | +UNIT_TEST_STATUS | ||
813 | +EFIAPI | ||
814 | +TestSanitizePrimaryHeaderGptEventSize ( | ||
815 | + IN UNIT_TEST_CONTEXT Context | ||
816 | + ) | ||
817 | +{ | ||
818 | + UINT32 EventSize; | ||
819 | + UINT32 ExistingLogicEventSize; | ||
820 | + EFI_STATUS Status; | ||
821 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
822 | + UINTN NumberOfPartition; | ||
823 | + EFI_GPT_DATA *GptData; | ||
824 | + EFI_TCG2_EVENT *Tcg2Event; | ||
825 | + | ||
826 | + Tcg2Event = NULL; | ||
827 | + GptData = NULL; | ||
828 | + | ||
829 | + // Test that a normal PrimaryHeader passes validation | ||
830 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
831 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
832 | + | ||
833 | + // set the number of partitions | ||
834 | + NumberOfPartition = 13; | ||
835 | + | ||
836 | + // that the primary event size is correct | ||
837 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize); | ||
838 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
839 | + | ||
840 | + // Calculate the existing logic event size | ||
841 | + ExistingLogicEventSize = (UINT32)(OFFSET_OF (EFI_TCG2_EVENT, Event) + OFFSET_OF (EFI_GPT_DATA, Partitions) | ||
842 | + + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry); | ||
843 | + | ||
844 | + // Check that the event size is correct | ||
845 | + UT_ASSERT_EQUAL (EventSize, ExistingLogicEventSize); | ||
846 | + | ||
847 | + // Tests that the primary event size may not overflow | ||
848 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, MAX_UINT32, &EventSize); | ||
849 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
850 | + | ||
851 | + // Test that the size of partition entries may not overflow | ||
852 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
853 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize); | ||
854 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
855 | + | ||
856 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
857 | + | ||
858 | + return UNIT_TEST_PASSED; | ||
859 | +} | ||
860 | + | ||
861 | +// *--------------------------------------------------------------------* | ||
862 | +// * Unit Test Code Main Function | ||
863 | +// *--------------------------------------------------------------------* | ||
864 | + | ||
865 | +/** | ||
866 | + This function acts as the entry point for the unit tests. | ||
867 | + | ||
868 | + @retval UNIT_TEST_PASSED The test passed. | ||
869 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
870 | + @retval others The test failed. | ||
871 | +**/ | ||
872 | +EFI_STATUS | ||
873 | +EFIAPI | ||
874 | +UefiTestMain ( | ||
875 | + VOID | ||
876 | + ) | ||
877 | +{ | ||
878 | + EFI_STATUS Status; | ||
879 | + UNIT_TEST_FRAMEWORK_HANDLE Framework; | ||
880 | + UNIT_TEST_SUITE_HANDLE Tcg2MeasureBootLibValidationTestSuite; | ||
881 | + | ||
882 | + Framework = NULL; | ||
883 | + | ||
884 | + DEBUG ((DEBUG_INFO, "%a: TestMain() - Start\n", UNIT_TEST_NAME)); | ||
885 | + | ||
886 | + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); | ||
887 | + if (EFI_ERROR (Status)) { | ||
888 | + DEBUG ((DEBUG_ERROR, "%a: Failed in InitUnitTestFramework. Status = %r\n", UNIT_TEST_NAME, Status)); | ||
889 | + goto EXIT; | ||
890 | + } | ||
891 | + | ||
892 | + Status = CreateUnitTestSuite (&Tcg2MeasureBootLibValidationTestSuite, Framework, "Tcg2MeasureBootLibValidationTestSuite", "Common.Tcg2MeasureBootLibValidation", NULL, NULL); | ||
893 | + if (EFI_ERROR (Status)) { | ||
894 | + DEBUG ((DEBUG_ERROR, "%s: Failed in CreateUnitTestSuite for Tcg2MeasureBootLibValidationTestSuite\n", UNIT_TEST_NAME)); | ||
895 | + Status = EFI_OUT_OF_RESOURCES; | ||
896 | + goto EXIT; | ||
897 | + } | ||
898 | + | ||
899 | + // -----------Suite---------------------------------Description----------------------------Class----------------------------------Test Function------------------------Pre---Clean-Context | ||
900 | + AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Validating EFI Partition Table", "Common.Tcg2MeasureBootLibValidation", TestSanitizeEfiPartitionTableHeader, NULL, NULL, NULL); | ||
901 | + AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Primary header gpt event checks for overflow", "Common.Tcg2MeasureBootLibValidation", TestSanitizePrimaryHeaderAllocationSize, NULL, NULL, NULL); | ||
902 | + AddTestCase (Tcg2MeasureBootLibValidationTestSuite, "Tests Primary header allocation size checks for overflow", "Common.Tcg2MeasureBootLibValidation", TestSanitizePrimaryHeaderGptEventSize, NULL, NULL, NULL); | ||
903 | + | ||
904 | + Status = RunAllTestSuites (Framework); | ||
905 | + | ||
906 | +EXIT: | ||
907 | + if (Framework != NULL) { | ||
908 | + FreeUnitTestFramework (Framework); | ||
909 | + } | ||
910 | + | ||
911 | + DEBUG ((DEBUG_INFO, "%a: TestMain() - End\n", UNIT_TEST_NAME)); | ||
912 | + return Status; | ||
913 | +} | ||
914 | + | ||
915 | +/// | ||
916 | +/// Avoid ECC error for function name that starts with lower case letter | ||
917 | +/// | ||
918 | +#define DxeTpm2MeasureBootLibUnitTestMain main | ||
919 | + | ||
920 | +/** | ||
921 | + Standard POSIX C entry point for host based unit test execution. | ||
922 | + | ||
923 | + @param[in] Argc Number of arguments | ||
924 | + @param[in] Argv Array of pointers to arguments | ||
925 | + | ||
926 | + @retval 0 Success | ||
927 | + @retval other Error | ||
928 | +**/ | ||
929 | +INT32 | ||
930 | +DxeTpm2MeasureBootLibUnitTestMain ( | ||
931 | + IN INT32 Argc, | ||
932 | + IN CHAR8 *Argv[] | ||
933 | + ) | ||
934 | +{ | ||
935 | + return (INT32)UefiTestMain (); | ||
936 | +} | ||
937 | diff --git a/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf | ||
938 | new file mode 100644 | ||
939 | index 0000000000..2999aa2a44 | ||
940 | --- /dev/null | ||
941 | +++ b/SecurityPkg/Library/DxeTpm2MeasureBootLib/InternalUnitTest/DxeTpm2MeasureBootLibSanitizationTestHost.inf | ||
942 | @@ -0,0 +1,28 @@ | ||
943 | +## @file | ||
944 | +# This file builds the unit tests for DxeTpm2MeasureBootLib | ||
945 | +# | ||
946 | +# Copyright (C) Microsoft Corporation.<BR> | ||
947 | +# SPDX-License-Identifier: BSD-2-Clause-Patent | ||
948 | +## | ||
949 | + | ||
950 | +[Defines] | ||
951 | + INF_VERSION = 0x00010006 | ||
952 | + BASE_NAME = DxeTpm2MeasuredBootLibTest | ||
953 | + FILE_GUID = 144d757f-d423-484e-9309-a23695fad5bd | ||
954 | + MODULE_TYPE = HOST_APPLICATION | ||
955 | + VERSION_STRING = 1.0 | ||
956 | + ENTRY_POINT = main | ||
957 | + | ||
958 | +[Sources] | ||
959 | + DxeTpm2MeasureBootLibSanitizationTest.c | ||
960 | + ../DxeTpm2MeasureBootLibSanitization.c | ||
961 | + | ||
962 | +[Packages] | ||
963 | + MdePkg/MdePkg.dec | ||
964 | + | ||
965 | +[LibraryClasses] | ||
966 | + BaseLib | ||
967 | + DebugLib | ||
968 | + UnitTestLib | ||
969 | + PrintLib | ||
970 | + SafeIntLib | ||
971 | diff --git a/SecurityPkg/SecurityPkg.ci.yaml b/SecurityPkg/SecurityPkg.ci.yaml | ||
972 | index 7912142398..da811fdf93 100644 | ||
973 | --- a/SecurityPkg/SecurityPkg.ci.yaml | ||
974 | +++ b/SecurityPkg/SecurityPkg.ci.yaml | ||
975 | @@ -15,6 +15,7 @@ | ||
976 | ## "<ErrorID>", "<KeyWord>" | ||
977 | ## ] | ||
978 | "ExceptionList": [ | ||
979 | + "8001", "DxeTpm2MeasureBootLibUnitTestMain", | ||
980 | ], | ||
981 | ## Both file path and directory path are accepted. | ||
982 | "IgnoreFiles": [ | ||
983 | -- | ||
984 | 2.40.0 | ||
985 | |||
diff --git a/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0002.patch b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0002.patch new file mode 100644 index 0000000000..6c20cc305e --- /dev/null +++ b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0002.patch | |||
@@ -0,0 +1,889 @@ | |||
1 | From 4776a1b39ee08fc45c70c1eab5a0195f325000d3 Mon Sep 17 00:00:00 2001 | ||
2 | From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com> | ||
3 | Date: Fri, 12 Jan 2024 02:16:02 +0800 | ||
4 | Subject: [PATCH] SecurityPkg: DxeTpmMeasureBootLib: SECURITY PATCH 4117 - CVE | ||
5 | 2022-36763 | ||
6 | |||
7 | This commit contains the patch files and tests for DxeTpmMeasureBootLib | ||
8 | CVE 2022-36763. | ||
9 | |||
10 | Cc: Jiewen Yao <jiewen.yao@intel.com> | ||
11 | |||
12 | Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com> | ||
13 | Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> | ||
14 | |||
15 | CVE: CVE-2022-36763 | ||
16 | |||
17 | Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/4776a1b39ee08fc45c70c1eab5a0195f325000d3] | ||
18 | |||
19 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
20 | --- | ||
21 | .../DxeTpmMeasureBootLib.c | 40 ++- | ||
22 | .../DxeTpmMeasureBootLib.inf | 4 +- | ||
23 | .../DxeTpmMeasureBootLibSanitization.c | 241 ++++++++++++++ | ||
24 | .../DxeTpmMeasureBootLibSanitization.h | 114 +++++++ | ||
25 | .../DxeTpmMeasureBootLibSanitizationTest.c | 301 ++++++++++++++++++ | ||
26 | ...eTpmMeasureBootLibSanitizationTestHost.inf | 28 ++ | ||
27 | SecurityPkg/SecurityPkg.ci.yaml | 1 + | ||
28 | 7 files changed, 715 insertions(+), 14 deletions(-) | ||
29 | create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c | ||
30 | create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h | ||
31 | create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c | ||
32 | create mode 100644 SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf | ||
33 | |||
34 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c | ||
35 | index 220393dd2b..669ab19134 100644 | ||
36 | --- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c | ||
37 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.c | ||
38 | @@ -18,6 +18,8 @@ | ||
39 | Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | ||
40 | SPDX-License-Identifier: BSD-2-Clause-Patent | ||
41 | |||
42 | +Copyright (c) Microsoft Corporation.<BR> | ||
43 | +SPDX-License-Identifier: BSD-2-Clause-Patent | ||
44 | **/ | ||
45 | |||
46 | #include <PiDxe.h> | ||
47 | @@ -40,6 +42,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent | ||
48 | #include <Library/SecurityManagementLib.h> | ||
49 | #include <Library/HobLib.h> | ||
50 | |||
51 | +#include "DxeTpmMeasureBootLibSanitization.h" | ||
52 | + | ||
53 | // | ||
54 | // Flag to check GPT partition. It only need be measured once. | ||
55 | // | ||
56 | @@ -136,6 +140,9 @@ TcgMeasureGptTable ( | ||
57 | UINT32 EventSize; | ||
58 | UINT32 EventNumber; | ||
59 | EFI_PHYSICAL_ADDRESS EventLogLastEntry; | ||
60 | + UINT32 AllocSize; | ||
61 | + | ||
62 | + GptData = NULL; | ||
63 | |||
64 | if (mMeasureGptCount > 0) { | ||
65 | return EFI_SUCCESS; | ||
66 | @@ -166,8 +173,8 @@ TcgMeasureGptTable ( | ||
67 | BlockIo->Media->BlockSize, | ||
68 | (UINT8 *)PrimaryHeader | ||
69 | ); | ||
70 | - if (EFI_ERROR (Status)) { | ||
71 | - DEBUG ((DEBUG_ERROR, "Failed to Read Partition Table Header!\n")); | ||
72 | + if (EFI_ERROR (Status) || EFI_ERROR (SanitizeEfiPartitionTableHeader (PrimaryHeader, BlockIo))) { | ||
73 | + DEBUG ((DEBUG_ERROR, "Failed to read Partition Table Header or invalid Partition Table Header!\n")); | ||
74 | FreePool (PrimaryHeader); | ||
75 | return EFI_DEVICE_ERROR; | ||
76 | } | ||
77 | @@ -175,7 +182,13 @@ TcgMeasureGptTable ( | ||
78 | // | ||
79 | // Read the partition entry. | ||
80 | // | ||
81 | - EntryPtr = (UINT8 *)AllocatePool (PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry); | ||
82 | + Status = SanitizePrimaryHeaderAllocationSize (PrimaryHeader, &AllocSize); | ||
83 | + if (EFI_ERROR (Status)) { | ||
84 | + FreePool (PrimaryHeader); | ||
85 | + return EFI_DEVICE_ERROR; | ||
86 | + } | ||
87 | + | ||
88 | + EntryPtr = (UINT8 *)AllocatePool (AllocSize); | ||
89 | if (EntryPtr == NULL) { | ||
90 | FreePool (PrimaryHeader); | ||
91 | return EFI_OUT_OF_RESOURCES; | ||
92 | @@ -185,7 +198,7 @@ TcgMeasureGptTable ( | ||
93 | DiskIo, | ||
94 | BlockIo->Media->MediaId, | ||
95 | MultU64x32 (PrimaryHeader->PartitionEntryLBA, BlockIo->Media->BlockSize), | ||
96 | - PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry, | ||
97 | + AllocSize, | ||
98 | EntryPtr | ||
99 | ); | ||
100 | if (EFI_ERROR (Status)) { | ||
101 | @@ -210,9 +223,8 @@ TcgMeasureGptTable ( | ||
102 | // | ||
103 | // Prepare Data for Measurement | ||
104 | // | ||
105 | - EventSize = (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) | ||
106 | - + NumberOfPartition * PrimaryHeader->SizeOfPartitionEntry); | ||
107 | - TcgEvent = (TCG_PCR_EVENT *)AllocateZeroPool (EventSize + sizeof (TCG_PCR_EVENT_HDR)); | ||
108 | + Status = SanitizePrimaryHeaderGptEventSize (PrimaryHeader, NumberOfPartition, &EventSize); | ||
109 | + TcgEvent = (TCG_PCR_EVENT *)AllocateZeroPool (EventSize); | ||
110 | if (TcgEvent == NULL) { | ||
111 | FreePool (PrimaryHeader); | ||
112 | FreePool (EntryPtr); | ||
113 | @@ -221,7 +233,7 @@ TcgMeasureGptTable ( | ||
114 | |||
115 | TcgEvent->PCRIndex = 5; | ||
116 | TcgEvent->EventType = EV_EFI_GPT_EVENT; | ||
117 | - TcgEvent->EventSize = EventSize; | ||
118 | + TcgEvent->EventSize = EventSize - sizeof (TCG_PCR_EVENT_HDR); | ||
119 | GptData = (EFI_GPT_DATA *)TcgEvent->Event; | ||
120 | |||
121 | // | ||
122 | @@ -361,11 +373,13 @@ TcgMeasurePeImage ( | ||
123 | TcgEvent->PCRIndex = 2; | ||
124 | break; | ||
125 | default: | ||
126 | - DEBUG (( | ||
127 | - DEBUG_ERROR, | ||
128 | - "TcgMeasurePeImage: Unknown subsystem type %d", | ||
129 | - ImageType | ||
130 | - )); | ||
131 | + DEBUG ( | ||
132 | + ( | ||
133 | + DEBUG_ERROR, | ||
134 | + "TcgMeasurePeImage: Unknown subsystem type %d", | ||
135 | + ImageType | ||
136 | + ) | ||
137 | + ); | ||
138 | goto Finish; | ||
139 | } | ||
140 | |||
141 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf | ||
142 | index ebab6f7c1e..414c654d15 100644 | ||
143 | --- a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf | ||
144 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLib.inf | ||
145 | @@ -32,6 +32,8 @@ | ||
146 | |||
147 | [Sources] | ||
148 | DxeTpmMeasureBootLib.c | ||
149 | + DxeTpmMeasureBootLibSanitization.c | ||
150 | + DxeTpmMeasureBootLibSanitization.h | ||
151 | |||
152 | [Packages] | ||
153 | MdePkg/MdePkg.dec | ||
154 | @@ -41,6 +43,7 @@ | ||
155 | |||
156 | [LibraryClasses] | ||
157 | BaseMemoryLib | ||
158 | + SafeIntLib | ||
159 | DebugLib | ||
160 | MemoryAllocationLib | ||
161 | DevicePathLib | ||
162 | @@ -59,4 +62,3 @@ | ||
163 | gEfiFirmwareVolumeBlockProtocolGuid ## SOMETIMES_CONSUMES | ||
164 | gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES | ||
165 | gEfiDiskIoProtocolGuid ## SOMETIMES_CONSUMES | ||
166 | - | ||
167 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c | ||
168 | new file mode 100644 | ||
169 | index 0000000000..a3fa46f5e6 | ||
170 | --- /dev/null | ||
171 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.c | ||
172 | @@ -0,0 +1,241 @@ | ||
173 | +/** @file | ||
174 | + The library instance provides security service of TPM2 measure boot and | ||
175 | + Confidential Computing (CC) measure boot. | ||
176 | + | ||
177 | + Caution: This file requires additional review when modified. | ||
178 | + This library will have external input - PE/COFF image and GPT partition. | ||
179 | + This external input must be validated carefully to avoid security issue like | ||
180 | + buffer overflow, integer overflow. | ||
181 | + | ||
182 | + This file will pull out the validation logic from the following functions, in an | ||
183 | + attempt to validate the untrusted input in the form of unit tests | ||
184 | + | ||
185 | + These are those functions: | ||
186 | + | ||
187 | + DxeTpmMeasureBootLibImageRead() function will make sure the PE/COFF image content | ||
188 | + read is within the image buffer. | ||
189 | + | ||
190 | + Tcg2MeasureGptTable() function will receive untrusted GPT partition table, and parse | ||
191 | + partition data carefully. | ||
192 | + | ||
193 | + Copyright (c) Microsoft Corporation.<BR> | ||
194 | + SPDX-License-Identifier: BSD-2-Clause-Patent | ||
195 | +**/ | ||
196 | +#include <Uefi.h> | ||
197 | +#include <Uefi/UefiSpec.h> | ||
198 | +#include <Library/SafeIntLib.h> | ||
199 | +#include <Library/UefiLib.h> | ||
200 | +#include <Library/DebugLib.h> | ||
201 | +#include <Library/BaseLib.h> | ||
202 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
203 | +#include <Protocol/BlockIo.h> | ||
204 | +#include <Library/MemoryAllocationLib.h> | ||
205 | + | ||
206 | +#include "DxeTpmMeasureBootLibSanitization.h" | ||
207 | + | ||
208 | +#define GPT_HEADER_REVISION_V1 0x00010000 | ||
209 | + | ||
210 | +/** | ||
211 | + This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse | ||
212 | + However this function will not attempt to verify the validity of the GPT partition | ||
213 | + It will check the following: | ||
214 | + - Signature | ||
215 | + - Revision | ||
216 | + - AlternateLBA | ||
217 | + - FirstUsableLBA | ||
218 | + - LastUsableLBA | ||
219 | + - PartitionEntryLBA | ||
220 | + - NumberOfPartitionEntries | ||
221 | + - SizeOfPartitionEntry | ||
222 | + - BlockIo | ||
223 | + | ||
224 | + @param[in] PrimaryHeader | ||
225 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
226 | + | ||
227 | + @param[in] BlockIo | ||
228 | + Pointer to the EFI_BLOCK_IO_PROTOCOL structure. | ||
229 | + | ||
230 | + @retval EFI_SUCCESS | ||
231 | + The EFI_PARTITION_TABLE_HEADER structure is valid. | ||
232 | + | ||
233 | + @retval EFI_INVALID_PARAMETER | ||
234 | + The EFI_PARTITION_TABLE_HEADER structure is invalid. | ||
235 | +**/ | ||
236 | +EFI_STATUS | ||
237 | +EFIAPI | ||
238 | +SanitizeEfiPartitionTableHeader ( | ||
239 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
240 | + IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo | ||
241 | + ) | ||
242 | +{ | ||
243 | + // Verify that the input parameters are safe to use | ||
244 | + if (PrimaryHeader == NULL) { | ||
245 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n")); | ||
246 | + return EFI_INVALID_PARAMETER; | ||
247 | + } | ||
248 | + | ||
249 | + if ((BlockIo == NULL) || (BlockIo->Media == NULL)) { | ||
250 | + DEBUG ((DEBUG_ERROR, "Invalid BlockIo!\n")); | ||
251 | + return EFI_INVALID_PARAMETER; | ||
252 | + } | ||
253 | + | ||
254 | + // The signature must be EFI_PTAB_HEADER_ID ("EFI PART" in ASCII) | ||
255 | + if (PrimaryHeader->Header.Signature != EFI_PTAB_HEADER_ID) { | ||
256 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header!\n")); | ||
257 | + return EFI_DEVICE_ERROR; | ||
258 | + } | ||
259 | + | ||
260 | + // The version must be GPT_HEADER_REVISION_V1 (0x00010000) | ||
261 | + if (PrimaryHeader->Header.Revision != GPT_HEADER_REVISION_V1) { | ||
262 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header Revision!\n")); | ||
263 | + return EFI_DEVICE_ERROR; | ||
264 | + } | ||
265 | + | ||
266 | + // The HeaderSize must be greater than or equal to 92 and must be less than or equal to the logical block size | ||
267 | + if ((PrimaryHeader->Header.HeaderSize < sizeof (EFI_PARTITION_TABLE_HEADER)) || (PrimaryHeader->Header.HeaderSize > BlockIo->Media->BlockSize)) { | ||
268 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header HeaderSize!\n")); | ||
269 | + return EFI_DEVICE_ERROR; | ||
270 | + } | ||
271 | + | ||
272 | + // check that the PartitionEntryLBA greater than the Max LBA | ||
273 | + // This will be used later for multiplication | ||
274 | + if (PrimaryHeader->PartitionEntryLBA > DivU64x32 (MAX_UINT64, BlockIo->Media->BlockSize)) { | ||
275 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header PartitionEntryLBA!\n")); | ||
276 | + return EFI_DEVICE_ERROR; | ||
277 | + } | ||
278 | + | ||
279 | + // Check that the number of partition entries is greater than zero | ||
280 | + if (PrimaryHeader->NumberOfPartitionEntries == 0) { | ||
281 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n")); | ||
282 | + return EFI_DEVICE_ERROR; | ||
283 | + } | ||
284 | + | ||
285 | + // SizeOfPartitionEntry must be 128, 256, 512... improper size may lead to accessing uninitialized memory | ||
286 | + if ((PrimaryHeader->SizeOfPartitionEntry < 128) || ((PrimaryHeader->SizeOfPartitionEntry & (PrimaryHeader->SizeOfPartitionEntry - 1)) != 0)) { | ||
287 | + DEBUG ((DEBUG_ERROR, "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!\n")); | ||
288 | + return EFI_DEVICE_ERROR; | ||
289 | + } | ||
290 | + | ||
291 | + // This check is to prevent overflow when calculating the allocation size for the partition entries | ||
292 | + // This check will be used later for multiplication | ||
293 | + if (PrimaryHeader->NumberOfPartitionEntries > DivU64x32 (MAX_UINT64, PrimaryHeader->SizeOfPartitionEntry)) { | ||
294 | + DEBUG ((DEBUG_ERROR, "Invalid Partition Table Header NumberOfPartitionEntries!\n")); | ||
295 | + return EFI_DEVICE_ERROR; | ||
296 | + } | ||
297 | + | ||
298 | + return EFI_SUCCESS; | ||
299 | +} | ||
300 | + | ||
301 | +/** | ||
302 | + This function will validate that the allocation size from the primary header is sane | ||
303 | + It will check the following: | ||
304 | + - AllocationSize does not overflow | ||
305 | + | ||
306 | + @param[in] PrimaryHeader | ||
307 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
308 | + | ||
309 | + @param[out] AllocationSize | ||
310 | + Pointer to the allocation size. | ||
311 | + | ||
312 | + @retval EFI_SUCCESS | ||
313 | + The allocation size is valid. | ||
314 | + | ||
315 | + @retval EFI_OUT_OF_RESOURCES | ||
316 | + The allocation size is invalid. | ||
317 | +**/ | ||
318 | +EFI_STATUS | ||
319 | +EFIAPI | ||
320 | +SanitizePrimaryHeaderAllocationSize ( | ||
321 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
322 | + OUT UINT32 *AllocationSize | ||
323 | + ) | ||
324 | +{ | ||
325 | + EFI_STATUS Status; | ||
326 | + | ||
327 | + if (PrimaryHeader == NULL) { | ||
328 | + return EFI_INVALID_PARAMETER; | ||
329 | + } | ||
330 | + | ||
331 | + if (AllocationSize == NULL) { | ||
332 | + return EFI_INVALID_PARAMETER; | ||
333 | + } | ||
334 | + | ||
335 | + // Replacing logic: | ||
336 | + // PrimaryHeader->NumberOfPartitionEntries * PrimaryHeader->SizeOfPartitionEntry; | ||
337 | + Status = SafeUint32Mult (PrimaryHeader->NumberOfPartitionEntries, PrimaryHeader->SizeOfPartitionEntry, AllocationSize); | ||
338 | + if (EFI_ERROR (Status)) { | ||
339 | + DEBUG ((DEBUG_ERROR, "Allocation Size would have overflowed!\n")); | ||
340 | + return EFI_BAD_BUFFER_SIZE; | ||
341 | + } | ||
342 | + | ||
343 | + return EFI_SUCCESS; | ||
344 | +} | ||
345 | + | ||
346 | +/** | ||
347 | + This function will validate that the Gpt Event Size calculated from the primary header is sane | ||
348 | + It will check the following: | ||
349 | + - EventSize does not overflow | ||
350 | + | ||
351 | + Important: This function includes the entire length of the allocated space, including the | ||
352 | + TCG_PCR_EVENT_HDR. When hashing the buffer allocated with this size, the caller must subtract | ||
353 | + the size of the TCG_PCR_EVENT_HDR from the size of the buffer before hashing. | ||
354 | + | ||
355 | + @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
356 | + @param[in] NumberOfPartition - Number of partitions. | ||
357 | + @param[out] EventSize - Pointer to the event size. | ||
358 | + | ||
359 | + @retval EFI_SUCCESS | ||
360 | + The event size is valid. | ||
361 | + | ||
362 | + @retval EFI_OUT_OF_RESOURCES | ||
363 | + Overflow would have occurred. | ||
364 | + | ||
365 | + @retval EFI_INVALID_PARAMETER | ||
366 | + One of the passed parameters was invalid. | ||
367 | +**/ | ||
368 | +EFI_STATUS | ||
369 | +SanitizePrimaryHeaderGptEventSize ( | ||
370 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
371 | + IN UINTN NumberOfPartition, | ||
372 | + OUT UINT32 *EventSize | ||
373 | + ) | ||
374 | +{ | ||
375 | + EFI_STATUS Status; | ||
376 | + UINT32 SafeNumberOfPartitions; | ||
377 | + | ||
378 | + if (PrimaryHeader == NULL) { | ||
379 | + return EFI_INVALID_PARAMETER; | ||
380 | + } | ||
381 | + | ||
382 | + if (EventSize == NULL) { | ||
383 | + return EFI_INVALID_PARAMETER; | ||
384 | + } | ||
385 | + | ||
386 | + // We shouldn't even attempt to perform the multiplication if the number of partitions is greater than the maximum value of UINT32 | ||
387 | + Status = SafeUintnToUint32 (NumberOfPartition, &SafeNumberOfPartitions); | ||
388 | + if (EFI_ERROR (Status)) { | ||
389 | + DEBUG ((DEBUG_ERROR, "NumberOfPartition would have overflowed!\n")); | ||
390 | + return EFI_INVALID_PARAMETER; | ||
391 | + } | ||
392 | + | ||
393 | + // Replacing logic: | ||
394 | + // (UINT32)(sizeof (EFI_GPT_DATA) - sizeof (GptData->Partitions) + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry + sizeof (TCG_PCR_EVENT_HDR)); | ||
395 | + Status = SafeUint32Mult (SafeNumberOfPartitions, PrimaryHeader->SizeOfPartitionEntry, EventSize); | ||
396 | + if (EFI_ERROR (Status)) { | ||
397 | + DEBUG ((DEBUG_ERROR, "Event Size would have overflowed!\n")); | ||
398 | + return EFI_BAD_BUFFER_SIZE; | ||
399 | + } | ||
400 | + | ||
401 | + Status = SafeUint32Add ( | ||
402 | + sizeof (TCG_PCR_EVENT_HDR) + | ||
403 | + OFFSET_OF (EFI_GPT_DATA, Partitions), | ||
404 | + *EventSize, | ||
405 | + EventSize | ||
406 | + ); | ||
407 | + if (EFI_ERROR (Status)) { | ||
408 | + DEBUG ((DEBUG_ERROR, "Event Size would have overflowed because of GPTData!\n")); | ||
409 | + return EFI_BAD_BUFFER_SIZE; | ||
410 | + } | ||
411 | + | ||
412 | + return EFI_SUCCESS; | ||
413 | +} | ||
414 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h | ||
415 | new file mode 100644 | ||
416 | index 0000000000..0d9d00c281 | ||
417 | --- /dev/null | ||
418 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/DxeTpmMeasureBootLibSanitization.h | ||
419 | @@ -0,0 +1,114 @@ | ||
420 | +/** @file | ||
421 | + This file includes the function prototypes for the sanitization functions. | ||
422 | + | ||
423 | + These are those functions: | ||
424 | + | ||
425 | + DxeTpmMeasureBootLibImageRead() function will make sure the PE/COFF image content | ||
426 | + read is within the image buffer. | ||
427 | + | ||
428 | + TcgMeasurePeImage() function will accept untrusted PE/COFF image and validate its | ||
429 | + data structure within this image buffer before use. | ||
430 | + | ||
431 | + TcgMeasureGptTable() function will receive untrusted GPT partition table, and parse | ||
432 | + partition data carefully. | ||
433 | + | ||
434 | + Copyright (c) Microsoft Corporation.<BR> | ||
435 | + SPDX-License-Identifier: BSD-2-Clause-Patent | ||
436 | + | ||
437 | +**/ | ||
438 | + | ||
439 | +#ifndef DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_ | ||
440 | +#define DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_ | ||
441 | + | ||
442 | +#include <Uefi.h> | ||
443 | +#include <Uefi/UefiSpec.h> | ||
444 | +#include <Protocol/BlockIo.h> | ||
445 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
446 | + | ||
447 | +/** | ||
448 | + This function will validate the EFI_PARTITION_TABLE_HEADER structure is safe to parse | ||
449 | + However this function will not attempt to verify the validity of the GPT partition | ||
450 | + It will check the following: | ||
451 | + - Signature | ||
452 | + - Revision | ||
453 | + - AlternateLBA | ||
454 | + - FirstUsableLBA | ||
455 | + - LastUsableLBA | ||
456 | + - PartitionEntryLBA | ||
457 | + - NumberOfPartitionEntries | ||
458 | + - SizeOfPartitionEntry | ||
459 | + - BlockIo | ||
460 | + | ||
461 | + @param[in] PrimaryHeader | ||
462 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
463 | + | ||
464 | + @param[in] BlockIo | ||
465 | + Pointer to the EFI_BLOCK_IO_PROTOCOL structure. | ||
466 | + | ||
467 | + @retval EFI_SUCCESS | ||
468 | + The EFI_PARTITION_TABLE_HEADER structure is valid. | ||
469 | + | ||
470 | + @retval EFI_INVALID_PARAMETER | ||
471 | + The EFI_PARTITION_TABLE_HEADER structure is invalid. | ||
472 | +**/ | ||
473 | +EFI_STATUS | ||
474 | +EFIAPI | ||
475 | +SanitizeEfiPartitionTableHeader ( | ||
476 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
477 | + IN CONST EFI_BLOCK_IO_PROTOCOL *BlockIo | ||
478 | + ); | ||
479 | + | ||
480 | +/** | ||
481 | + This function will validate that the allocation size from the primary header is sane | ||
482 | + It will check the following: | ||
483 | + - AllocationSize does not overflow | ||
484 | + | ||
485 | + @param[in] PrimaryHeader | ||
486 | + Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
487 | + | ||
488 | + @param[out] AllocationSize | ||
489 | + Pointer to the allocation size. | ||
490 | + | ||
491 | + @retval EFI_SUCCESS | ||
492 | + The allocation size is valid. | ||
493 | + | ||
494 | + @retval EFI_OUT_OF_RESOURCES | ||
495 | + The allocation size is invalid. | ||
496 | +**/ | ||
497 | +EFI_STATUS | ||
498 | +EFIAPI | ||
499 | +SanitizePrimaryHeaderAllocationSize ( | ||
500 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
501 | + OUT UINT32 *AllocationSize | ||
502 | + ); | ||
503 | + | ||
504 | +/** | ||
505 | + This function will validate that the Gpt Event Size calculated from the primary header is sane | ||
506 | + It will check the following: | ||
507 | + - EventSize does not overflow | ||
508 | + | ||
509 | + Important: This function includes the entire length of the allocated space, including the | ||
510 | + TCG_PCR_EVENT_HDR. When hashing the buffer allocated with this size, the caller must subtract | ||
511 | + the size of the TCG_PCR_EVENT_HDR from the size of the buffer before hashing. | ||
512 | + | ||
513 | + @param[in] PrimaryHeader - Pointer to the EFI_PARTITION_TABLE_HEADER structure. | ||
514 | + @param[in] NumberOfPartition - Number of partitions. | ||
515 | + @param[out] EventSize - Pointer to the event size. | ||
516 | + | ||
517 | + @retval EFI_SUCCESS | ||
518 | + The event size is valid. | ||
519 | + | ||
520 | + @retval EFI_OUT_OF_RESOURCES | ||
521 | + Overflow would have occurred. | ||
522 | + | ||
523 | + @retval EFI_INVALID_PARAMETER | ||
524 | + One of the passed parameters was invalid. | ||
525 | +**/ | ||
526 | +EFI_STATUS | ||
527 | +SanitizePrimaryHeaderGptEventSize ( | ||
528 | + IN CONST EFI_PARTITION_TABLE_HEADER *PrimaryHeader, | ||
529 | + IN UINTN NumberOfPartition, | ||
530 | + OUT UINT32 *EventSize | ||
531 | + ); | ||
532 | + | ||
533 | +#endif // DXE_TPM_MEASURE_BOOT_LIB_VALIDATION_ | ||
534 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c | ||
535 | new file mode 100644 | ||
536 | index 0000000000..eeb928cdb0 | ||
537 | --- /dev/null | ||
538 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTest.c | ||
539 | @@ -0,0 +1,301 @@ | ||
540 | +/** @file | ||
541 | +This file includes the unit test cases for the DxeTpmMeasureBootLibSanitizationTest.c. | ||
542 | + | ||
543 | +Copyright (c) Microsoft Corporation.<BR> | ||
544 | +SPDX-License-Identifier: BSD-2-Clause-Patent | ||
545 | +**/ | ||
546 | + | ||
547 | +#include <Uefi.h> | ||
548 | +#include <Library/UefiLib.h> | ||
549 | +#include <Library/DebugLib.h> | ||
550 | +#include <Library/UnitTestLib.h> | ||
551 | +#include <Protocol/BlockIo.h> | ||
552 | +#include <Library/MemoryAllocationLib.h> | ||
553 | +#include <Library/BaseMemoryLib.h> | ||
554 | +#include <IndustryStandard/UefiTcgPlatform.h> | ||
555 | + | ||
556 | +#include "../DxeTpmMeasureBootLibSanitization.h" | ||
557 | + | ||
558 | +#define UNIT_TEST_NAME "DxeTpmMeasureBootLibSanitizationTest" | ||
559 | +#define UNIT_TEST_VERSION "1.0" | ||
560 | + | ||
561 | +#define DEFAULT_PRIMARY_TABLE_HEADER_REVISION 0x00010000 | ||
562 | +#define DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES 1 | ||
563 | +#define DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY 128 | ||
564 | + | ||
565 | +/** | ||
566 | + This function tests the SanitizeEfiPartitionTableHeader function. | ||
567 | + It's intent is to test that a malicious EFI_PARTITION_TABLE_HEADER | ||
568 | + structure will not cause undefined or unexpected behavior. | ||
569 | + | ||
570 | + In general the TPM should still be able to measure the data, but | ||
571 | + be the header should be sanitized to prevent any unexpected behavior. | ||
572 | + | ||
573 | + @param[in] Context The unit test context. | ||
574 | + | ||
575 | + @retval UNIT_TEST_PASSED The test passed. | ||
576 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
577 | +**/ | ||
578 | +UNIT_TEST_STATUS | ||
579 | +EFIAPI | ||
580 | +TestSanitizeEfiPartitionTableHeader ( | ||
581 | + IN UNIT_TEST_CONTEXT Context | ||
582 | + ) | ||
583 | +{ | ||
584 | + EFI_STATUS Status; | ||
585 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
586 | + EFI_BLOCK_IO_PROTOCOL BlockIo; | ||
587 | + EFI_BLOCK_IO_MEDIA BlockMedia; | ||
588 | + | ||
589 | + // Generate EFI_BLOCK_IO_MEDIA test data | ||
590 | + BlockMedia.MediaId = 1; | ||
591 | + BlockMedia.RemovableMedia = FALSE; | ||
592 | + BlockMedia.MediaPresent = TRUE; | ||
593 | + BlockMedia.LogicalPartition = FALSE; | ||
594 | + BlockMedia.ReadOnly = FALSE; | ||
595 | + BlockMedia.WriteCaching = FALSE; | ||
596 | + BlockMedia.BlockSize = 512; | ||
597 | + BlockMedia.IoAlign = 1; | ||
598 | + BlockMedia.LastBlock = 0; | ||
599 | + | ||
600 | + // Generate EFI_BLOCK_IO_PROTOCOL test data | ||
601 | + BlockIo.Revision = 1; | ||
602 | + BlockIo.Media = &BlockMedia; | ||
603 | + BlockIo.Reset = NULL; | ||
604 | + BlockIo.ReadBlocks = NULL; | ||
605 | + BlockIo.WriteBlocks = NULL; | ||
606 | + BlockIo.FlushBlocks = NULL; | ||
607 | + | ||
608 | + // Geneate EFI_PARTITION_TABLE_HEADER test data | ||
609 | + PrimaryHeader.Header.Signature = EFI_PTAB_HEADER_ID; | ||
610 | + PrimaryHeader.Header.Revision = DEFAULT_PRIMARY_TABLE_HEADER_REVISION; | ||
611 | + PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER); | ||
612 | + PrimaryHeader.MyLBA = 1; | ||
613 | + PrimaryHeader.AlternateLBA = 2; | ||
614 | + PrimaryHeader.FirstUsableLBA = 3; | ||
615 | + PrimaryHeader.LastUsableLBA = 4; | ||
616 | + PrimaryHeader.PartitionEntryLBA = 5; | ||
617 | + PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_NUMBER_OF_PARTITION_ENTRIES; | ||
618 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
619 | + PrimaryHeader.PartitionEntryArrayCRC32 = 0; // Purposely invalid | ||
620 | + | ||
621 | + // Calculate the CRC32 of the PrimaryHeader | ||
622 | + PrimaryHeader.Header.CRC32 = CalculateCrc32 ((UINT8 *)&PrimaryHeader, PrimaryHeader.Header.HeaderSize); | ||
623 | + | ||
624 | + // Test that a normal PrimaryHeader passes validation | ||
625 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
626 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
627 | + | ||
628 | + // Test that when number of partition entries is 0, the function returns EFI_DEVICE_ERROR | ||
629 | + // Should print "Invalid Partition Table Header NumberOfPartitionEntries!"" | ||
630 | + PrimaryHeader.NumberOfPartitionEntries = 0; | ||
631 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
632 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
633 | + PrimaryHeader.NumberOfPartitionEntries = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
634 | + | ||
635 | + // Test that when the header size is too small, the function returns EFI_DEVICE_ERROR | ||
636 | + // Should print "Invalid Partition Table Header Size!" | ||
637 | + PrimaryHeader.Header.HeaderSize = 0; | ||
638 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
639 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
640 | + PrimaryHeader.Header.HeaderSize = sizeof (EFI_PARTITION_TABLE_HEADER); | ||
641 | + | ||
642 | + // Test that when the SizeOfPartitionEntry is too small, the function returns EFI_DEVICE_ERROR | ||
643 | + // should print: "SizeOfPartitionEntry shall be set to a value of 128 x 2^n where n is an integer greater than or equal to zero (e.g., 128, 256, 512, etc.)!" | ||
644 | + PrimaryHeader.SizeOfPartitionEntry = 1; | ||
645 | + Status = SanitizeEfiPartitionTableHeader (&PrimaryHeader, &BlockIo); | ||
646 | + UT_ASSERT_EQUAL (Status, EFI_DEVICE_ERROR); | ||
647 | + | ||
648 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
649 | + | ||
650 | + return UNIT_TEST_PASSED; | ||
651 | +} | ||
652 | + | ||
653 | +/** | ||
654 | + This function tests the SanitizePrimaryHeaderAllocationSize function. | ||
655 | + It's intent is to test that the untrusted input from a EFI_PARTITION_TABLE_HEADER | ||
656 | + structure will not cause an overflow when calculating the allocation size. | ||
657 | + | ||
658 | + @param[in] Context The unit test context. | ||
659 | + | ||
660 | + @retval UNIT_TEST_PASSED The test passed. | ||
661 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
662 | +**/ | ||
663 | +UNIT_TEST_STATUS | ||
664 | +EFIAPI | ||
665 | +TestSanitizePrimaryHeaderAllocationSize ( | ||
666 | + IN UNIT_TEST_CONTEXT Context | ||
667 | + ) | ||
668 | +{ | ||
669 | + UINT32 AllocationSize; | ||
670 | + | ||
671 | + EFI_STATUS Status; | ||
672 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
673 | + | ||
674 | + // Test that a normal PrimaryHeader passes validation | ||
675 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
676 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
677 | + | ||
678 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
679 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
680 | + | ||
681 | + // Test that the allocation size is correct compared to the existing logic | ||
682 | + UT_ASSERT_EQUAL (AllocationSize, PrimaryHeader.NumberOfPartitionEntries * PrimaryHeader.SizeOfPartitionEntry); | ||
683 | + | ||
684 | + // Test that an overflow is detected | ||
685 | + PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32; | ||
686 | + PrimaryHeader.SizeOfPartitionEntry = 5; | ||
687 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
688 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
689 | + | ||
690 | + // Test the inverse | ||
691 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
692 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
693 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
694 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
695 | + | ||
696 | + // Test the worst case scenario | ||
697 | + PrimaryHeader.NumberOfPartitionEntries = MAX_UINT32; | ||
698 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
699 | + Status = SanitizePrimaryHeaderAllocationSize (&PrimaryHeader, &AllocationSize); | ||
700 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
701 | + | ||
702 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
703 | + | ||
704 | + return UNIT_TEST_PASSED; | ||
705 | +} | ||
706 | + | ||
707 | +/** | ||
708 | + This function tests the SanitizePrimaryHeaderGptEventSize function. | ||
709 | + It's intent is to test that the untrusted input from a EFI_GPT_DATA structure | ||
710 | + will not cause an overflow when calculating the event size. | ||
711 | + | ||
712 | + @param[in] Context The unit test context. | ||
713 | + | ||
714 | + @retval UNIT_TEST_PASSED The test passed. | ||
715 | + @retval UNIT_TEST_ERROR_TEST_FAILED The test failed. | ||
716 | +**/ | ||
717 | +UNIT_TEST_STATUS | ||
718 | +EFIAPI | ||
719 | +TestSanitizePrimaryHeaderGptEventSize ( | ||
720 | + IN UNIT_TEST_CONTEXT Context | ||
721 | + ) | ||
722 | +{ | ||
723 | + UINT32 EventSize; | ||
724 | + UINT32 ExistingLogicEventSize; | ||
725 | + EFI_STATUS Status; | ||
726 | + EFI_PARTITION_TABLE_HEADER PrimaryHeader; | ||
727 | + UINTN NumberOfPartition; | ||
728 | + EFI_GPT_DATA *GptData; | ||
729 | + | ||
730 | + GptData = NULL; | ||
731 | + | ||
732 | + // Test that a normal PrimaryHeader passes validation | ||
733 | + PrimaryHeader.NumberOfPartitionEntries = 5; | ||
734 | + PrimaryHeader.SizeOfPartitionEntry = DEFAULT_PRIMARY_TABLE_HEADER_SIZE_OF_PARTITION_ENTRY; | ||
735 | + | ||
736 | + // set the number of partitions | ||
737 | + NumberOfPartition = 13; | ||
738 | + | ||
739 | + // that the primary event size is correct | ||
740 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize); | ||
741 | + UT_ASSERT_NOT_EFI_ERROR (Status); | ||
742 | + | ||
743 | + // Calculate the existing logic event size | ||
744 | + ExistingLogicEventSize = (UINT32)(sizeof (TCG_PCR_EVENT_HDR) + OFFSET_OF (EFI_GPT_DATA, Partitions) | ||
745 | + + NumberOfPartition * PrimaryHeader.SizeOfPartitionEntry); | ||
746 | + | ||
747 | + // Check that the event size is correct | ||
748 | + UT_ASSERT_EQUAL (EventSize, ExistingLogicEventSize); | ||
749 | + | ||
750 | + // Tests that the primary event size may not overflow | ||
751 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, MAX_UINT32, &EventSize); | ||
752 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
753 | + | ||
754 | + // Test that the size of partition entries may not overflow | ||
755 | + PrimaryHeader.SizeOfPartitionEntry = MAX_UINT32; | ||
756 | + Status = SanitizePrimaryHeaderGptEventSize (&PrimaryHeader, NumberOfPartition, &EventSize); | ||
757 | + UT_ASSERT_EQUAL (Status, EFI_BAD_BUFFER_SIZE); | ||
758 | + | ||
759 | + DEBUG ((DEBUG_INFO, "%a: Test passed\n", __func__)); | ||
760 | + | ||
761 | + return UNIT_TEST_PASSED; | ||
762 | +} | ||
763 | + | ||
764 | +// *--------------------------------------------------------------------* | ||
765 | +// * Unit Test Code Main Function | ||
766 | +// *--------------------------------------------------------------------* | ||
767 | + | ||
768 | +/** | ||
769 | + This function acts as the entry point for the unit tests. | ||
770 | + | ||
771 | + @param argc - The number of command line arguments | ||
772 | + @param argv - The command line arguments | ||
773 | + | ||
774 | + @return int - The status of the test | ||
775 | +**/ | ||
776 | +EFI_STATUS | ||
777 | +EFIAPI | ||
778 | +UefiTestMain ( | ||
779 | + VOID | ||
780 | + ) | ||
781 | +{ | ||
782 | + EFI_STATUS Status; | ||
783 | + UNIT_TEST_FRAMEWORK_HANDLE Framework; | ||
784 | + UNIT_TEST_SUITE_HANDLE TcgMeasureBootLibValidationTestSuite; | ||
785 | + | ||
786 | + Framework = NULL; | ||
787 | + | ||
788 | + DEBUG ((DEBUG_INFO, "%a: TestMain() - Start\n", UNIT_TEST_NAME)); | ||
789 | + | ||
790 | + Status = InitUnitTestFramework (&Framework, UNIT_TEST_NAME, gEfiCallerBaseName, UNIT_TEST_VERSION); | ||
791 | + if (EFI_ERROR (Status)) { | ||
792 | + DEBUG ((DEBUG_ERROR, "%a: Failed in InitUnitTestFramework. Status = %r\n", UNIT_TEST_NAME, Status)); | ||
793 | + goto EXIT; | ||
794 | + } | ||
795 | + | ||
796 | + Status = CreateUnitTestSuite (&TcgMeasureBootLibValidationTestSuite, Framework, "TcgMeasureBootLibValidationTestSuite", "Common.TcgMeasureBootLibValidation", NULL, NULL); | ||
797 | + if (EFI_ERROR (Status)) { | ||
798 | + DEBUG ((DEBUG_ERROR, "%s: Failed in CreateUnitTestSuite for TcgMeasureBootLibValidationTestSuite\n", UNIT_TEST_NAME)); | ||
799 | + Status = EFI_OUT_OF_RESOURCES; | ||
800 | + goto EXIT; | ||
801 | + } | ||
802 | + | ||
803 | + // -----------Suite---------------------------------Description----------------------------Class----------------------------------Test Function------------------------Pre---Clean-Context | ||
804 | + AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Validating EFI Partition Table", "Common.TcgMeasureBootLibValidation", TestSanitizeEfiPartitionTableHeader, NULL, NULL, NULL); | ||
805 | + AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Primary header gpt event checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePrimaryHeaderAllocationSize, NULL, NULL, NULL); | ||
806 | + AddTestCase (TcgMeasureBootLibValidationTestSuite, "Tests Primary header allocation size checks for overflow", "Common.TcgMeasureBootLibValidation", TestSanitizePrimaryHeaderGptEventSize, NULL, NULL, NULL); | ||
807 | + | ||
808 | + Status = RunAllTestSuites (Framework); | ||
809 | + | ||
810 | +EXIT: | ||
811 | + if (Framework != NULL) { | ||
812 | + FreeUnitTestFramework (Framework); | ||
813 | + } | ||
814 | + | ||
815 | + DEBUG ((DEBUG_INFO, "%a: TestMain() - End\n", UNIT_TEST_NAME)); | ||
816 | + return Status; | ||
817 | +} | ||
818 | + | ||
819 | +/// | ||
820 | +/// Avoid ECC error for function name that starts with lower case letter | ||
821 | +/// | ||
822 | +#define DxeTpmMeasureBootLibUnitTestMain main | ||
823 | + | ||
824 | +/** | ||
825 | + Standard POSIX C entry point for host based unit test execution. | ||
826 | + | ||
827 | + @param[in] Argc Number of arguments | ||
828 | + @param[in] Argv Array of pointers to arguments | ||
829 | + | ||
830 | + @retval 0 Success | ||
831 | + @retval other Error | ||
832 | +**/ | ||
833 | +INT32 | ||
834 | +DxeTpmMeasureBootLibUnitTestMain ( | ||
835 | + IN INT32 Argc, | ||
836 | + IN CHAR8 *Argv[] | ||
837 | + ) | ||
838 | +{ | ||
839 | + return (INT32)UefiTestMain (); | ||
840 | +} | ||
841 | diff --git a/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf | ||
842 | new file mode 100644 | ||
843 | index 0000000000..47b0811b00 | ||
844 | --- /dev/null | ||
845 | +++ b/SecurityPkg/Library/DxeTpmMeasureBootLib/InternalUnitTest/DxeTpmMeasureBootLibSanitizationTestHost.inf | ||
846 | @@ -0,0 +1,28 @@ | ||
847 | +## @file | ||
848 | +# This file builds the unit tests for DxeTpmMeasureBootLib | ||
849 | +# | ||
850 | +# Copyright (C) Microsoft Corporation.<BR> | ||
851 | +# SPDX-License-Identifier: BSD-2-Clause-Patent | ||
852 | +## | ||
853 | + | ||
854 | +[Defines] | ||
855 | + INF_VERSION = 0x00010006 | ||
856 | + BASE_NAME = DxeTpmMeasuredBootLibTest | ||
857 | + FILE_GUID = eb01bc38-309c-4d3e-967e-9f078c90772f | ||
858 | + MODULE_TYPE = HOST_APPLICATION | ||
859 | + VERSION_STRING = 1.0 | ||
860 | + ENTRY_POINT = main | ||
861 | + | ||
862 | +[Sources] | ||
863 | + DxeTpmMeasureBootLibSanitizationTest.c | ||
864 | + ../DxeTpmMeasureBootLibSanitization.c | ||
865 | + | ||
866 | +[Packages] | ||
867 | + MdePkg/MdePkg.dec | ||
868 | + | ||
869 | +[LibraryClasses] | ||
870 | + BaseLib | ||
871 | + DebugLib | ||
872 | + UnitTestLib | ||
873 | + PrintLib | ||
874 | + SafeIntLib | ||
875 | diff --git a/SecurityPkg/SecurityPkg.ci.yaml b/SecurityPkg/SecurityPkg.ci.yaml | ||
876 | index da811fdf93..0e40eaa0fe 100644 | ||
877 | --- a/SecurityPkg/SecurityPkg.ci.yaml | ||
878 | +++ b/SecurityPkg/SecurityPkg.ci.yaml | ||
879 | @@ -16,6 +16,7 @@ | ||
880 | ## ] | ||
881 | "ExceptionList": [ | ||
882 | "8001", "DxeTpm2MeasureBootLibUnitTestMain", | ||
883 | + "8001", "DxeTpmMeasureBootLibUnitTestMain" | ||
884 | ], | ||
885 | ## Both file path and directory path are accepted. | ||
886 | "IgnoreFiles": [ | ||
887 | -- | ||
888 | 2.40.0 | ||
889 | |||
diff --git a/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0003.patch b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0003.patch new file mode 100644 index 0000000000..59bd5c4910 --- /dev/null +++ b/meta/recipes-core/ovmf/ovmf/CVE-2022-36763-0003.patch | |||
@@ -0,0 +1,55 @@ | |||
1 | From 1ddcb9fc6b4164e882687b031e8beacfcf7df29e Mon Sep 17 00:00:00 2001 | ||
2 | From: "Douglas Flick [MSFT]" <doug.edk2@gmail.com> | ||
3 | Date: Fri, 12 Jan 2024 02:16:03 +0800 | ||
4 | Subject: [PATCH] SecurityPkg: : Adding CVE 2022-36763 to SecurityFixes.yaml | ||
5 | |||
6 | This creates / adds a security file that tracks the security fixes | ||
7 | found in this package and can be used to find the fixes that were | ||
8 | applied. | ||
9 | |||
10 | Cc: Jiewen Yao <jiewen.yao@intel.com> | ||
11 | |||
12 | Signed-off-by: Doug Flick [MSFT] <doug.edk2@gmail.com> | ||
13 | Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> | ||
14 | |||
15 | CVE: CVE-2022-36763 | ||
16 | |||
17 | Upstream-Status: Backport [https://github.com/tianocore/edk2/commit/1ddcb9fc6b4164e882687b031e8beacfcf7df29e] | ||
18 | |||
19 | Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com> | ||
20 | --- | ||
21 | SecurityPkg/SecurityFixes.yaml | 22 ++++++++++++++++++++++ | ||
22 | 1 file changed, 22 insertions(+) | ||
23 | create mode 100644 SecurityPkg/SecurityFixes.yaml | ||
24 | |||
25 | diff --git a/SecurityPkg/SecurityFixes.yaml b/SecurityPkg/SecurityFixes.yaml | ||
26 | new file mode 100644 | ||
27 | index 0000000000..f9e3e7be74 | ||
28 | --- /dev/null | ||
29 | +++ b/SecurityPkg/SecurityFixes.yaml | ||
30 | @@ -0,0 +1,22 @@ | ||
31 | +## @file | ||
32 | +# Security Fixes for SecurityPkg | ||
33 | +# | ||
34 | +# Copyright (c) Microsoft Corporation | ||
35 | +# SPDX-License-Identifier: BSD-2-Clause-Patent | ||
36 | +## | ||
37 | +CVE_2022_36763: | ||
38 | + commit_titles: | ||
39 | + - "SecurityPkg: DxeTpm2Measurement: SECURITY PATCH 4117 - CVE 2022-36763" | ||
40 | + - "SecurityPkg: DxeTpmMeasurement: SECURITY PATCH 4117 - CVE 2022-36763" | ||
41 | + - "SecurityPkg: : Adding CVE 2022-36763 to SecurityFixes.yaml" | ||
42 | + cve: CVE-2022-36763 | ||
43 | + date_reported: 2022-10-25 11:31 UTC | ||
44 | + description: (CVE-2022-36763) - Heap Buffer Overflow in Tcg2MeasureGptTable() | ||
45 | + note: This patch is related to and supersedes TCBZ2168 | ||
46 | + files_impacted: | ||
47 | + - Library\DxeTpm2MeasureBootLib\DxeTpm2MeasureBootLib.c | ||
48 | + - Library\DxeTpmMeasureBootLib\DxeTpmMeasureBootLib.c | ||
49 | + links: | ||
50 | + - https://bugzilla.tianocore.org/show_bug.cgi?id=4117 | ||
51 | + - https://bugzilla.tianocore.org/show_bug.cgi?id=2168 | ||
52 | + - https://bugzilla.tianocore.org/show_bug.cgi?id=1990 | ||
53 | -- | ||
54 | 2.40.0 | ||
55 | |||
diff --git a/meta/recipes-core/ovmf/ovmf_git.bb b/meta/recipes-core/ovmf/ovmf_git.bb index 84e3360a3a..78d86ad879 100644 --- a/meta/recipes-core/ovmf/ovmf_git.bb +++ b/meta/recipes-core/ovmf/ovmf_git.bb | |||
@@ -27,6 +27,9 @@ SRC_URI = "gitsm://github.com/tianocore/edk2.git;branch=master;protocol=https \ | |||
27 | file://0006-reproducible.patch \ | 27 | file://0006-reproducible.patch \ |
28 | file://0001-BaseTools-fix-gcc12-warning.patch \ | 28 | file://0001-BaseTools-fix-gcc12-warning.patch \ |
29 | file://0001-BaseTools-fix-gcc12-warning-1.patch \ | 29 | file://0001-BaseTools-fix-gcc12-warning-1.patch \ |
30 | file://CVE-2022-36763-0001.patch \ | ||
31 | file://CVE-2022-36763-0002.patch \ | ||
32 | file://CVE-2022-36763-0003.patch \ | ||
30 | " | 33 | " |
31 | 34 | ||
32 | PV = "edk2-stable202202" | 35 | PV = "edk2-stable202202" |