Skip to content

Commit 9655ce3

Browse files
committed
loader-protocol: add workaround for EDK2 2025.02 page fault on FreePages
EDK2 since version 2025.02 introduced the EFI memory attribute protocol: tianocore/edk2@efaa102 This is used by shim to unset the writable bit and set the executable bit on images when they are loaded. EDK2 also (at least in the Debian/Ubuntu production builds) overwrites pages with a fixed 0xaf pattern when BS->FreePages() is called: https://github.com/tianocore/edk2/blob/399a40e5cba2ed70197ac61c8da9cf9805fb918e/MdePkg/Library/BaseDebugLibSerialPort/DebugLib.c#L256 https://github.com/tianocore/edk2/blob/399a40e5cba2ed70197ac61c8da9cf9805fb918e/MdePkg/Library/UefiDebugLibConOut/DebugLib.c#L256 https://github.com/tianocore/edk2/blob/399a40e5cba2ed70197ac61c8da9cf9805fb918e/MdePkg/Library/UefiDebugLibStdErr/DebugLib.c#L256 These two properties mix together as well as one may image: systemd-stub@0x72c64000 v999-bluca FSOpen: Open '\loader\addons' Success FSOpen: Open '\loader\addons\1.addon.efi' Success InstallProtocolInterface: 6E6BAEB8-7108-4179-949D-A3493415EC97 7D368D18 InstallProtocolInterface: 5B1B31A1-9562-11D2-8E3F-00A0C969723B 7D368D18 InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 7D376D18 !!!! X64 Exception Type - 0E(#PF - Page-Fault) CPU Apic ID - 00000000 !!!! ExceptionData - 0000000000000003 I:0 R:0 U:0 W:1 P:1 PK:0 SS:0 SGX:0 RIP - 000000007EF0931A, CS - 0000000000000038, RFLAGS - 0000000000010206 RAX - 00000000000000AF, RCX - 0000000000005000, RDX - 000000007D352000 RBX - 000000007D352000, RSP - 000000007EEEAAF0, RBP - 000000007EEEAB80 RSI - 000000007EF10F20, RDI - 000000007D353000 R8 - 00000000000000AF, R9 - 000000007D357FFF, R10 - 0000000072C7C2F7 R11 - 0000000001CBD9D0, R12 - 000000007D357FFF, R13 - 000000007D358000 R14 - 000000007D35B300, R15 - 000000007D357FFF DS - 0000000000000030, ES - 0000000000000030, FS - 0000000000000030 GS - 0000000000000030, SS - 0000000000000030 CR0 - 0000000080010033, CR2 - 000000007D353000, CR3 - 000000007EC01000 CR4 - 0000000000000668, CR8 - 0000000000000000 DR0 - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000 DR3 - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400 GDTR - 000000007E9E1000 0000000000000047, LDTR - 0000000000000000 IDTR - 000000007E407018 0000000000000FFF, TR - 0000000000000000 FXSAVE_STATE - 000000007EEEA750 !!!! Find image based on IP(0x7EF0931A) /home/bluca/git/edk2/Build/OvmfX64/DEBUG_GCC5/X64/MdeModulePkg/Core/Dxe/DxeMain/DEBUG/DxeCore.dll (ImageBase=000000007EEEC000, EntryPoint=000000007EF026FD) !!!! This issue can be reproduced by simply calling BS->UnloadImage() on any image that was loaded by shim's BS->LoadImage(). The systemd stub used in UKIs does so while loading and parsing addons. While this is arguably a bug in EDK2 (it should check that pages are writable before attempting to write them) and in the Debian/Ubuntu build (these debug modules should be swapped out for the BaseDebugLibNull module where this is a no-op: https://github.com/tianocore/edk2/blob/399a40e5cba2ed70197ac61c8da9cf9805fb918e/MdePkg/Library/BaseDebugLibNull/DebugLib.c#L137 ), this is how things work currently in at least two distribution stable releases, so add a workaround and set W+ X- before calling BS->FreePages(). Signed-off-by: Luca Boccassi <[email protected]>
1 parent 0e8459f commit 9655ce3

File tree

5 files changed

+36
-12
lines changed

5 files changed

+36
-12
lines changed

include/pe.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ handle_image (void *data, unsigned int datasize,
4141
EFI_LOADED_IMAGE *li,
4242
EFI_IMAGE_ENTRY_POINT *entry_point,
4343
EFI_PHYSICAL_ADDRESS *alloc_address,
44-
UINTN *alloc_pages);
44+
UINTN *alloc_pages, unsigned int *alloc_alignment);
4545

4646
EFI_STATUS
4747
generate_hash (char *data, unsigned int datasize,

loader-proto.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,28 @@ try_load_from_lf2(EFI_DEVICE_PATH *dp, buffer_properties_t *bprop)
163163
return status;
164164
}
165165

166+
static void
167+
free_pages_alloc_image(SHIM_LOADED_IMAGE *image)
168+
{
169+
char *buffer;
170+
171+
if (!image || !image->alloc_address || !image->alloc_pages)
172+
return;
173+
174+
/* EDKII overwrites memory pages with a fixed pattern in at least
175+
* production Debian/Ubuntu builds as of 2025.02. If the W- X+ bits
176+
* are set on a loaded image, this will cause a page fault when it
177+
* is freed. Ensure W+ X- are set instead before freeing. */
178+
179+
buffer = (void *)ALIGN_VALUE((unsigned long)image->alloc_address, image->alloc_alignment);
180+
update_mem_attrs((uintptr_t)buffer, image->alloc_pages * PAGE_SIZE, MEM_ATTR_R|MEM_ATTR_W,
181+
MEM_ATTR_X);
182+
183+
BS->FreePages(image->alloc_address, image->alloc_pages);
184+
image->alloc_address = 0;
185+
image->alloc_pages = 0;
186+
}
187+
166188
static EFI_STATUS EFIAPI
167189
shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
168190
EFI_DEVICE_PATH *DevicePath, VOID *SourceBuffer,
@@ -240,7 +262,7 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
240262
in_protocol = 1;
241263
efi_status = handle_image(SourceBuffer, SourceSize, &image->li,
242264
&image->entry_point, &image->alloc_address,
243-
&image->alloc_pages);
265+
&image->alloc_pages, &image->alloc_alignment);
244266
in_protocol = 0;
245267
if (EFI_ERROR(efi_status))
246268
goto free_image;
@@ -261,7 +283,7 @@ shim_load_image(BOOLEAN BootPolicy, EFI_HANDLE ParentImageHandle,
261283
return EFI_SUCCESS;
262284

263285
free_alloc:
264-
BS->FreePages(image->alloc_address, image->alloc_pages);
286+
free_pages_alloc_image(image);
265287
free_image:
266288
if (image->loaded_image_device_path)
267289
FreePool(image->loaded_image_device_path);
@@ -317,7 +339,7 @@ shim_start_image(IN EFI_HANDLE ImageHandle, OUT UINTN *ExitDataSize,
317339
image->loaded_image_device_path,
318340
NULL);
319341

320-
BS->FreePages(image->alloc_address, image->alloc_pages);
342+
free_pages_alloc_image(image);
321343
if (image->li.FilePath)
322344
BS->FreePool(image->li.FilePath);
323345
if (image->loaded_image_device_path)
@@ -339,7 +361,7 @@ shim_unload_image(EFI_HANDLE ImageHandle)
339361
if (efi_status == EFI_UNSUPPORTED)
340362
return system_unload_image(ImageHandle);
341363

342-
BS->FreePages(image->alloc_address, image->alloc_pages);
364+
free_pages_alloc_image(image);
343365
FreePool(image);
344366

345367
return EFI_SUCCESS;

pe.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ handle_image (void *data, unsigned int datasize,
465465
EFI_LOADED_IMAGE *li,
466466
EFI_IMAGE_ENTRY_POINT *entry_point,
467467
EFI_PHYSICAL_ADDRESS *alloc_address,
468-
UINTN *alloc_pages)
468+
UINTN *alloc_pages, unsigned int *alloc_alignment)
469469
{
470470
EFI_STATUS efi_status;
471471
char *buffer;
@@ -474,7 +474,7 @@ handle_image (void *data, unsigned int datasize,
474474
char *base, *end;
475475
UINT32 size;
476476
PE_COFF_LOADER_IMAGE_CONTEXT context;
477-
unsigned int alignment, alloc_size;
477+
unsigned int alloc_size;
478478
int found_entry_point = 0;
479479
UINT8 sha1hash[SHA1_DIGEST_SIZE];
480480
UINT8 sha256hash[SHA256_DIGEST_SIZE];
@@ -547,9 +547,9 @@ handle_image (void *data, unsigned int datasize,
547547
*
548548
* We only support one page size, so if it's zero, nerf it to 4096.
549549
*/
550-
alignment = context.SectionAlignment;
551-
if (!alignment)
552-
alignment = 4096;
550+
*alloc_alignment = context.SectionAlignment;
551+
if (!*alloc_alignment)
552+
*alloc_alignment = 4096;
553553

554554
alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment,
555555
PAGE_SIZE);
@@ -562,7 +562,7 @@ handle_image (void *data, unsigned int datasize,
562562
return EFI_OUT_OF_RESOURCES;
563563
}
564564

565-
buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment);
565+
buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, *alloc_alignment);
566566
dprint(L"Loading 0x%llx bytes at 0x%llx\n",
567567
(unsigned long long)context.ImageSize,
568568
(unsigned long long)(uintptr_t)buffer);

shim.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
11481148
CHAR16 *PathName = NULL;
11491149
void *data = NULL;
11501150
int datasize = 0;
1151+
unsigned int alloc_alignment;
11511152

11521153
efi_status = read_image(image_handle, ImagePath, &PathName, &data,
11531154
&datasize, 0);
@@ -1174,7 +1175,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
11741175
* Verify and, if appropriate, relocate and execute the executable
11751176
*/
11761177
efi_status = handle_image(data, datasize, shim_li, &entry_point,
1177-
&alloc_address, &alloc_pages);
1178+
&alloc_address, &alloc_pages, &alloc_alignment);
11781179
if (EFI_ERROR(efi_status)) {
11791180
perror(L"Failed to load image: %r\n", efi_status);
11801181
PrintErrors();

shim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ typedef struct {
330330
EFI_IMAGE_ENTRY_POINT entry_point;
331331
EFI_PHYSICAL_ADDRESS alloc_address;
332332
UINTN alloc_pages;
333+
unsigned int alloc_alignment;
333334
EFI_STATUS exit_status;
334335
CONST CHAR16 *exit_data;
335336
UINTN exit_data_size;

0 commit comments

Comments
 (0)