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 handle_sbat(char *SBATBase
, size_t SBATSize
)
830 EFI_STATUS efi_status
;
832 struct sbat_entry
**entries
= NULL
;
836 if (list_empty(&sbat_var
))
839 if (SBATBase
== NULL
|| SBATSize
== 0) {
840 dprint(L
"No .sbat section data\n");
841 return EFI_SECURITY_VIOLATION
;
844 sbat_size
= SBATSize
+ 1;
845 sbat_data
= AllocatePool(sbat_size
);
847 console_print(L
"Failed to allocate .sbat section buffer\n");
848 return EFI_OUT_OF_RESOURCES
;
850 CopyMem(sbat_data
, SBATBase
, SBATSize
);
851 sbat_data
[SBATSize
] = '\0';
853 efi_status
= parse_sbat(sbat_data
, sbat_size
, &n
, &entries
);
854 if (EFI_ERROR(efi_status
)) {
855 perror(L
"Could not parse .sbat section data: %r\n", efi_status
);
859 dprint(L
"SBAT section data\n");
860 for (i
= 0; i
< n
; i
++) {
861 dprint(L
"%a, %a, %a, %a, %a, %a\n",
862 entries
[i
]->component_name
,
863 entries
[i
]->component_generation
,
864 entries
[i
]->vendor_name
,
865 entries
[i
]->vendor_package_name
,
866 entries
[i
]->vendor_version
,
867 entries
[i
]->vendor_url
);
870 efi_status
= verify_sbat(n
, entries
);
872 cleanup_sbat_entries(n
, entries
);
881 * Once the image has been loaded it needs to be validated and relocated
884 handle_image (void *data
, unsigned int datasize
,
885 EFI_LOADED_IMAGE
*li
,
886 EFI_IMAGE_ENTRY_POINT
*entry_point
,
887 EFI_PHYSICAL_ADDRESS
*alloc_address
,
890 EFI_STATUS efi_status
;
893 EFI_IMAGE_SECTION_HEADER
*Section
;
895 PE_COFF_LOADER_IMAGE_CONTEXT context
;
896 unsigned int alignment
, alloc_size
;
897 int found_entry_point
= 0;
898 UINT8 sha1hash
[SHA1_DIGEST_SIZE
];
899 UINT8 sha256hash
[SHA256_DIGEST_SIZE
];
902 * The binary header contains relevant context and section pointers
904 efi_status
= read_header(data
, datasize
, &context
);
905 if (EFI_ERROR(efi_status
)) {
906 perror(L
"Failed to read header: %r\n", efi_status
);
911 * We only need to verify the binary if we're in secure mode
913 efi_status
= generate_hash(data
, datasize
, &context
, sha256hash
,
915 if (EFI_ERROR(efi_status
))
918 /* Measure the binary into the TPM */
922 tpm_log_pe((EFI_PHYSICAL_ADDRESS
)(UINTN
)data
, datasize
,
923 (EFI_PHYSICAL_ADDRESS
)(UINTN
)context
.ImageAddress
,
924 li
->FilePath
, sha1hash
, 4);
926 if (efi_status
!= EFI_SUCCESS
) {
931 /* The spec says, uselessly, of SectionAlignment:
933 * The alignment (in bytes) of sections when they are loaded into
934 * memory. It must be greater than or equal to FileAlignment. The
935 * default is the page size for the architecture.
937 * Which doesn't tell you whose responsibility it is to enforce the
938 * "default", or when. It implies that the value in the field must
939 * be > FileAlignment (also poorly defined), but it appears visual
940 * studio will happily write 512 for FileAlignment (its default) and
941 * 0 for SectionAlignment, intending to imply PAGE_SIZE.
943 * We only support one page size, so if it's zero, nerf it to 4096.
945 alignment
= context
.SectionAlignment
;
949 alloc_size
= ALIGN_VALUE(context
.ImageSize
+ context
.SectionAlignment
,
951 *alloc_pages
= alloc_size
/ PAGE_SIZE
;
953 efi_status
= gBS
->AllocatePages(AllocateAnyPages
, EfiLoaderCode
,
954 *alloc_pages
, alloc_address
);
955 if (EFI_ERROR(efi_status
)) {
956 perror(L
"Failed to allocate image buffer\n");
957 return EFI_OUT_OF_RESOURCES
;
960 buffer
= (void *)ALIGN_VALUE((unsigned long)*alloc_address
, alignment
);
962 CopyMem(buffer
, data
, context
.SizeOfHeaders
);
964 *entry_point
= ImageAddress(buffer
, context
.ImageSize
, context
.EntryPoint
);
966 perror(L
"Entry point is invalid\n");
967 gBS
->FreePages(*alloc_address
, *alloc_pages
);
968 return EFI_UNSUPPORTED
;
971 char *RelocBase
, *RelocBaseEnd
;
973 * These are relative virtual addresses, so we have to check them
974 * against the image size, not the data size.
976 RelocBase
= ImageAddress(buffer
, context
.ImageSize
,
977 context
.RelocDir
->VirtualAddress
);
979 * RelocBaseEnd here is the address of the last byte of the table
981 RelocBaseEnd
= ImageAddress(buffer
, context
.ImageSize
,
982 context
.RelocDir
->VirtualAddress
+
983 context
.RelocDir
->Size
- 1);
985 EFI_IMAGE_SECTION_HEADER
*RelocSection
= NULL
;
987 char *SBATBase
= NULL
;
991 * Copy the executable's sections to their desired offsets
993 Section
= context
.FirstSection
;
994 for (i
= 0; i
< context
.NumberOfSections
; i
++, Section
++) {
995 /* Don't try to copy discardable sections with zero size */
996 if ((Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) &&
997 !Section
->Misc
.VirtualSize
)
1000 base
= ImageAddress (buffer
, context
.ImageSize
,
1001 Section
->VirtualAddress
);
1002 end
= ImageAddress (buffer
, context
.ImageSize
,
1003 Section
->VirtualAddress
1004 + Section
->Misc
.VirtualSize
- 1);
1007 perror(L
"Section %d has negative size\n", i
);
1008 gBS
->FreePages(*alloc_address
, *alloc_pages
);
1009 return EFI_UNSUPPORTED
;
1012 if (Section
->VirtualAddress
<= context
.EntryPoint
&&
1013 (Section
->VirtualAddress
+ Section
->SizeOfRawData
- 1)
1014 > context
.EntryPoint
)
1015 found_entry_point
++;
1017 /* We do want to process .reloc, but it's often marked
1018 * discardable, so we don't want to memcpy it. */
1019 if (CompareMem(Section
->Name
, ".reloc\0\0", 8) == 0) {
1021 perror(L
"Image has multiple relocation sections\n");
1022 return EFI_UNSUPPORTED
;
1024 /* If it has nonzero sizes, and our bounds check
1025 * made sense, and the VA and size match RelocDir's
1026 * versions, then we believe in this section table. */
1027 if (Section
->SizeOfRawData
&&
1028 Section
->Misc
.VirtualSize
&&
1030 RelocBase
== base
&&
1031 RelocBaseEnd
== end
) {
1032 RelocSection
= Section
;
1034 } else if (CompareMem(Section
->Name
, ".sbat\0\0\0", 8) == 0) {
1035 if (SBATBase
|| SBATSize
) {
1036 perror(L
"Image has multiple resource sections\n");
1037 return EFI_UNSUPPORTED
;
1040 if (Section
->NumberOfRelocations
!= 0 ||
1041 Section
->PointerToRelocations
!= 0) {
1042 perror(L
"SBAT section has relocations\n");
1043 return EFI_UNSUPPORTED
;
1046 /* If it has nonzero size, and our bounds check made
1047 * sense, sizes match, then we believe it's okay. */
1048 if (Section
->SizeOfRawData
&&
1049 Section
->SizeOfRawData
== Section
->Misc
.VirtualSize
&&
1052 /* +1 because of size vs last byte location */
1053 SBATSize
= end
- base
+ 1;
1057 if (Section
->Characteristics
& EFI_IMAGE_SCN_MEM_DISCARDABLE
) {
1062 perror(L
"Section %d has invalid base address\n", i
);
1063 return EFI_UNSUPPORTED
;
1066 perror(L
"Section %d has zero size\n", i
);
1067 return EFI_UNSUPPORTED
;
1070 if (!(Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) &&
1071 (Section
->VirtualAddress
< context
.SizeOfHeaders
||
1072 Section
->PointerToRawData
< context
.SizeOfHeaders
)) {
1073 perror(L
"Section %d is inside image headers\n", i
);
1074 return EFI_UNSUPPORTED
;
1077 if (Section
->Characteristics
& EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA
) {
1078 ZeroMem(base
, Section
->Misc
.VirtualSize
);
1080 if (Section
->PointerToRawData
< context
.SizeOfHeaders
) {
1081 perror(L
"Section %d is inside image headers\n", i
);
1082 return EFI_UNSUPPORTED
;
1085 if (Section
->SizeOfRawData
> 0)
1086 CopyMem(base
, data
+ Section
->PointerToRawData
,
1087 Section
->SizeOfRawData
);
1089 if (Section
->SizeOfRawData
< Section
->Misc
.VirtualSize
)
1090 ZeroMem(base
+ Section
->SizeOfRawData
,
1091 Section
->Misc
.VirtualSize
- Section
->SizeOfRawData
);
1095 if (secure_mode ()) {
1096 efi_status
= handle_sbat(SBATBase
, SBATSize
);
1098 if (!EFI_ERROR(efi_status
))
1099 efi_status
= verify_buffer(data
, datasize
,
1100 &context
, sha256hash
, sha1hash
);
1102 if (EFI_ERROR(efi_status
)) {
1104 console_print(L
"Verification failed: %r\n", efi_status
);
1106 console_error(L
"Verification failed", efi_status
);
1110 console_print(L
"Verification succeeded\n");
1114 if (context
.NumberOfRvaAndSizes
<= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC
) {
1115 perror(L
"Image has no relocation entry\n");
1117 return EFI_UNSUPPORTED
;
1120 if (context
.RelocDir
->Size
&& RelocSection
) {
1122 * Run the relocation fixups
1124 efi_status
= relocate_coff(&context
, RelocSection
, data
,
1127 if (EFI_ERROR(efi_status
)) {
1128 perror(L
"Relocation failed: %r\n", efi_status
);
1135 * grub needs to know its location and size in memory, so fix up
1136 * the loaded image protocol values
1138 li
->ImageBase
= buffer
;
1139 li
->ImageSize
= context
.ImageSize
;
1141 /* Pass the load options to the second stage loader */
1142 if ( load_options
) {
1143 li
->LoadOptions
= load_options
;
1144 li
->LoadOptionsSize
= load_options_size
;
1147 if (!found_entry_point
) {
1148 perror(L
"Entry point is not within sections\n");
1149 return EFI_UNSUPPORTED
;
1151 if (found_entry_point
> 1) {
1152 perror(L
"%d sections contain entry point\n");
1153 return EFI_UNSUPPORTED
;
1159 // vim:fenc=utf-8:tw=75:noet