1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * pe.c - helper functions for pe binaries.
4 * Copyright Peter Jones <pjones@redhat.com>
10 #include <openssl/err.h>
11 #include <openssl/bn.h>
12 #include <openssl/dh.h>
13 #include <openssl/ocsp.h>
14 #include <openssl/pkcs12.h>
15 #include <openssl/rand.h>
16 #include <openssl/crypto.h>
17 #include <openssl/ssl.h>
18 #include <openssl/x509.h>
19 #include <openssl/x509v3.h>
20 #include <openssl/rsa.h>
21 #include <openssl/dso.h>
23 #include <Library/BaseCryptLib.h>
26 * Perform basic bounds checking of the intra-image pointers
29 ImageAddress (void *image
, uint64_t size
, uint64_t address
)
31 /* ensure our local pointer isn't bigger than our size */
35 /* Insure our math won't overflow */
36 if (UINT64_MAX
- address
< (uint64_t)(intptr_t)image
)
39 /* return the absolute pointer */
40 return image
+ address
;
44 * Perform the actual relocation
47 relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
48 EFI_IMAGE_SECTION_HEADER
*Section
,
49 void *orig
, void *data
)
51 EFI_IMAGE_BASE_RELOCATION
*RelocBase
, *RelocBaseEnd
;
53 UINT16
*Reloc
, *RelocEnd
;
54 char *Fixup
, *FixupBase
;
58 int size
= context
->ImageSize
;
59 void *ImageEnd
= (char *)orig
+ size
;
62 /* Alright, so here's how this works:
64 * context->RelocDir gives us two things:
65 * - the VA the table of base relocation blocks are (maybe) to be
66 * mapped at (RelocDir->VirtualAddress)
67 * - the virtual size (RelocDir->Size)
69 * The .reloc section (Section here) gives us some other things:
70 * - the name! kind of. (Section->Name)
71 * - the virtual size (Section->VirtualSize), which should be the same
73 * - the virtual address (Section->VirtualAddress)
74 * - the file section size (Section->SizeOfRawData), which is
75 * a multiple of OptHdr->FileAlignment. Only useful for image
76 * validation, not really useful for iteration bounds.
77 * - the file address (Section->PointerToRawData)
78 * - a bunch of stuff we don't use that's 0 in our binaries usually
79 * - Flags (Section->Characteristics)
81 * and then the thing that's actually at the file address is an array
82 * of EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
83 * them. The SizeOfBlock field of this structure includes the
84 * structure itself, and adding it to that structure's address will
85 * yield the next entry in the array.
87 RelocBase
= ImageAddress(orig
, size
, Section
->PointerToRawData
);
88 /* RelocBaseEnd here is the address of the first entry /past/ the
90 RelocBaseEnd
= ImageAddress(orig
, size
, Section
->PointerToRawData
+
91 Section
->Misc
.VirtualSize
);
93 if (!RelocBase
&& !RelocBaseEnd
)
96 if (!RelocBase
|| !RelocBaseEnd
) {
97 perror(L
"Reloc table overflows binary\n");
98 return EFI_UNSUPPORTED
;
101 Adjust
= (UINTN
)data
- context
->ImageAddress
;
106 while (RelocBase
< RelocBaseEnd
) {
107 Reloc
= (UINT16
*) ((char *) RelocBase
+ sizeof (EFI_IMAGE_BASE_RELOCATION
));
109 if (RelocBase
->SizeOfBlock
== 0) {
110 perror(L
"Reloc %d block size 0 is invalid\n", n
);
111 return EFI_UNSUPPORTED
;
112 } else if (RelocBase
->SizeOfBlock
> context
->RelocDir
->Size
) {
113 perror(L
"Reloc %d block size %d greater than reloc dir"
114 "size %d, which is invalid\n", n
,
115 RelocBase
->SizeOfBlock
,
116 context
->RelocDir
->Size
);
117 return EFI_UNSUPPORTED
;
120 RelocEnd
= (UINT16
*) ((char *) RelocBase
+ RelocBase
->SizeOfBlock
);
121 if ((void *)RelocEnd
< orig
|| (void *)RelocEnd
> ImageEnd
) {
122 perror(L
"Reloc %d entry overflows binary\n", n
);
123 return EFI_UNSUPPORTED
;
126 FixupBase
= ImageAddress(data
, size
, RelocBase
->VirtualAddress
);
128 perror(L
"Reloc %d Invalid fixupbase\n", n
);
129 return EFI_UNSUPPORTED
;
132 while (Reloc
< RelocEnd
) {
133 Fixup
= FixupBase
+ (*Reloc
& 0xFFF);
134 switch ((*Reloc
) >> 12) {
135 case EFI_IMAGE_REL_BASED_ABSOLUTE
:
138 case EFI_IMAGE_REL_BASED_HIGH
:
139 Fixup16
= (UINT16
*) Fixup
;
140 *Fixup16
= (UINT16
) (*Fixup16
+ ((UINT16
) ((UINT32
) Adjust
>> 16)));
143 case EFI_IMAGE_REL_BASED_LOW
:
144 Fixup16
= (UINT16
*) Fixup
;
145 *Fixup16
= (UINT16
) (*Fixup16
+ (UINT16
) Adjust
);
148 case EFI_IMAGE_REL_BASED_HIGHLOW
:
149 Fixup32
= (UINT32
*) Fixup
;
150 *Fixup32
= *Fixup32
+ (UINT32
) Adjust
;
153 case EFI_IMAGE_REL_BASED_DIR64
:
154 Fixup64
= (UINT64
*) Fixup
;
155 *Fixup64
= *Fixup64
+ (UINT64
) Adjust
;
159 perror(L
"Reloc %d Unknown relocation\n", n
);
160 return EFI_UNSUPPORTED
;
164 RelocBase
= (EFI_IMAGE_BASE_RELOCATION
*) RelocEnd
;
171 #define check_size_line(data, datasize_in, hashbase, hashsize, l) ({ \
172 if ((unsigned long)hashbase > \
173 (unsigned long)data + datasize_in) { \
174 efi_status = EFI_INVALID_PARAMETER; \
175 perror(L"shim.c:%d Invalid hash base 0x%016x\n", l, \
179 if ((unsigned long)hashbase + hashsize > \
180 (unsigned long)data + datasize_in) { \
181 efi_status = EFI_INVALID_PARAMETER; \
182 perror(L"shim.c:%d Invalid hash size 0x%016x\n", l, \
187 #define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__)
190 get_section_vma (UINTN section_num
,
191 char *buffer
, size_t bufsz
,
192 PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
193 char **basep
, size_t *sizep
,
194 EFI_IMAGE_SECTION_HEADER
**sectionp
)
196 EFI_IMAGE_SECTION_HEADER
*sections
= context
->FirstSection
;
197 EFI_IMAGE_SECTION_HEADER
*section
;
198 char *base
= NULL
, *end
= NULL
;
200 if (section_num
>= context
->NumberOfSections
)
201 return EFI_NOT_FOUND
;
203 if (context
->FirstSection
== NULL
) {
204 perror(L
"Invalid section %d requested\n", section_num
);
205 return EFI_UNSUPPORTED
;
208 section
= §ions
[section_num
];
210 base
= ImageAddress (buffer
, context
->ImageSize
, section
->VirtualAddress
);
211 end
= ImageAddress (buffer
, context
->ImageSize
,
212 section
->VirtualAddress
+ section
->Misc
.VirtualSize
- 1);
214 if (!(section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
)) {
216 perror(L
"Section %d has invalid base address\n", section_num
);
217 return EFI_UNSUPPORTED
;
220 perror(L
"Section %d has zero size\n", section_num
);
221 return EFI_UNSUPPORTED
;
225 if (!(section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) &&
226 (section
->VirtualAddress
< context
->SizeOfHeaders
||
227 section
->PointerToRawData
< context
->SizeOfHeaders
)) {
228 perror(L
"Section %d is inside image headers\n", section_num
);
229 return EFI_UNSUPPORTED
;
233 perror(L
"Section %d has negative size\n", section_num
);
234 return EFI_UNSUPPORTED
;
244 get_section_vma_by_name (char *name
, size_t namesz
,
245 char *buffer
, size_t bufsz
,
246 PE_COFF_LOADER_IMAGE_CONTEXT
*context
,
247 char **basep
, size_t *sizep
,
248 EFI_IMAGE_SECTION_HEADER
**sectionp
)
253 if (!name
|| namesz
== 0 || !buffer
|| bufsz
< namesz
|| !context
254 || !basep
|| !sizep
|| !sectionp
)
255 return EFI_INVALID_PARAMETER
;
258 * This code currently is only used for ".reloc\0\0" and
259 * ".sbat\0\0\0", and it doesn't know how to look up longer section
263 return EFI_UNSUPPORTED
;
265 SetMem(namebuf
, sizeof(namebuf
), 0);
266 CopyMem(namebuf
, name
, MIN(namesz
, 8));
269 * Copy the executable's sections to their desired offsets
271 for (i
= 0; i
< context
->NumberOfSections
; i
++) {
273 EFI_IMAGE_SECTION_HEADER
*section
= NULL
;
277 status
= get_section_vma(i
, buffer
, bufsz
, context
, &base
, &size
, §ion
);
278 if (!EFI_ERROR(status
)) {
279 if (CompareMem(section
->Name
, namebuf
, 8) == 0) {
294 return EFI_NOT_FOUND
;
298 * Calculate the SHA1 and SHA256 hashes of a binary
302 generate_hash(char *data
, unsigned int datasize_in
,
303 PE_COFF_LOADER_IMAGE_CONTEXT
*context
, UINT8
*sha256hash
,
306 unsigned int sha256ctxsize
, sha1ctxsize
;
307 unsigned int size
= datasize_in
;
308 void *sha256ctx
= NULL
, *sha1ctx
= NULL
;
310 unsigned int hashsize
;
311 unsigned int SumOfBytesHashed
, SumOfSectionBytes
;
312 unsigned int index
, pos
;
313 unsigned int datasize
;
314 EFI_IMAGE_SECTION_HEADER
*Section
;
315 EFI_IMAGE_SECTION_HEADER
*SectionHeader
= NULL
;
316 EFI_STATUS efi_status
= EFI_SUCCESS
;
317 EFI_IMAGE_DOS_HEADER
*DosHdr
= (void *)data
;
318 unsigned int PEHdr_offset
= 0;
320 size
= datasize
= datasize_in
;
322 if (datasize
<= sizeof (*DosHdr
) ||
323 DosHdr
->e_magic
!= EFI_IMAGE_DOS_SIGNATURE
) {
324 perror(L
"Invalid signature\n");
325 return EFI_INVALID_PARAMETER
;
327 PEHdr_offset
= DosHdr
->e_lfanew
;
329 sha256ctxsize
= Sha256GetContextSize();
330 sha256ctx
= AllocatePool(sha256ctxsize
);
332 sha1ctxsize
= Sha1GetContextSize();
333 sha1ctx
= AllocatePool(sha1ctxsize
);
335 if (!sha256ctx
|| !sha1ctx
) {
336 perror(L
"Unable to allocate memory for hash context\n");
337 return EFI_OUT_OF_RESOURCES
;
340 if (!Sha256Init(sha256ctx
) || !Sha1Init(sha1ctx
)) {
341 perror(L
"Unable to initialise hash\n");
342 efi_status
= EFI_OUT_OF_RESOURCES
;
346 /* Hash start to checksum */
348 hashsize
= (char *)&context
->PEHdr
->Pe32
.OptionalHeader
.CheckSum
-
350 check_size(data
, datasize_in
, hashbase
, hashsize
);
352 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
353 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
354 perror(L
"Unable to generate hash\n");
355 efi_status
= EFI_OUT_OF_RESOURCES
;
359 /* Hash post-checksum to start of certificate table */
360 hashbase
= (char *)&context
->PEHdr
->Pe32
.OptionalHeader
.CheckSum
+
362 hashsize
= (char *)context
->SecDir
- hashbase
;
363 check_size(data
, datasize_in
, hashbase
, hashsize
);
365 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
366 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
367 perror(L
"Unable to generate hash\n");
368 efi_status
= EFI_OUT_OF_RESOURCES
;
372 /* Hash end of certificate table to end of image header */
373 EFI_IMAGE_DATA_DIRECTORY
*dd
= context
->SecDir
+ 1;
374 hashbase
= (char *)dd
;
375 hashsize
= context
->SizeOfHeaders
- (unsigned long)((char *)dd
- data
);
376 if (hashsize
> datasize_in
) {
377 perror(L
"Data Directory size %d is invalid\n", hashsize
);
378 efi_status
= EFI_INVALID_PARAMETER
;
381 check_size(data
, datasize_in
, hashbase
, hashsize
);
383 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
384 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
385 perror(L
"Unable to generate hash\n");
386 efi_status
= EFI_OUT_OF_RESOURCES
;
391 SumOfBytesHashed
= context
->SizeOfHeaders
;
394 * XXX Do we need this here, or is it already done in all cases?
396 if (context
->NumberOfSections
== 0 ||
397 context
->FirstSection
== NULL
) {
401 EFI_IMAGE_SECTION_HEADER
*section0
, *sectionN
;
403 nsections
= context
->PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
404 opthdrsz
= context
->PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
;
406 /* Validate section0 is within image */
407 addr
= PEHdr_offset
+ sizeof(UINT32
)
408 + sizeof(EFI_IMAGE_FILE_HEADER
)
410 section0
= ImageAddress(data
, datasize
, addr
);
412 perror(L
"Malformed file header.\n");
413 perror(L
"Image address for Section Header 0 is 0x%016llx\n",
415 perror(L
"File size is 0x%016llx\n", datasize
);
416 efi_status
= EFI_INVALID_PARAMETER
;
420 /* Validate sectionN is within image */
421 addr
+= (uint64_t)(intptr_t)§ion0
[nsections
-1] -
422 (uint64_t)(intptr_t)section0
;
423 sectionN
= ImageAddress(data
, datasize
, addr
);
425 perror(L
"Malformed file header.\n");
426 perror(L
"Image address for Section Header %d is 0x%016llx\n",
427 nsections
- 1, addr
);
428 perror(L
"File size is 0x%016llx\n", datasize
);
429 efi_status
= EFI_INVALID_PARAMETER
;
433 context
->NumberOfSections
= nsections
;
434 context
->FirstSection
= section0
;
438 * Allocate a new section table so we can sort them without
439 * modifying the image.
441 SectionHeader
= AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER
)
442 * context
->NumberOfSections
);
443 if (SectionHeader
== NULL
) {
444 perror(L
"Unable to allocate section header\n");
445 efi_status
= EFI_OUT_OF_RESOURCES
;
450 * Validate section locations and sizes, and sort the table into
451 * our newly allocated header table
453 SumOfSectionBytes
= 0;
454 Section
= context
->FirstSection
;
455 for (index
= 0; index
< context
->NumberOfSections
; index
++) {
456 EFI_IMAGE_SECTION_HEADER
*SectionPtr
;
460 efi_status
= get_section_vma(index
, data
, datasize
, context
,
461 &base
, &size
, &SectionPtr
);
462 if (efi_status
== EFI_NOT_FOUND
)
464 if (EFI_ERROR(efi_status
)) {
465 perror(L
"Malformed section header\n");
469 /* Validate section size is within image. */
470 if (SectionPtr
->SizeOfRawData
>
471 datasize
- SumOfBytesHashed
- SumOfSectionBytes
) {
472 perror(L
"Malformed section %d size\n", index
);
473 efi_status
= EFI_INVALID_PARAMETER
;
476 SumOfSectionBytes
+= SectionPtr
->SizeOfRawData
;
479 while ((pos
> 0) && (Section
->PointerToRawData
< SectionHeader
[pos
- 1].PointerToRawData
)) {
480 CopyMem (&SectionHeader
[pos
], &SectionHeader
[pos
- 1], sizeof (EFI_IMAGE_SECTION_HEADER
));
483 CopyMem (&SectionHeader
[pos
], Section
, sizeof (EFI_IMAGE_SECTION_HEADER
));
488 /* Hash the sections */
489 for (index
= 0; index
< context
->NumberOfSections
; index
++) {
490 Section
= &SectionHeader
[index
];
491 if (Section
->SizeOfRawData
== 0) {
495 hashbase
= ImageAddress(data
, size
, Section
->PointerToRawData
);
497 perror(L
"Malformed section header\n");
498 efi_status
= EFI_INVALID_PARAMETER
;
502 /* Verify hashsize within image. */
503 if (Section
->SizeOfRawData
>
504 datasize
- Section
->PointerToRawData
) {
505 perror(L
"Malformed section raw size %d\n", index
);
506 efi_status
= EFI_INVALID_PARAMETER
;
509 hashsize
= (unsigned int) Section
->SizeOfRawData
;
510 check_size(data
, datasize_in
, hashbase
, hashsize
);
512 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
513 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
514 perror(L
"Unable to generate hash\n");
515 efi_status
= EFI_OUT_OF_RESOURCES
;
518 SumOfBytesHashed
+= Section
->SizeOfRawData
;
521 /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */
522 if (datasize
> SumOfBytesHashed
&& context
->SecDir
->Size
) {
523 hashbase
= data
+ SumOfBytesHashed
;
524 hashsize
= datasize
- context
->SecDir
->Size
- SumOfBytesHashed
;
526 if ((datasize
- SumOfBytesHashed
< context
->SecDir
->Size
) ||
527 (SumOfBytesHashed
+ hashsize
!= context
->SecDir
->VirtualAddress
)) {
528 perror(L
"Malformed binary after Attribute Certificate Table\n");
529 console_print(L
"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n",
530 datasize
, SumOfBytesHashed
, context
->SecDir
->Size
);
531 console_print(L
"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n",
532 hashsize
, context
->SecDir
->VirtualAddress
);
533 efi_status
= EFI_INVALID_PARAMETER
;
536 check_size(data
, datasize_in
, hashbase
, hashsize
);
538 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
539 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
540 perror(L
"Unable to generate hash\n");
541 efi_status
= EFI_OUT_OF_RESOURCES
;
547 #else // we have to migrate to doing this later :/
548 SumOfBytesHashed
+= hashsize
;
551 /* Hash all remaining data */
552 if (datasize
> SumOfBytesHashed
) {
553 hashbase
= data
+ SumOfBytesHashed
;
554 hashsize
= datasize
- SumOfBytesHashed
;
556 check_size(data
, datasize_in
, hashbase
, hashsize
);
558 if (!(Sha256Update(sha256ctx
, hashbase
, hashsize
)) ||
559 !(Sha1Update(sha1ctx
, hashbase
, hashsize
))) {
560 perror(L
"Unable to generate hash\n");
561 efi_status
= EFI_OUT_OF_RESOURCES
;
565 SumOfBytesHashed
+= hashsize
;
569 if (!(Sha256Final(sha256ctx
, sha256hash
)) ||
570 !(Sha1Final(sha1ctx
, sha1hash
))) {
571 perror(L
"Unable to finalise hash\n");
572 efi_status
= EFI_OUT_OF_RESOURCES
;
576 dprint(L
"sha1 authenticode hash:\n");
577 dhexdumpat(sha1hash
, SHA1_DIGEST_SIZE
, 0);
578 dprint(L
"sha256 authenticode hash:\n");
579 dhexdumpat(sha256hash
, SHA256_DIGEST_SIZE
, 0);
583 FreePool(SectionHeader
);
593 * i686 x86_64 aarch64
594 * 64-on-64: nyet yes yes
595 * 64-on-32: nyet yes nyet
596 * 32-on-32: yes yes no
601 #if defined(__x86_64__) || defined(__aarch64__)
603 #elif defined(__i386__) || defined(__i686__)
604 /* Right now blindly assuming the kernel will correctly detect this
605 * and /halt the system/ if you're not really on a 64-bit cpu */
609 #else /* assuming everything else is 32-bit... */
617 #if defined(__x86_64__)
618 #if defined(ALLOW_32BIT_KERNEL_ON_X64)
625 #elif defined(__i386__) || defined(__i686__)
627 #elif defined(__aarch64__)
629 #else /* assuming everything else is 32-bit... */
635 image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
)
637 /* .Magic is the same offset in all cases */
638 if (PEHdr
->Pe32Plus
.OptionalHeader
.Magic
639 == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
)
644 static const UINT16 machine_type
=
645 #if defined(__x86_64__)
646 IMAGE_FILE_MACHINE_X64
;
647 #elif defined(__aarch64__)
648 IMAGE_FILE_MACHINE_ARM64
;
649 #elif defined(__arm__)
650 IMAGE_FILE_MACHINE_ARMTHUMB_MIXED
;
651 #elif defined(__i386__) || defined(__i486__) || defined(__i686__)
652 IMAGE_FILE_MACHINE_I386
;
653 #elif defined(__ia64__)
654 IMAGE_FILE_MACHINE_IA64
;
656 #error this architecture is not supported by shim
660 image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
)
662 /* If the machine type doesn't match the binary, bail, unless
663 * we're in an allowed 64-on-32 scenario */
664 if (PEHdr
->Pe32
.FileHeader
.Machine
!= machine_type
) {
665 if (!(machine_type
== IMAGE_FILE_MACHINE_I386
&&
666 PEHdr
->Pe32
.FileHeader
.Machine
== IMAGE_FILE_MACHINE_X64
&&
672 /* If it's not a header type we recognize at all, bail */
673 switch (PEHdr
->Pe32Plus
.OptionalHeader
.Magic
) {
674 case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
675 case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
681 /* and now just check for general 64-vs-32 compatibility */
682 if (image_is_64_bit(PEHdr
)) {
693 * Read the binary header and grab appropriate information from it
696 read_header(void *data
, unsigned int datasize
,
697 PE_COFF_LOADER_IMAGE_CONTEXT
*context
)
699 EFI_IMAGE_DOS_HEADER
*DosHdr
= data
;
700 EFI_IMAGE_OPTIONAL_HEADER_UNION
*PEHdr
= data
;
701 unsigned long HeaderWithoutDataDir
, SectionHeaderOffset
, OptHeaderSize
;
702 unsigned long FileAlignment
= 0;
704 if (datasize
< sizeof (PEHdr
->Pe32
)) {
705 perror(L
"Invalid image\n");
706 return EFI_UNSUPPORTED
;
709 if (DosHdr
->e_magic
== EFI_IMAGE_DOS_SIGNATURE
)
710 PEHdr
= (EFI_IMAGE_OPTIONAL_HEADER_UNION
*)((char *)data
+ DosHdr
->e_lfanew
);
712 if (!image_is_loadable(PEHdr
)) {
713 perror(L
"Platform does not support this image\n");
714 return EFI_UNSUPPORTED
;
717 if (image_is_64_bit(PEHdr
)) {
718 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32Plus
.OptionalHeader
.NumberOfRvaAndSizes
;
719 context
->SizeOfHeaders
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfHeaders
;
720 context
->ImageSize
= PEHdr
->Pe32Plus
.OptionalHeader
.SizeOfImage
;
721 context
->SectionAlignment
= PEHdr
->Pe32Plus
.OptionalHeader
.SectionAlignment
;
722 FileAlignment
= PEHdr
->Pe32Plus
.OptionalHeader
.FileAlignment
;
723 OptHeaderSize
= sizeof(EFI_IMAGE_OPTIONAL_HEADER64
);
725 context
->NumberOfRvaAndSizes
= PEHdr
->Pe32
.OptionalHeader
.NumberOfRvaAndSizes
;
726 context
->SizeOfHeaders
= PEHdr
->Pe32
.OptionalHeader
.SizeOfHeaders
;
727 context
->ImageSize
= (UINT64
)PEHdr
->Pe32
.OptionalHeader
.SizeOfImage
;
728 context
->SectionAlignment
= PEHdr
->Pe32
.OptionalHeader
.SectionAlignment
;
729 FileAlignment
= PEHdr
->Pe32
.OptionalHeader
.FileAlignment
;
730 OptHeaderSize
= sizeof(EFI_IMAGE_OPTIONAL_HEADER32
);
733 if (FileAlignment
% 2 != 0) {
734 perror(L
"File Alignment is invalid (%d)\n", FileAlignment
);
735 return EFI_UNSUPPORTED
;
737 if (FileAlignment
== 0)
738 FileAlignment
= 0x200;
739 if (context
->SectionAlignment
== 0)
740 context
->SectionAlignment
= PAGE_SIZE
;
741 if (context
->SectionAlignment
< FileAlignment
)
742 context
->SectionAlignment
= FileAlignment
;
744 context
->NumberOfSections
= PEHdr
->Pe32
.FileHeader
.NumberOfSections
;
746 if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES
< context
->NumberOfRvaAndSizes
) {
747 perror(L
"Image header too small\n");
748 return EFI_UNSUPPORTED
;
751 HeaderWithoutDataDir
= OptHeaderSize
752 - sizeof (EFI_IMAGE_DATA_DIRECTORY
) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES
;
753 if (((UINT32
)PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
- HeaderWithoutDataDir
) !=
754 context
->NumberOfRvaAndSizes
* sizeof (EFI_IMAGE_DATA_DIRECTORY
)) {
755 perror(L
"Image header overflows data directory\n");
756 return EFI_UNSUPPORTED
;
759 SectionHeaderOffset
= DosHdr
->e_lfanew
761 + sizeof (EFI_IMAGE_FILE_HEADER
)
762 + PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
;
763 if (((UINT32
)context
->ImageSize
- SectionHeaderOffset
) / EFI_IMAGE_SIZEOF_SECTION_HEADER
764 <= context
->NumberOfSections
) {
765 perror(L
"Image sections overflow image size\n");
766 return EFI_UNSUPPORTED
;
769 if ((context
->SizeOfHeaders
- SectionHeaderOffset
) / EFI_IMAGE_SIZEOF_SECTION_HEADER
770 < (UINT32
)context
->NumberOfSections
) {
771 perror(L
"Image sections overflow section headers\n");
772 return EFI_UNSUPPORTED
;
775 if ((((UINT8
*)PEHdr
- (UINT8
*)data
) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION
)) > datasize
) {
776 perror(L
"Invalid image\n");
777 return EFI_UNSUPPORTED
;
780 if (PEHdr
->Te
.Signature
!= EFI_IMAGE_NT_SIGNATURE
) {
781 perror(L
"Unsupported image type\n");
782 return EFI_UNSUPPORTED
;
785 if (PEHdr
->Pe32
.FileHeader
.Characteristics
& EFI_IMAGE_FILE_RELOCS_STRIPPED
) {
786 perror(L
"Unsupported image - Relocations have been stripped\n");
787 return EFI_UNSUPPORTED
;
790 context
->PEHdr
= PEHdr
;
792 if (image_is_64_bit(PEHdr
)) {
793 context
->ImageAddress
= PEHdr
->Pe32Plus
.OptionalHeader
.ImageBase
;
794 context
->EntryPoint
= PEHdr
->Pe32Plus
.OptionalHeader
.AddressOfEntryPoint
;
795 context
->RelocDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
796 context
->SecDir
= &PEHdr
->Pe32Plus
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
798 context
->ImageAddress
= PEHdr
->Pe32
.OptionalHeader
.ImageBase
;
799 context
->EntryPoint
= PEHdr
->Pe32
.OptionalHeader
.AddressOfEntryPoint
;
800 context
->RelocDir
= &PEHdr
->Pe32
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
];
801 context
->SecDir
= &PEHdr
->Pe32
.OptionalHeader
.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY
];
804 context
->FirstSection
= (EFI_IMAGE_SECTION_HEADER
*)((char *)PEHdr
+ PEHdr
->Pe32
.FileHeader
.SizeOfOptionalHeader
+ sizeof(UINT32
) + sizeof(EFI_IMAGE_FILE_HEADER
));
806 if (context
->ImageSize
< context
->SizeOfHeaders
) {
807 perror(L
"Invalid image\n");
808 return EFI_UNSUPPORTED
;
811 if ((unsigned long)((UINT8
*)context
->SecDir
- (UINT8
*)data
) >
812 (datasize
- sizeof(EFI_IMAGE_DATA_DIRECTORY
))) {
813 perror(L
"Invalid image\n");
814 return EFI_UNSUPPORTED
;
817 if (context
->SecDir
->VirtualAddress
> datasize
||
818 (context
->SecDir
->VirtualAddress
== datasize
&&
819 context
->SecDir
->Size
> 0)) {
820 perror(L
"Malformed security header\n");
821 return EFI_INVALID_PARAMETER
;
827 * Once the image has been loaded it needs to be validated and relocated
830 handle_image (void *data
, unsigned int datasize
,
831 EFI_LOADED_IMAGE
*li
,
832 EFI_IMAGE_ENTRY_POINT
*entry_point
,
833 EFI_PHYSICAL_ADDRESS
*alloc_address
,
836 EFI_STATUS efi_status
;
839 EFI_IMAGE_SECTION_HEADER
*Section
;
841 PE_COFF_LOADER_IMAGE_CONTEXT context
;
842 unsigned int alignment
, alloc_size
;
843 int found_entry_point
= 0;
844 UINT8 sha1hash
[SHA1_DIGEST_SIZE
];
845 UINT8 sha256hash
[SHA256_DIGEST_SIZE
];
848 * The binary header contains relevant context and section pointers
850 efi_status
= read_header(data
, datasize
, &context
);
851 if (EFI_ERROR(efi_status
)) {
852 perror(L
"Failed to read header: %r\n", efi_status
);
857 * We only need to verify the binary if we're in secure mode
859 efi_status
= generate_hash(data
, datasize
, &context
, sha256hash
,
861 if (EFI_ERROR(efi_status
))
864 /* Measure the binary into the TPM */
868 tpm_log_pe((EFI_PHYSICAL_ADDRESS
)(UINTN
)data
, datasize
,
869 (EFI_PHYSICAL_ADDRESS
)(UINTN
)context
.ImageAddress
,
870 li
->FilePath
, sha1hash
, 4);
872 if (efi_status
!= EFI_SUCCESS
) {
877 /* The spec says, uselessly, of SectionAlignment:
879 * The alignment (in bytes) of sections when they are loaded into
880 * memory. It must be greater than or equal to FileAlignment. The
881 * default is the page size for the architecture.
883 * Which doesn't tell you whose responsibility it is to enforce the
884 * "default", or when. It implies that the value in the field must
885 * be > FileAlignment (also poorly defined), but it appears visual
886 * studio will happily write 512 for FileAlignment (its default) and
887 * 0 for SectionAlignment, intending to imply PAGE_SIZE.
889 * We only support one page size, so if it's zero, nerf it to 4096.
891 alignment
= context
.SectionAlignment
;
895 alloc_size
= ALIGN_VALUE(context
.ImageSize
+ context
.SectionAlignment
,
897 *alloc_pages
= alloc_size
/ PAGE_SIZE
;
899 efi_status
= gBS
->AllocatePages(AllocateAnyPages
, EfiLoaderCode
,
900 *alloc_pages
, alloc_address
);
901 if (EFI_ERROR(efi_status
)) {
902 perror(L
"Failed to allocate image buffer\n");
903 return EFI_OUT_OF_RESOURCES
;
906 buffer
= (void *)ALIGN_VALUE((unsigned long)*alloc_address
, alignment
);
908 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
910 *entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
912 perror(L
"Entry point is invalid\n");
913 gBS
->FreePages(*alloc_address
, *alloc_pages
);
914 return EFI_UNSUPPORTED
;
917 char *RelocBase
, *RelocBaseEnd
;
919 * These are relative virtual addresses, so we have to check them
920 * against the image size, not the data size.
922 RelocBase
= ImageAddress(buffer
, context
.ImageSize
,
923 context
.RelocDir
->VirtualAddress
);
925 * RelocBaseEnd here is the address of the last byte of the table
927 RelocBaseEnd
= ImageAddress(buffer
, context
.ImageSize
,
928 context
.RelocDir
->VirtualAddress
+
929 context
.RelocDir
->Size
- 1);
931 EFI_IMAGE_SECTION_HEADER
*RelocSection
= NULL
;
933 char *SBATBase
= NULL
;
937 * Copy the executable's sections to their desired offsets
939 Section
= context
.FirstSection
;
940 for (i
= 0; i
< context
.NumberOfSections
; i
++, Section
++) {
941 /* Don't try to copy discardable sections with zero size */
942 if ((Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) &&
943 !Section
->Misc
.VirtualSize
)
946 base
= ImageAddress (buffer
, context
.ImageSize
,
947 Section
->VirtualAddress
);
948 end
= ImageAddress (buffer
, context
.ImageSize
,
949 Section
->VirtualAddress
950 + Section
->Misc
.VirtualSize
- 1);
953 perror(L
"Section %d has negative size\n", i
);
954 gBS
->FreePages(*alloc_address
, *alloc_pages
);
955 return EFI_UNSUPPORTED
;
958 if (Section
->VirtualAddress
<= context
.EntryPoint
&&
959 (Section
->VirtualAddress
+ Section
->SizeOfRawData
- 1)
960 > context
.EntryPoint
)
963 /* We do want to process .reloc, but it's often marked
964 * discardable, so we don't want to memcpy it. */
965 if (CompareMem(Section
->Name
, ".reloc\0\0", 8) == 0) {
967 perror(L
"Image has multiple relocation sections\n");
968 return EFI_UNSUPPORTED
;
970 /* If it has nonzero sizes, and our bounds check
971 * made sense, and the VA and size match RelocDir's
972 * versions, then we believe in this section table. */
973 if (Section
->SizeOfRawData
&&
974 Section
->Misc
.VirtualSize
&&
977 RelocBaseEnd
== end
) {
978 RelocSection
= Section
;
980 } else if (CompareMem(Section
->Name
, ".sbat\0\0\0", 8) == 0) {
981 if (SBATBase
|| SBATSize
) {
982 perror(L
"Image has multiple resource sections\n");
983 return EFI_UNSUPPORTED
;
986 if (Section
->NumberOfRelocations
!= 0 ||
987 Section
->PointerToRelocations
!= 0) {
988 perror(L
"SBAT section has relocations\n");
989 return EFI_UNSUPPORTED
;
992 /* If it has nonzero size, and our bounds check made
993 * sense, sizes match, then we believe it's okay. */
994 if (Section
->SizeOfRawData
&&
995 Section
->SizeOfRawData
== Section
->Misc
.VirtualSize
&&
998 /* +1 because of size vs last byte location */
999 SBATSize
= end
- base
+ 1;
1003 if (Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) {
1008 perror(L
"Section %d has invalid base address\n", i
);
1009 return EFI_UNSUPPORTED
;
1012 perror(L
"Section %d has zero size\n", i
);
1013 return EFI_UNSUPPORTED
;
1016 if (!(Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) &&
1017 (Section
->VirtualAddress
< context
.SizeOfHeaders
||
1018 Section
->PointerToRawData
< context
.SizeOfHeaders
)) {
1019 perror(L
"Section %d is inside image headers\n", i
);
1020 return EFI_UNSUPPORTED
;
1023 if (Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) {
1024 ZeroMem(base
, Section
->Misc
.VirtualSize
);
1026 if (Section
->PointerToRawData
< context
.SizeOfHeaders
) {
1027 perror(L
"Section %d is inside image headers\n", i
);
1028 return EFI_UNSUPPORTED
;
1031 if (Section
->SizeOfRawData
> 0)
1032 CopyMem(base
, data
+ Section
->PointerToRawData
,
1033 Section
->SizeOfRawData
);
1035 if (Section
->SizeOfRawData
< Section
->Misc
.VirtualSize
)
1036 ZeroMem(base
+ Section
->SizeOfRawData
,
1037 Section
->Misc
.VirtualSize
- Section
->SizeOfRawData
);
1041 if (secure_mode ()) {
1044 struct sbat sbat
= { 0 };
1045 struct sbat_entry
*entry
= NULL
;
1047 if (SBATBase
&& SBATSize
) {
1051 sbat_size
= SBATSize
+ 1;
1052 sbat_data
= AllocatePool(sbat_size
);
1054 console_print(L
"Failed to allocate SBAT buffer\n");
1055 return EFI_OUT_OF_RESOURCES
;
1057 CopyMem(sbat_data
, SBATBase
, SBATSize
);
1058 sbat_data
[SBATSize
] = '\0';
1060 res
= parse_sbat(sbat_data
, sbat_size
, buffer
, &sbat
);
1062 console_print(L
"SBAT data not correct: %r\n", res
);
1063 return EFI_UNSUPPORTED
;
1066 dprint(L
"SBAT data\n");
1067 for (i
= 0; i
< sbat
.size
; i
++) {
1068 entry
= sbat
.entries
[i
];
1069 dprint(L
"%a, %a, %a, %a, %a, %a\n",
1070 entry
->component_name
,
1071 entry
->component_generation
,
1073 entry
->vendor_package_name
,
1074 entry
->vendor_version
,
1078 perror(L
"SBAT data not found\n");
1079 return EFI_UNSUPPORTED
;
1082 efi_status
= verify_buffer(data
, datasize
,
1083 &context
, sha256hash
, sha1hash
);
1086 for (i
= 0; i
< sbat
.size
; i
++)
1087 FreePool(sbat
.entries
[i
]);
1089 if (EFI_ERROR(efi_status
)) {
1091 console_print(L
"Verification failed: %r\n", efi_status
);
1093 console_error(L
"Verification failed", efi_status
);
1097 console_print(L
"Verification succeeded\n");
1101 if (context
.NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
1102 perror(L
"Image has no relocation entry\n");
1104 return EFI_UNSUPPORTED
;
1107 if (context
.RelocDir
->Size
&& RelocSection
) {
1109 * Run the relocation fixups
1111 efi_status
= relocate_coff(&context
, RelocSection
, data
,
1114 if (EFI_ERROR(efi_status
)) {
1115 perror(L
"Relocation failed: %r\n", efi_status
);
1122 * grub needs to know its location and size in memory, so fix up
1123 * the loaded image protocol values
1125 li
->ImageBase
= buffer
;
1126 li
->ImageSize
= context
.ImageSize
;
1128 /* Pass the load options to the second stage loader */
1129 if ( load_options
) {
1130 li
->LoadOptions
= load_options
;
1131 li
->LoadOptionsSize
= load_options_size
;
1134 if (!found_entry_point
) {
1135 perror(L
"Entry point is not within sections\n");
1136 return EFI_UNSUPPORTED
;
1138 if (found_entry_point
> 1) {
1139 perror(L
"%d sections contain entry point\n");
1140 return EFI_UNSUPPORTED
;
1146 // vim:fenc=utf-8:tw=75:noet