mirror of
https://git.proxmox.com/git/efi-boot-shim
synced 2025-07-27 11:03:07 +00:00
shim: make the PE loader less overzealous on rejections
This commit is contained in:
parent
08ede98fbc
commit
a8b6d058f0
@ -306,35 +306,35 @@ typedef struct {
|
||||
/// Size of EFI_IMAGE_SECTION_HEADER.
|
||||
///
|
||||
#define EFI_IMAGE_SIZEOF_SECTION_HEADER 40
|
||||
|
||||
|
||||
//
|
||||
// Section Flags Values
|
||||
//
|
||||
#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved.
|
||||
#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020
|
||||
#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040
|
||||
#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080
|
||||
|
||||
#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved.
|
||||
#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information.
|
||||
#define EFI_IMAGE_SCN_LNK_REMOVE BIT11 ///< 0x00000800 ///< Section contents will not become part of image.
|
||||
#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000
|
||||
|
||||
#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000
|
||||
#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000
|
||||
#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000
|
||||
#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000
|
||||
#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000
|
||||
#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000
|
||||
#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000
|
||||
|
||||
#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000
|
||||
#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000
|
||||
#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000
|
||||
#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000
|
||||
#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000
|
||||
#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000
|
||||
#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000
|
||||
#define EFI_IMAGE_SCN_TYPE_NO_PAD 0x00000008 ///< Reserved.
|
||||
#define EFI_IMAGE_SCN_CNT_CODE 0x00000020
|
||||
#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
|
||||
#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
|
||||
|
||||
#define EFI_IMAGE_SCN_LNK_OTHER 0x00000100 ///< Reserved.
|
||||
#define EFI_IMAGE_SCN_LNK_INFO 0x00000200 ///< Section contains comments or some other type of information.
|
||||
#define EFI_IMAGE_SCN_LNK_REMOVE 0x00000800 ///< Section contents will not become part of image.
|
||||
#define EFI_IMAGE_SCN_LNK_COMDAT 0x00001000
|
||||
|
||||
#define EFI_IMAGE_SCN_ALIGN_1BYTES 0x00100000
|
||||
#define EFI_IMAGE_SCN_ALIGN_2BYTES 0x00200000
|
||||
#define EFI_IMAGE_SCN_ALIGN_4BYTES 0x00300000
|
||||
#define EFI_IMAGE_SCN_ALIGN_8BYTES 0x00400000
|
||||
#define EFI_IMAGE_SCN_ALIGN_16BYTES 0x00500000
|
||||
#define EFI_IMAGE_SCN_ALIGN_32BYTES 0x00600000
|
||||
#define EFI_IMAGE_SCN_ALIGN_64BYTES 0x00700000
|
||||
|
||||
#define EFI_IMAGE_SCN_MEM_DISCARDABLE 0x02000000
|
||||
#define EFI_IMAGE_SCN_MEM_NOT_CACHED 0x04000000
|
||||
#define EFI_IMAGE_SCN_MEM_NOT_PAGED 0x08000000
|
||||
#define EFI_IMAGE_SCN_MEM_SHARED 0x10000000
|
||||
#define EFI_IMAGE_SCN_MEM_EXECUTE 0x20000000
|
||||
#define EFI_IMAGE_SCN_MEM_READ 0x40000000
|
||||
#define EFI_IMAGE_SCN_MEM_WRITE 0x80000000
|
||||
|
||||
///
|
||||
/// Size of a Symbol Table Record.
|
||||
|
94
shim.c
94
shim.c
@ -1122,6 +1122,8 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
EFI_IMAGE_SECTION_HEADER *Section;
|
||||
char *base, *end;
|
||||
PE_COFF_LOADER_IMAGE_CONTEXT context;
|
||||
unsigned int alignment;
|
||||
int found_entry_point = 0;
|
||||
|
||||
/*
|
||||
* The binary header contains relevant context and section pointers
|
||||
@ -1147,8 +1149,26 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
}
|
||||
}
|
||||
|
||||
/* The spec says, uselessly, of SectionAlignment:
|
||||
* =====
|
||||
* The alignment (in bytes) of sections when they are loaded into
|
||||
* memory. It must be greater than or equal to FileAlignment. The
|
||||
* default is the page size for the architecture.
|
||||
* =====
|
||||
* Which doesn't tell you whose responsibility it is to enforce the
|
||||
* "default", or when. It implies that the value in the field must
|
||||
* be > FileAlignment (also poorly defined), but it appears visual
|
||||
* studio will happily write 512 for FileAlignment (its default) and
|
||||
* 0 for SectionAlignment, intending to imply PAGE_SIZE.
|
||||
*
|
||||
* We only support one page size, so if it's zero, nerf it to 4096.
|
||||
*/
|
||||
alignment = context.SectionAlignment;
|
||||
if (!alignment)
|
||||
alignment = 4096;
|
||||
|
||||
buffer = AllocatePool(context.ImageSize + context.SectionAlignment);
|
||||
buffer = ALIGN_POINTER(buffer, context.SectionAlignment);
|
||||
buffer = ALIGN_POINTER(buffer, alignment);
|
||||
|
||||
if (!buffer) {
|
||||
perror(L"Failed to allocate image buffer\n");
|
||||
@ -1157,11 +1177,25 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
|
||||
CopyMem(buffer, data, context.SizeOfHeaders);
|
||||
|
||||
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
|
||||
if (!entry_point) {
|
||||
perror(L"Entry point is invalid\n");
|
||||
FreePool(buffer);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
||||
char *RelocBase, *RelocBaseEnd;
|
||||
RelocBase = ImageAddress(buffer, datasize,
|
||||
/*
|
||||
* These are relative virtual addresses, so we have to check them
|
||||
* against the image size, not the data size.
|
||||
*/
|
||||
RelocBase = ImageAddress(buffer, context.ImageSize,
|
||||
context.RelocDir->VirtualAddress);
|
||||
/* RelocBaseEnd here is the address of the last byte of the table */
|
||||
RelocBaseEnd = ImageAddress(buffer, datasize,
|
||||
/*
|
||||
* RelocBaseEnd here is the address of the last byte of the table
|
||||
*/
|
||||
RelocBaseEnd = ImageAddress(buffer, context.ImageSize,
|
||||
context.RelocDir->VirtualAddress +
|
||||
context.RelocDir->Size - 1);
|
||||
|
||||
@ -1172,13 +1206,22 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
*/
|
||||
Section = context.FirstSection;
|
||||
for (i = 0; i < context.NumberOfSections; i++, Section++) {
|
||||
size = Section->Misc.VirtualSize;
|
||||
base = ImageAddress (buffer, context.ImageSize,
|
||||
Section->VirtualAddress);
|
||||
end = ImageAddress (buffer, context.ImageSize,
|
||||
Section->VirtualAddress
|
||||
+ Section->Misc.VirtualSize - 1);
|
||||
|
||||
if (size > Section->SizeOfRawData)
|
||||
size = Section->SizeOfRawData;
|
||||
if (end < base) {
|
||||
perror(L"Section %d has negative size\n", i);
|
||||
FreePool(buffer);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
base = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress);
|
||||
end = ImageAddress (buffer, context.ImageSize, Section->VirtualAddress + size - 1);
|
||||
if (Section->VirtualAddress <= context.EntryPoint &&
|
||||
(Section->VirtualAddress + Section->SizeOfRawData - 1)
|
||||
> context.EntryPoint)
|
||||
found_entry_point++;
|
||||
|
||||
/* We do want to process .reloc, but it's often marked
|
||||
* discardable, so we don't want to memcpy it. */
|
||||
@ -1199,27 +1242,32 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
}
|
||||
}
|
||||
|
||||
if (Section->Characteristics & 0x02000000) {
|
||||
/* section has EFI_IMAGE_SCN_MEM_DISCARDABLE attr set */
|
||||
if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!base || !end) {
|
||||
perror(L"Section %d has invalid size\n", i);
|
||||
if (!base) {
|
||||
perror(L"Section %d has invalid base address\n", i);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
if (!end) {
|
||||
perror(L"Section %d has zero size\n", i);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (Section->VirtualAddress < context.SizeOfHeaders ||
|
||||
Section->PointerToRawData < context.SizeOfHeaders) {
|
||||
if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) &&
|
||||
(Section->VirtualAddress < context.SizeOfHeaders ||
|
||||
Section->PointerToRawData < context.SizeOfHeaders)) {
|
||||
perror(L"Section %d is inside image headers\n", i);
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
if (Section->SizeOfRawData > 0)
|
||||
CopyMem(base, data + Section->PointerToRawData, size);
|
||||
CopyMem(base, data + Section->PointerToRawData, Section->SizeOfRawData);
|
||||
|
||||
if (size < Section->Misc.VirtualSize)
|
||||
ZeroMem (base + size, Section->Misc.VirtualSize - size);
|
||||
if (Section->SizeOfRawData < Section->Misc.VirtualSize)
|
||||
ZeroMem (base + Section->SizeOfRawData,
|
||||
Section->Misc.VirtualSize - Section->SizeOfRawData);
|
||||
}
|
||||
|
||||
if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
|
||||
@ -1242,7 +1290,6 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
}
|
||||
}
|
||||
|
||||
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
|
||||
/*
|
||||
* grub needs to know its location and size in memory, so fix up
|
||||
* the loaded image protocol values
|
||||
@ -1254,9 +1301,12 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
|
||||
li->LoadOptions = load_options;
|
||||
li->LoadOptionsSize = load_options_size;
|
||||
|
||||
if (!entry_point) {
|
||||
perror(L"Invalid entry point\n");
|
||||
FreePool(buffer);
|
||||
if (!found_entry_point) {
|
||||
perror(L"Entry point is not within sections\n");
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
if (found_entry_point > 1) {
|
||||
perror(L"%d sections contain entry point\n");
|
||||
return EFI_UNSUPPORTED;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user