1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * pe.c - helper functions for pe binaries.
4 * Copyright Peter Jones <pjones@redhat.com>
9 #include <openssl/err.h>
10 #include <openssl/bn.h>
11 #include <openssl/dh.h>
12 #include <openssl/ocsp.h>
13 #include <openssl/pkcs12.h>
14 #include <openssl/rand.h>
15 #include <openssl/crypto.h>
16 #include <openssl/ssl.h>
17 #include <openssl/x509.h>
18 #include <openssl/x509v3.h>
19 #include <openssl/rsa.h>
20 #include <openssl/dso.h>
22 #include <Library/BaseCryptLib.h>
25 * Perform basic bounds checking of the intra-image pointers
28 ImageAddress (void *image
, uint64_t size
, uint64_t address
)
30 /* ensure our local pointer isn't bigger than our size */
34 /* Insure our math won't overflow */
35 if (UINT64_MAX
- address
< (uint64_t)(intptr_t)image
)
38 /* return the absolute pointer */
39 return image
+ address
;
43 * Perform the actual relocation
46 relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
47 EFI_IMAGE_SECTION_HEADER
*Section
,
48 void *orig
, void *data
)
50 EFI_IMAGE_BASE_RELOCATION
*RelocBase
, *RelocBaseEnd
;
52 UINT16
*Reloc
, *RelocEnd
;
53 char *Fixup
, *FixupBase
;
57 int size
= context
->ImageSize
;
58 void *ImageEnd
= (char *)orig
+ size
;
61 /* Alright, so here's how this works:
63 * context->RelocDir gives us two things:
64 * - the VA the table of base relocation blocks are (maybe) to be
65 * mapped at (RelocDir->VirtualAddress)
66 * - the virtual size (RelocDir->Size)
68 * The .reloc section (Section here) gives us some other things:
69 * - the name! kind of. (Section->Name)
70 * - the virtual size (Section->VirtualSize), which should be the same
72 * - the virtual address (Section->VirtualAddress)
73 * - the file section size (Section->SizeOfRawData), which is
74 * a multiple of OptHdr->FileAlignment. Only useful for image
75 * validation, not really useful for iteration bounds.
76 * - the file address (Section->PointerToRawData)
77 * - a bunch of stuff we don't use that's 0 in our binaries usually
78 * - Flags (Section->Characteristics)
80 * and then the thing that's actually at the file address is an array
81 * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
82 * them. The SizeOfBlock field of this structure includes the
83 * structure itself, and adding it to that structure's address will
84 * yield the next entry in the array.
86 RelocBase
= ImageAddress(orig
, size
, Section
->PointerToRawData
);
87 /* RelocBaseEnd here is the address of the first entry /past/ the
89 RelocBaseEnd
= ImageAddress(orig
, size
, Section
->PointerToRawData
+
90 Section
->Misc
.VirtualSize
);
92 if (!RelocBase
&& !RelocBaseEnd
)
95 if (!RelocBase
|| !RelocBaseEnd
) {
96 perror(L
"Reloc table overflows binary\n");
97 return EFI_UNSUPPORTED
;
100 Adjust
= (UINTN
)data
- context
->ImageAddress
;
105 while (RelocBase
< RelocBaseEnd
) {
106 Reloc
= (UINT16
*) ((char *) RelocBase
+ sizeof (EFI_IMAGE_BASE_RELOCATION
));
108 if (RelocBase
->SizeOfBlock
== 0) {
109 perror(L
"Reloc %d block size 0 is invalid\n", n
);
110 return EFI_UNSUPPORTED
;
111 } else if (RelocBase
->SizeOfBlock
> context
->RelocDir
->Size
) {
112 perror(L
"Reloc %d block size %d greater than reloc dir"
113 "size %d, which is invalid\n", n
,
114 RelocBase
->SizeOfBlock
,
115 context
->RelocDir
->Size
);
116 return EFI_UNSUPPORTED
;
119 RelocEnd
= (UINT16
*) ((char *) RelocBase
+ RelocBase
->SizeOfBlock
);
120 if ((void *)RelocEnd
< orig
|| (void *)RelocEnd
> ImageEnd
) {
121 perror(L
"Reloc %d entry overflows binary\n", n
);
122 return EFI_UNSUPPORTED
;
125 FixupBase
= ImageAddress(data
, size
, RelocBase
->VirtualAddress
);
127 perror(L
"Reloc %d Invalid fixupbase\n", n
);
128 return EFI_UNSUPPORTED
;
131 while (Reloc
< RelocEnd
) {
132 Fixup
= FixupBase
+ (*Reloc
& 0xFFF);
133 switch ((*Reloc
) >> 12) {
134 case EFI_IMAGE_REL_BASED_ABSOLUTE
:
137 case EFI_IMAGE_REL_BASED_HIGH
:
138 Fixup16
= (UINT16
*) Fixup
;
139 *Fixup16
= (UINT16
) (*Fixup16
+ ((UINT16
) ((UINT32
) Adjust
>> 16)));
142 case EFI_IMAGE_REL_BASED_LOW
:
143 Fixup16
= (UINT16
*) Fixup
;
144 *Fixup16
= (UINT16
) (*Fixup16
+ (UINT16
) Adjust
);
147 case EFI_IMAGE_REL_BASED_HIGHLOW
:
148 Fixup32
= (UINT32
*) Fixup
;
149 *Fixup32
= *Fixup32
+ (UINT32
) Adjust
;
152 case EFI_IMAGE_REL_BASED_DIR64
:
153 Fixup64
= (UINT64
*) Fixup
;
154 *Fixup64
= *Fixup64
+ (UINT64
) Adjust
;
158 perror(L
"Reloc %d Unknown relocation\n", n
);
159 return EFI_UNSUPPORTED
;
163 RelocBase
= (EFI_IMAGE_BASE_RELOCATION
*) RelocEnd
;
170 #define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \
171 if ((unsigned long)hashbase > \
172 (unsigned long)data + datasize_in) { \
173 efi_status = EFI_INVALID_PARAMETER; \
174 perror(L"shim.c:%d Invalid hash base 0x%016x\n", l, \
178 if ((unsigned long)hashbase + hashsize > \
179 (unsigned long)data + datasize_in) { \
180 efi_status = EFI_INVALID_PARAMETER; \
181 perror(L"shim.c:%d Invalid hash size 0x%016x\n", l, \
186 #define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__)
189 get_section_vma (UINTN section_num
,
190 char *buffer
, size_t bufsz UNUSED
,
191 PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
192 char **basep
, size_t *sizep
,
193 EFI_IMAGE_SECTION_HEADER
**sectionp
)
195 EFI_IMAGE_SECTION_HEADER
*sections
= context
->FirstSection
;
196 EFI_IMAGE_SECTION_HEADER
*section
;
197 char *base
= NULL
, *end
= NULL
;
199 if (section_num
>= context
->NumberOfSections
)
200 return EFI_NOT_FOUND
;
202 if (context
->FirstSection
== NULL
) {
203 perror(L
"Invalid section %d requested\n", section_num
);
204 return EFI_UNSUPPORTED
;
207 section
= §ions
[section_num
];
209 base
= ImageAddress (buffer
, context
->ImageSize
, section
->VirtualAddress
);
210 end
= ImageAddress (buffer
, context
->ImageSize
,
211 section
->VirtualAddress
+ section
->Misc
.VirtualSize
- 1);
213 if (!(section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
)) {
215 perror(L
"Section %d has invalid base address\n", section_num
);
216 return EFI_UNSUPPORTED
;
219 perror(L
"Section %d has zero size\n", section_num
);
220 return EFI_UNSUPPORTED
;
224 if (!(section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) &&
225 (section
->VirtualAddress
< context
->SizeOfHeaders
||
226 section
->PointerToRawData
< context
->SizeOfHeaders
)) {
227 perror(L
"Section %d is inside image headers\n", section_num
);
228 return EFI_UNSUPPORTED
;
232 perror(L
"Section %d has negative size\n", section_num
);
233 return EFI_UNSUPPORTED
;
243 get_section_vma_by_name (char *name
, size_t namesz
,
244 char *buffer
, size_t bufsz
,
245 PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
246 char **basep
, size_t *sizep
,
247 EFI_IMAGE_SECTION_HEADER
**sectionp
)
252 if (!name
|| namesz
== 0 || !buffer
|| bufsz
< namesz
|| !context
253 || !basep
|| !sizep
|| !sectionp
)
254 return EFI_INVALID_PARAMETER
;
257 * This code currently is only used for ".reloc\0\0" and
258 * ".sbat\0\0\0", and it doesn't know how to look up longer section
262 return EFI_UNSUPPORTED
;
264 SetMem(namebuf
, sizeof(namebuf
), 0);
265 CopyMem(namebuf
, name
, MIN(namesz
, 8));
268 * Copy the executable's sections to their desired offsets
270 for (i
= 0; i
< context
->NumberOfSections
; i
++) {
272 EFI_IMAGE_SECTION_HEADER
*section
= NULL
;
276 status
= get_section_vma(i
, buffer
, bufsz
, context
, &base
, &size
, §ion
);
277 if (!EFI_ERROR(status
)) {
278 if (CompareMem(section
->Name
, namebuf
, 8) == 0) {
293 return EFI_NOT_FOUND
;
297 * Calculate the SHA1 and SHA256 hashes of a binary
301 generate_hash(char *data
, unsigned int datasize_in
,
302 PE_COFF_LOADER_IMAGE_CONTEXT
*context
, UINT8
*sha256hash
,
305 unsigned int sha256ctxsize
, sha1ctxsize
;
306 unsigned int size
= datasize_in
;
307 void *sha256ctx
= NULL
, *sha1ctx
= NULL
;
309 unsigned int hashsize
;
310 unsigned int SumOfBytesHashed
, SumOfSectionBytes
;
311 unsigned int index
, pos
;
312 unsigned int datasize
;
313 EFI_IMAGE_SECTION_HEADER
*Section
;
314 EFI_IMAGE_SECTION_HEADER
*SectionHeader
= NULL
;
315 EFI_STATUS efi_status
= EFI_SUCCESS
;
316 EFI_IMAGE_DOS_HEADER
*DosHdr
= (void *)data
;
317 unsigned int PEHdr_offset
= 0;
319 size
= datasize
= datasize_in
;
321 if (datasize
<= sizeof (*DosHdr
) ||
322 DosHdr
->e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
323 perror(L
"Invalid signature\n");
324 return EFI_INVALID_PARAMETER
;
326 PEHdr_offset
= DosHdr
->e_lfanew
;
328 sha256ctxsize
= Sha256GetContextSize();
329 sha256ctx
= AllocatePool(sha256ctxsize
);
331 sha1ctxsize
= Sha1GetContextSize();
332 sha1ctx
= AllocatePool(sha1ctxsize
);
334 if (!sha256ctx
|| !sha1ctx
) {
335 perror(L
"Unable to allocate memory for hash context\n");
336 return EFI_OUT_OF_RESOURCES
;
339 if (!Sha256Init(sha256ctx
) || !Sha1Init(sha1ctx
)) {
340 perror(L
"Unable to initialise hash\n");
341 efi_status
= EFI_OUT_OF_RESOURCES
;
345 /* Hash start to checksum */
347 hashsize
= (char *)&context
->PEHdr
->Pe32
.OptionalHeader
.CheckSum
-
349 check_size(data
, datasize_in
, hashbase
, hashsize
);
351 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
352 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
353 perror(L
"Unable to generate hash\n");
354 efi_status
= EFI_OUT_OF_RESOURCES
;
358 /* Hash post-checksum to start of certificate table */
359 hashbase
= (char *)&context
->PEHdr
->Pe32
.OptionalHeader
.CheckSum
+
361 hashsize
= (char *)context
->SecDir
- hashbase
;
362 check_size(data
, datasize_in
, hashbase
, hashsize
);
364 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
365 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
366 perror(L
"Unable to generate hash\n");
367 efi_status
= EFI_OUT_OF_RESOURCES
;
371 /* Hash end of certificate table to end of image header */
372 EFI_IMAGE_DATA_DIRECTORY
*dd
= context
->SecDir
+ 1;
373 hashbase
= (char *)dd
;
374 hashsize
= context
->SizeOfHeaders
- (unsigned long)((char *)dd
- data
);
375 if (hashsize
> datasize_in
) {
376 perror(L
"Data Directory size %d is invalid\n", hashsize
);
377 efi_status
= EFI_INVALID_PARAMETER
;
380 check_size(data
, datasize_in
, hashbase
, hashsize
);
382 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
383 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
384 perror(L
"Unable to generate hash\n");
385 efi_status
= EFI_OUT_OF_RESOURCES
;
390 SumOfBytesHashed
= context
->SizeOfHeaders
;
393 * XXX Do we need this here, or is it already done in all cases?
395 if (context
->NumberOfSections
== 0 ||
396 context
->FirstSection
== NULL
) {
400 EFI_IMAGE_SECTION_HEADER
*section0
, *sectionN
;
402 nsections
= context
->PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
403 opthdrsz
= context
->PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
;
405 /* Validate section0 is within image */
406 addr
= PEHdr_offset
+ sizeof(UINT32
)
407 + sizeof(EFI_IMAGE_FILE_HEADER
)
409 section0
= ImageAddress(data
, datasize
, addr
);
411 perror(L
"Malformed file header.\n");
412 perror(L
"Image address for Section Header 0 is 0x%016llx\n",
414 perror(L
"File size is 0x%016llx\n", datasize
);
415 efi_status
= EFI_INVALID_PARAMETER
;
419 /* Validate sectionN is within image */
420 addr
+= (uint64_t)(intptr_t)§ion0
[nsections
-1] -
421 (uint64_t)(intptr_t)section0
;
422 sectionN
= ImageAddress(data
, datasize
, addr
);
424 perror(L
"Malformed file header.\n");
425 perror(L
"Image address for Section Header %d is 0x%016llx\n",
426 nsections
- 1, addr
);
427 perror(L
"File size is 0x%016llx\n", datasize
);
428 efi_status
= EFI_INVALID_PARAMETER
;
432 context
->NumberOfSections
= nsections
;
433 context
->FirstSection
= section0
;
437 * Allocate a new section table so we can sort them without
438 * modifying the image.
440 SectionHeader
= AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER
)
441 * context
->NumberOfSections
);
442 if (SectionHeader
== NULL
) {
443 perror(L
"Unable to allocate section header\n");
444 efi_status
= EFI_OUT_OF_RESOURCES
;
449 * Validate section locations and sizes, and sort the table into
450 * our newly allocated header table
452 SumOfSectionBytes
= 0;
453 Section
= context
->FirstSection
;
454 for (index
= 0; index
< context
->NumberOfSections
; index
++) {
455 EFI_IMAGE_SECTION_HEADER
*SectionPtr
;
459 efi_status
= get_section_vma(index
, data
, datasize
, context
,
460 &base
, &size
, &SectionPtr
);
461 if (efi_status
== EFI_NOT_FOUND
)
463 if (EFI_ERROR(efi_status
)) {
464 perror(L
"Malformed section header\n");
468 /* Validate section size is within image. */
469 if (SectionPtr
->SizeOfRawData
>
470 datasize
- SumOfBytesHashed
- SumOfSectionBytes
) {
471 perror(L
"Malformed section %d size\n", index
);
472 efi_status
= EFI_INVALID_PARAMETER
;
475 SumOfSectionBytes
+= SectionPtr
->SizeOfRawData
;
478 while ((pos
> 0) && (Section
->PointerToRawData
< SectionHeader
[pos
- 1].PointerToRawData
)) {
479 CopyMem (&SectionHeader
[pos
], &SectionHeader
[pos
- 1], sizeof (EFI_IMAGE_SECTION_HEADER
));
482 CopyMem (&SectionHeader
[pos
], Section
, sizeof (EFI_IMAGE_SECTION_HEADER
));
487 /* Hash the sections */
488 for (index
= 0; index
< context
->NumberOfSections
; index
++) {
489 Section
= &SectionHeader
[index
];
490 if (Section
->SizeOfRawData
== 0) {
494 hashbase
= ImageAddress(data
, size
, Section
->PointerToRawData
);
496 perror(L
"Malformed section header\n");
497 efi_status
= EFI_INVALID_PARAMETER
;
501 /* Verify hashsize within image. */
502 if (Section
->SizeOfRawData
>
503 datasize
- Section
->PointerToRawData
) {
504 perror(L
"Malformed section raw size %d\n", index
);
505 efi_status
= EFI_INVALID_PARAMETER
;
508 hashsize
= (unsigned int) Section
->SizeOfRawData
;
509 check_size(data
, datasize_in
, hashbase
, hashsize
);
511 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
512 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
513 perror(L
"Unable to generate hash\n");
514 efi_status
= EFI_OUT_OF_RESOURCES
;
517 SumOfBytesHashed
+= Section
->SizeOfRawData
;
520 /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */
521 if (datasize
> SumOfBytesHashed
&& context
->SecDir
->Size
) {
522 hashbase
= data
+ SumOfBytesHashed
;
523 hashsize
= datasize
- context
->SecDir
->Size
- SumOfBytesHashed
;
525 if ((datasize
- SumOfBytesHashed
< context
->SecDir
->Size
) ||
526 (SumOfBytesHashed
+ hashsize
!= context
->SecDir
->VirtualAddress
)) {
527 perror(L
"Malformed binary after Attribute Certificate Table\n");
528 console_print(L
"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n",
529 datasize
, SumOfBytesHashed
, context
->SecDir
->Size
);
530 console_print(L
"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n",
531 hashsize
, context
->SecDir
->VirtualAddress
);
532 efi_status
= EFI_INVALID_PARAMETER
;
535 check_size(data
, datasize_in
, hashbase
, hashsize
);
537 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
538 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
539 perror(L
"Unable to generate hash\n");
540 efi_status
= EFI_OUT_OF_RESOURCES
;
546 #else // we have to migrate to doing this later :/
547 SumOfBytesHashed
+= hashsize
;
550 /* Hash all remaining data */
551 if (datasize
> SumOfBytesHashed
) {
552 hashbase
= data
+ SumOfBytesHashed
;
553 hashsize
= datasize
- SumOfBytesHashed
;
555 check_size(data
, datasize_in
, hashbase
, hashsize
);
557 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
558 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
559 perror(L
"Unable to generate hash\n");
560 efi_status
= EFI_OUT_OF_RESOURCES
;
564 SumOfBytesHashed
+= hashsize
;
568 if (!(Sha256Final(sha256ctx
, sha256hash
)) ||
569 !(Sha1Final(sha1ctx
, sha1hash
))) {
570 perror(L
"Unable to finalise hash\n");
571 efi_status
= EFI_OUT_OF_RESOURCES
;
575 dprint(L
"sha1 authenticode hash:\n");
576 dhexdumpat(sha1hash
, SHA1_DIGEST_SIZE
, 0);
577 dprint(L
"sha256 authenticode hash:\n");
578 dhexdumpat(sha256hash
, SHA256_DIGEST_SIZE
, 0);
582 FreePool(SectionHeader
);
592 * i686 x86_64 aarch64
593 * 64-on-64: nyet yes yes
594 * 64-on-32: nyet yes nyet
595 * 32-on-32: yes yes no
600 #if defined(__x86_64__) || defined(__aarch64__)
602 #elif defined(__i386__) || defined(__i686__)
603 /* Right now blindly assuming the kernel will correctly detect this
604 * and /halt the system/ if you're not really on a 64-bit cpu */
608 #else /* assuming everything else is 32-bit... */
616 #if defined(__x86_64__)
617 #if defined(ALLOW_32BIT_KERNEL_ON_X64)
624 #elif defined(__i386__) || defined(__i686__)
626 #elif defined(__aarch64__)
628 #else /* assuming everything else is 32-bit... */
634 image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
)
636 /* .Magic is the same offset in all cases */
637 if (PEHdr
->Pe32Plus
.OptionalHeader
.Magic
638 == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
)
643 static const UINT16 machine_type
=
644 #if defined(__x86_64__)
645 IMAGE_FILE_MACHINE_X64
;
646 #elif defined(__aarch64__)
647 IMAGE_FILE_MACHINE_ARM64
;
648 #elif defined(__arm__)
649 IMAGE_FILE_MACHINE_ARMTHUMB_MIXED
;
650 #elif defined(__i386__) || defined(__i486__) || defined(__i686__)
651 IMAGE_FILE_MACHINE_I386
;
652 #elif defined(__ia64__)
653 IMAGE_FILE_MACHINE_IA64
;
655 #error this architecture is not supported by shim
659 image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
)
661 /* If the machine type doesn't match the binary, bail, unless
662 * we're in an allowed 64-on-32 scenario */
663 if (PEHdr
->Pe32
.FileHeader
.Machine
!= machine_type
) {
664 if (!(machine_type
== IMAGE_FILE_MACHINE_I386
&&
665 PEHdr
->Pe32
.FileHeader
.Machine
== IMAGE_FILE_MACHINE_X64
&&
671 /* If it's not a header type we recognize at all, bail */
672 switch (PEHdr
->Pe32Plus
.OptionalHeader
.Magic
) {
673 case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
674 case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
680 /* and now just check for general 64-vs-32 compatibility */
681 if (image_is_64_bit(PEHdr
)) {
692 * Read the binary header and grab appropriate information from it
695 read_header(void *data
, unsigned int datasize
,
696 PE_COFF_LOADER_IMAGE_CONTEXT
*context
)
698 EFI_IMAGE_DOS_HEADER
*DosHdr
= data
;
699 EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
= data
;
700 unsigned long HeaderWithoutDataDir
, SectionHeaderOffset
, OptHeaderSize
;
701 unsigned long FileAlignment
= 0;
703 if (datasize
< sizeof (PEHdr
->Pe32
)) {
704 perror(L
"Invalid image\n");
705 return EFI_UNSUPPORTED
;
708 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
)
709 PEHdr
= (EFI_IMAGE_OPTIONAL_HEADER_UNION
*)((char *)data
+ DosHdr
->e_lfanew
);
711 if (!image_is_loadable(PEHdr
)) {
712 perror(L
"Platform does not support this image\n");
713 return EFI_UNSUPPORTED
;
716 if (image_is_64_bit(PEHdr
)) {
717 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32Plus
.OptionalHeader
.NumberOfRvaAndSizes
;
718 context
->SizeOfHeaders
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfHeaders
;
719 context
->ImageSize
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
720 context
->SectionAlignment
= PEHdr
->Pe32Plus
.OptionalHeader
.SectionAlignment
;
721 FileAlignment
= PEHdr
->Pe32Plus
.OptionalHeader
.FileAlignment
;
722 OptHeaderSize
= sizeof(EFI_IMAGE_OPTIONAL_HEADER64
);
724 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32
.OptionalHeader
.NumberOfRvaAndSizes
;
725 context
->SizeOfHeaders
= PEHdr
->Pe32
.OptionalHeader
.SizeOfHeaders
;
726 context
->ImageSize
= (UINT64
)PEHdr
->Pe32
.OptionalHeader
.SizeOfImage
;
727 context
->SectionAlignment
= PEHdr
->Pe32
.OptionalHeader
.SectionAlignment
;
728 FileAlignment
= PEHdr
->Pe32
.OptionalHeader
.FileAlignment
;
729 OptHeaderSize
= sizeof(EFI_IMAGE_OPTIONAL_HEADER32
);
732 if (FileAlignment
% 2 != 0) {
733 perror(L
"File Alignment is invalid (%d)\n", FileAlignment
);
734 return EFI_UNSUPPORTED
;
736 if (FileAlignment
== 0)
737 FileAlignment
= 0x200;
738 if (context
->SectionAlignment
== 0)
739 context
->SectionAlignment
= PAGE_SIZE
;
740 if (context
->SectionAlignment
< FileAlignment
)
741 context
->SectionAlignment
= FileAlignment
;
743 context
->NumberOfSections
= PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
745 if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES
< context
->NumberOfRvaAndSizes
) {
746 perror(L
"Image header too small\n");
747 return EFI_UNSUPPORTED
;
750 HeaderWithoutDataDir
= OptHeaderSize
751 - sizeof (EFI_IMAGE_DATA_DIRECTORY
) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES
;
752 if (((UINT32
)PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
- HeaderWithoutDataDir
) !=
753 context
->NumberOfRvaAndSizes
* sizeof (EFI_IMAGE_DATA_DIRECTORY
)) {
754 perror(L
"Image header overflows data directory\n");
755 return EFI_UNSUPPORTED
;
758 SectionHeaderOffset
= DosHdr
->e_lfanew
760 + sizeof (EFI_IMAGE_FILE_HEADER
)
761 + PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
;
762 if (((UINT32
)context
->ImageSize
- SectionHeaderOffset
) / EFI_IMAGE_SIZEOF_SECTION_HEADER
763 <= context
->NumberOfSections
) {
764 perror(L
"Image sections overflow image size\n");
765 return EFI_UNSUPPORTED
;
768 if ((context
->SizeOfHeaders
- SectionHeaderOffset
) / EFI_IMAGE_SIZEOF_SECTION_HEADER
769 < (UINT32
)context
->NumberOfSections
) {
770 perror(L
"Image sections overflow section headers\n");
771 return EFI_UNSUPPORTED
;
774 if ((((UINT8
*)PEHdr
- (UINT8
*)data
) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION
)) > datasize
) {
775 perror(L
"Invalid image\n");
776 return EFI_UNSUPPORTED
;
779 if (PEHdr
->Te
.Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
780 perror(L
"Unsupported image type\n");
781 return EFI_UNSUPPORTED
;
784 if (PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) {
785 perror(L
"Unsupported image - Relocations have been stripped\n");
786 return EFI_UNSUPPORTED
;
789 context
->PEHdr
= PEHdr
;
791 if (image_is_64_bit(PEHdr
)) {
792 context
->ImageAddress
= PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
;
793 context
->EntryPoint
= PEHdr
->Pe32Plus
.OptionalHeader
.AddressOfEntryPoint
;
794 context
->RelocDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
795 context
->SecDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
797 context
->ImageAddress
= PEHdr
->Pe32
.OptionalHeader
.ImageBase
;
798 context
->EntryPoint
= PEHdr
->Pe32
.OptionalHeader
.AddressOfEntryPoint
;
799 context
->RelocDir
= &PEHdr
->Pe32
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
800 context
->SecDir
= &PEHdr
->Pe32
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
803 context
->FirstSection
= (EFI_IMAGE_SECTION_HEADER
*)((char *)PEHdr
+ PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
+ sizeof(UINT32
) + sizeof(EFI_IMAGE_FILE_HEADER
));
805 if (context
->ImageSize
< context
->SizeOfHeaders
) {
806 perror(L
"Invalid image\n");
807 return EFI_UNSUPPORTED
;
810 if ((unsigned long)((UINT8
*)context
->SecDir
- (UINT8
*)data
) >
811 (datasize
- sizeof(EFI_IMAGE_DATA_DIRECTORY
))) {
812 perror(L
"Invalid image\n");
813 return EFI_UNSUPPORTED
;
816 if (context
->SecDir
->VirtualAddress
> datasize
||
817 (context
->SecDir
->VirtualAddress
== datasize
&&
818 context
->SecDir
->Size
> 0)) {
819 perror(L
"Malformed security header\n");
820 return EFI_INVALID_PARAMETER
;
826 handle_sbat(char *SBATBase
, size_t SBATSize
)
829 EFI_STATUS efi_status
;
831 struct sbat_section_entry
**entries
= NULL
;
835 if (list_empty(&sbat_var
))
838 if (SBATBase
== NULL
|| SBATSize
== 0) {
839 dprint(L
"No .sbat section data\n");
840 return EFI_SECURITY_VIOLATION
;
843 sbat_size
= SBATSize
+ 1;
844 sbat_data
= AllocatePool(sbat_size
);
846 console_print(L
"Failed to allocate .sbat section buffer\n");
847 return EFI_OUT_OF_RESOURCES
;
849 CopyMem(sbat_data
, SBATBase
, SBATSize
);
850 sbat_data
[SBATSize
] = '\0';
852 efi_status
= parse_sbat_section(sbat_data
, sbat_size
, &n
, &entries
);
853 if (EFI_ERROR(efi_status
)) {
854 perror(L
"Could not parse .sbat section data: %r\n", efi_status
);
858 dprint(L
"SBAT section data\n");
859 for (i
= 0; i
< n
; i
++) {
860 dprint(L
"%a, %a, %a, %a, %a, %a\n",
861 entries
[i
]->component_name
,
862 entries
[i
]->component_generation
,
863 entries
[i
]->vendor_name
,
864 entries
[i
]->vendor_package_name
,
865 entries
[i
]->vendor_version
,
866 entries
[i
]->vendor_url
);
869 efi_status
= verify_sbat(n
, entries
);
871 cleanup_sbat_section_entries(n
, entries
);
880 * Once the image has been loaded it needs to be validated and relocated
883 handle_image (void *data
, unsigned int datasize
,
884 EFI_LOADED_IMAGE
*li
,
885 EFI_IMAGE_ENTRY_POINT
*entry_point
,
886 EFI_PHYSICAL_ADDRESS
*alloc_address
,
889 EFI_STATUS efi_status
;
892 EFI_IMAGE_SECTION_HEADER
*Section
;
894 PE_COFF_LOADER_IMAGE_CONTEXT context
;
895 unsigned int alignment
, alloc_size
;
896 int found_entry_point
= 0;
897 UINT8 sha1hash
[SHA1_DIGEST_SIZE
];
898 UINT8 sha256hash
[SHA256_DIGEST_SIZE
];
901 * The binary header contains relevant context and section pointers
903 efi_status
= read_header(data
, datasize
, &context
);
904 if (EFI_ERROR(efi_status
)) {
905 perror(L
"Failed to read header: %r\n", efi_status
);
910 * We only need to verify the binary if we're in secure mode
912 efi_status
= generate_hash(data
, datasize
, &context
, sha256hash
,
914 if (EFI_ERROR(efi_status
))
917 /* Measure the binary into the TPM */
921 tpm_log_pe((EFI_PHYSICAL_ADDRESS
)(UINTN
)data
, datasize
,
922 (EFI_PHYSICAL_ADDRESS
)(UINTN
)context
.ImageAddress
,
923 li
->FilePath
, sha1hash
, 4);
925 if (efi_status
!= EFI_SUCCESS
) {
930 /* The spec says, uselessly, of SectionAlignment:
932 * The alignment (in bytes) of sections when they are loaded into
933 * memory. It must be greater than or equal to FileAlignment. The
934 * default is the page size for the architecture.
936 * Which doesn't tell you whose responsibility it is to enforce the
937 * "default", or when. It implies that the value in the field must
938 * be > FileAlignment (also poorly defined), but it appears visual
939 * studio will happily write 512 for FileAlignment (its default) and
940 * 0 for SectionAlignment, intending to imply PAGE_SIZE.
942 * We only support one page size, so if it's zero, nerf it to 4096.
944 alignment
= context
.SectionAlignment
;
948 alloc_size
= ALIGN_VALUE(context
.ImageSize
+ context
.SectionAlignment
,
950 *alloc_pages
= alloc_size
/ PAGE_SIZE
;
952 efi_status
= gBS
->AllocatePages(AllocateAnyPages
, EfiLoaderCode
,
953 *alloc_pages
, alloc_address
);
954 if (EFI_ERROR(efi_status
)) {
955 perror(L
"Failed to allocate image buffer\n");
956 return EFI_OUT_OF_RESOURCES
;
959 buffer
= (void *)ALIGN_VALUE((unsigned long)*alloc_address
, alignment
);
961 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
963 *entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
965 perror(L
"Entry point is invalid\n");
966 gBS
->FreePages(*alloc_address
, *alloc_pages
);
967 return EFI_UNSUPPORTED
;
970 char *RelocBase
, *RelocBaseEnd
;
972 * These are relative virtual addresses, so we have to check them
973 * against the image size, not the data size.
975 RelocBase
= ImageAddress(buffer
, context
.ImageSize
,
976 context
.RelocDir
->VirtualAddress
);
978 * RelocBaseEnd here is the address of the last byte of the table
980 RelocBaseEnd
= ImageAddress(buffer
, context
.ImageSize
,
981 context
.RelocDir
->VirtualAddress
+
982 context
.RelocDir
->Size
- 1);
984 EFI_IMAGE_SECTION_HEADER
*RelocSection
= NULL
;
986 char *SBATBase
= NULL
;
990 * Copy the executable's sections to their desired offsets
992 Section
= context
.FirstSection
;
993 for (i
= 0; i
< context
.NumberOfSections
; i
++, Section
++) {
994 /* Don't try to copy discardable sections with zero size */
995 if ((Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) &&
996 !Section
->Misc
.VirtualSize
)
999 base
= ImageAddress (buffer
, context
.ImageSize
,
1000 Section
->VirtualAddress
);
1001 end
= ImageAddress (buffer
, context
.ImageSize
,
1002 Section
->VirtualAddress
1003 + Section
->Misc
.VirtualSize
- 1);
1006 perror(L
"Section %d has negative size\n", i
);
1007 gBS
->FreePages(*alloc_address
, *alloc_pages
);
1008 return EFI_UNSUPPORTED
;
1011 if (Section
->VirtualAddress
<= context
.EntryPoint
&&
1012 (Section
->VirtualAddress
+ Section
->SizeOfRawData
- 1)
1013 > context
.EntryPoint
)
1014 found_entry_point
++;
1016 /* We do want to process .reloc, but it's often marked
1017 * discardable, so we don't want to memcpy it. */
1018 if (CompareMem(Section
->Name
, ".reloc\0\0", 8) == 0) {
1020 perror(L
"Image has multiple relocation sections\n");
1021 return EFI_UNSUPPORTED
;
1023 /* If it has nonzero sizes, and our bounds check
1024 * made sense, and the VA and size match RelocDir's
1025 * versions, then we believe in this section table. */
1026 if (Section
->SizeOfRawData
&&
1027 Section
->Misc
.VirtualSize
&&
1029 RelocBase
== base
&&
1030 RelocBaseEnd
== end
) {
1031 RelocSection
= Section
;
1033 } else if (CompareMem(Section
->Name
, ".sbat\0\0\0", 8) == 0) {
1034 if (SBATBase
|| SBATSize
) {
1035 perror(L
"Image has multiple SBAT sections\n");
1036 return EFI_UNSUPPORTED
;
1039 if (Section
->NumberOfRelocations
!= 0 ||
1040 Section
->PointerToRelocations
!= 0) {
1041 perror(L
"SBAT section has relocations\n");
1042 return EFI_UNSUPPORTED
;
1045 /* The virtual size corresponds to the size of the SBAT
1046 * metadata and isn't necessarily a multiple of the file
1047 * alignment. The on-disk size is a multiple of the file
1048 * alignment and is zero padded. Make sure that the
1049 * on-disk size is at least as large as virtual size,
1050 * and ignore the section if it isn't. */
1051 if (Section
->SizeOfRawData
&&
1052 Section
->SizeOfRawData
>= Section
->Misc
.VirtualSize
&&
1055 /* +1 because of size vs last byte location */
1056 SBATSize
= end
- base
+ 1;
1057 dprint(L
"sbat section base:0x%lx size:0x%lx\n",
1058 SBATBase
, SBATSize
);
1062 if (Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) {
1067 perror(L
"Section %d has invalid base address\n", i
);
1068 return EFI_UNSUPPORTED
;
1071 perror(L
"Section %d has zero size\n", i
);
1072 return EFI_UNSUPPORTED
;
1075 if (!(Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) &&
1076 (Section
->VirtualAddress
< context
.SizeOfHeaders
||
1077 Section
->PointerToRawData
< context
.SizeOfHeaders
)) {
1078 perror(L
"Section %d is inside image headers\n", i
);
1079 return EFI_UNSUPPORTED
;
1082 if (Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) {
1083 ZeroMem(base
, Section
->Misc
.VirtualSize
);
1085 if (Section
->PointerToRawData
< context
.SizeOfHeaders
) {
1086 perror(L
"Section %d is inside image headers\n", i
);
1087 return EFI_UNSUPPORTED
;
1090 if (Section
->SizeOfRawData
> 0)
1091 CopyMem(base
, data
+ Section
->PointerToRawData
,
1092 Section
->SizeOfRawData
);
1094 if (Section
->SizeOfRawData
< Section
->Misc
.VirtualSize
)
1095 ZeroMem(base
+ Section
->SizeOfRawData
,
1096 Section
->Misc
.VirtualSize
- Section
->SizeOfRawData
);
1100 if (secure_mode ()) {
1101 efi_status
= handle_sbat(SBATBase
, SBATSize
);
1103 if (!EFI_ERROR(efi_status
))
1104 efi_status
= verify_buffer(data
, datasize
,
1105 &context
, sha256hash
, sha1hash
);
1107 if (EFI_ERROR(efi_status
)) {
1109 console_print(L
"Verification failed: %r\n", efi_status
);
1111 console_error(L
"Verification failed", efi_status
);
1115 console_print(L
"Verification succeeded\n");
1119 if (context
.NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
1120 perror(L
"Image has no relocation entry\n");
1122 return EFI_UNSUPPORTED
;
1125 if (context
.RelocDir
->Size
&& RelocSection
) {
1127 * Run the relocation fixups
1129 efi_status
= relocate_coff(&context
, RelocSection
, data
,
1132 if (EFI_ERROR(efi_status
)) {
1133 perror(L
"Relocation failed: %r\n", efi_status
);
1140 * grub needs to know its location and size in memory, so fix up
1141 * the loaded image protocol values
1143 li
->ImageBase
= buffer
;
1144 li
->ImageSize
= context
.ImageSize
;
1146 /* Pass the load options to the second stage loader */
1147 if ( load_options
) {
1148 li
->LoadOptions
= load_options
;
1149 li
->LoadOptionsSize
= load_options_size
;
1152 if (!found_entry_point
) {
1153 perror(L
"Entry point is not within sections\n");
1154 return EFI_UNSUPPORTED
;
1156 if (found_entry_point
> 1) {
1157 perror(L
"%d sections contain entry point\n");
1158 return EFI_UNSUPPORTED
;
1164 // vim:fenc=utf-8:tw=75:noet