Skip to content

Commit cd81b47

Browse files
committed
[CHERRY-PICK] StandaloneMmPkg: Call PeCoffLoaderUnloadImage When Unloading Image
Today, StandaloneMmCore calls PeCoffLoaderRelocateImage() when loading images, which calls PeCoffLoaderRelocateImageExtraAction(). On AARCH64, this sets the image memory protections accordingly, RO + E on code sections, RW + NX on data sections. However, if an image fails to start (i.e. its entry point returns a failure) StandaloneMmCore does not call the corresponding PeCoffLoaderUnloadImage, which calls PeCoffLoaderUnloadImageExtraAction, which on AARCH64 undoes the memory protections on the image, setting the whole memory region back to RW + NX. The core then frees this memory and the next allocation attempts to use it, which results in a data abort if a read only memory region is attempted to be written to. Theoretically, other instances of the PeCoffExtraActionLib could take other actions and so regardless of architecture, the contract with the PeCoffLoader should be maintained. This patch calls PeCoffLoaderUnloadImage when an image's entry point returns a failure, before freeing the image memory. This meets the contract and follows the DXE core behavior. Signed-off-by: Oliver Smith-Denny <[email protected]>
1 parent 93dd0b8 commit cd81b47

File tree

1 file changed

+42
-28
lines changed

1 file changed

+42
-28
lines changed

StandaloneMmPkg/Core/Dispatcher.c

+42-28
Original file line numberDiff line numberDiff line change
@@ -101,40 +101,45 @@ BOOLEAN gRequestDispatch = FALSE;
101101
Loads an EFI image into SMRAM.
102102
103103
@param DriverEntry EFI_MM_DRIVER_ENTRY instance
104+
@param ImageContext Allocated ImageContext to be filled out by this function
104105
105106
@return EFI_STATUS
106107
107108
**/
108109
EFI_STATUS
109110
EFIAPI
110111
MmLoadImage (
111-
IN OUT EFI_MM_DRIVER_ENTRY *DriverEntry
112+
IN OUT EFI_MM_DRIVER_ENTRY *DriverEntry,
113+
IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext
112114
)
113115
{
114-
UINTN PageCount;
115-
EFI_STATUS Status;
116-
EFI_PHYSICAL_ADDRESS DstBuffer;
117-
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
116+
UINTN PageCount;
117+
EFI_STATUS Status;
118+
EFI_PHYSICAL_ADDRESS DstBuffer;
118119

119120
DEBUG ((DEBUG_INFO, "MmLoadImage - %g\n", &DriverEntry->FileName));
120121

122+
if (ImageContext == NULL) {
123+
return EFI_INVALID_PARAMETER;
124+
}
125+
121126
Status = EFI_SUCCESS;
122127

123128
//
124129
// Initialize ImageContext
125130
//
126-
ImageContext.Handle = DriverEntry->Pe32Data;
127-
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
131+
ImageContext->Handle = DriverEntry->Pe32Data;
132+
ImageContext->ImageRead = PeCoffLoaderImageReadFromMemory;
128133

129134
//
130135
// Get information about the image being loaded
131136
//
132-
Status = PeCoffLoaderGetImageInfo (&ImageContext);
137+
Status = PeCoffLoaderGetImageInfo (ImageContext);
133138
if (EFI_ERROR (Status)) {
134139
return Status;
135140
}
136141

137-
PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize + ImageContext.SectionAlignment);
142+
PageCount = (UINTN)EFI_SIZE_TO_PAGES ((UINTN)ImageContext->ImageSize + ImageContext->SectionAlignment);
138143
DstBuffer = (UINTN)(-1);
139144

140145
Status = MmAllocatePages (
@@ -147,18 +152,18 @@ MmLoadImage (
147152
return Status;
148153
}
149154

150-
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;
155+
ImageContext->ImageAddress = (EFI_PHYSICAL_ADDRESS)DstBuffer;
151156

152157
//
153158
// Align buffer on section boundary
154159
//
155-
ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
156-
ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext.SectionAlignment - 1));
160+
ImageContext->ImageAddress += ImageContext->SectionAlignment - 1;
161+
ImageContext->ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)(ImageContext->SectionAlignment - 1));
157162

158163
//
159164
// Load the image to our new buffer
160165
//
161-
Status = PeCoffLoaderLoadImage (&ImageContext);
166+
Status = PeCoffLoaderLoadImage (ImageContext);
162167
if (EFI_ERROR (Status)) {
163168
MmFreePages (DstBuffer, PageCount);
164169
return Status;
@@ -167,21 +172,23 @@ MmLoadImage (
167172
//
168173
// Relocate the image in our new buffer
169174
//
170-
Status = PeCoffLoaderRelocateImage (&ImageContext);
175+
Status = PeCoffLoaderRelocateImage (ImageContext);
171176
if (EFI_ERROR (Status)) {
177+
// if relocate fails, we don't need to call unload image here, as the extra action that may change page attributes
178+
// only is called on a successful return
172179
MmFreePages (DstBuffer, PageCount);
173180
return Status;
174181
}
175182

176183
//
177184
// Flush the instruction cache so the image data are written before we execute it
178185
//
179-
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
186+
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext->ImageAddress, (UINTN)ImageContext->ImageSize);
180187

181188
//
182189
// Save Image EntryPoint in DriverEntry
183190
//
184-
DriverEntry->ImageEntryPoint = ImageContext.EntryPoint;
191+
DriverEntry->ImageEntryPoint = ImageContext->EntryPoint;
185192
DriverEntry->ImageBuffer = DstBuffer;
186193
DriverEntry->NumberOfPage = PageCount;
187194

@@ -192,6 +199,7 @@ MmLoadImage (
192199
(VOID **)&DriverEntry->LoadedImage
193200
);
194201
if (EFI_ERROR (Status)) {
202+
PeCoffLoaderUnloadImage (ImageContext);
195203
MmFreePages (DstBuffer, PageCount);
196204
return Status;
197205
}
@@ -208,7 +216,7 @@ MmLoadImage (
208216
DriverEntry->LoadedImage->FilePath = NULL;
209217

210218
DriverEntry->LoadedImage->ImageBase = (VOID *)(UINTN)DriverEntry->ImageBuffer;
211-
DriverEntry->LoadedImage->ImageSize = ImageContext.ImageSize;
219+
DriverEntry->LoadedImage->ImageSize = ImageContext->ImageSize;
212220
DriverEntry->LoadedImage->ImageCodeType = EfiRuntimeServicesCode;
213221
DriverEntry->LoadedImage->ImageDataType = EfiRuntimeServicesData;
214222

@@ -236,18 +244,18 @@ MmLoadImage (
236244
DEBUG ((
237245
DEBUG_INFO | DEBUG_LOAD,
238246
"Loading MM driver at 0x%11p EntryPoint=0x%11p ",
239-
(VOID *)(UINTN)ImageContext.ImageAddress,
240-
FUNCTION_ENTRY_POINT (ImageContext.EntryPoint)
247+
(VOID *)(UINTN)ImageContext->ImageAddress,
248+
FUNCTION_ENTRY_POINT (ImageContext->EntryPoint)
241249
));
242250

243251
//
244252
// Print Module Name by Pdb file path.
245253
// Windows and Unix style file path are all trimmed correctly.
246254
//
247-
if (ImageContext.PdbPointer != NULL) {
255+
if (ImageContext->PdbPointer != NULL) {
248256
StartIndex = 0;
249-
for (Index = 0; ImageContext.PdbPointer[Index] != 0; Index++) {
250-
if ((ImageContext.PdbPointer[Index] == '\\') || (ImageContext.PdbPointer[Index] == '/')) {
257+
for (Index = 0; ImageContext->PdbPointer[Index] != 0; Index++) {
258+
if ((ImageContext->PdbPointer[Index] == '\\') || (ImageContext->PdbPointer[Index] == '/')) {
251259
StartIndex = Index + 1;
252260
}
253261
}
@@ -258,7 +266,7 @@ MmLoadImage (
258266
// If the length is bigger than 255, trim the redundant characters to avoid overflow in array boundary.
259267
//
260268
for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {
261-
EfiFileName[Index] = ImageContext.PdbPointer[Index + StartIndex];
269+
EfiFileName[Index] = ImageContext->PdbPointer[Index + StartIndex];
262270
if (EfiFileName[Index] == 0) {
263271
EfiFileName[Index] = '.';
264272
}
@@ -394,10 +402,11 @@ MmDispatcher (
394402
VOID
395403
)
396404
{
397-
EFI_STATUS Status;
398-
LIST_ENTRY *Link;
399-
EFI_MM_DRIVER_ENTRY *DriverEntry;
400-
BOOLEAN ReadyToRun;
405+
EFI_STATUS Status;
406+
LIST_ENTRY *Link;
407+
EFI_MM_DRIVER_ENTRY *DriverEntry;
408+
BOOLEAN ReadyToRun;
409+
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
401410

402411
DEBUG ((DEBUG_INFO, "MmDispatcher\n"));
403412

@@ -436,7 +445,7 @@ MmDispatcher (
436445
// skip the LoadImage
437446
//
438447
if (DriverEntry->ImageHandle == NULL) {
439-
Status = MmLoadImage (DriverEntry);
448+
Status = MmLoadImage (DriverEntry, &ImageContext);
440449

441450
//
442451
// Update the driver state to reflect that it's been loaded
@@ -477,6 +486,11 @@ MmDispatcher (
477486

478487
if (EFI_ERROR (Status)) {
479488
DEBUG ((DEBUG_INFO, "StartImage Status - %r\n", Status));
489+
490+
// we need to unload the image before we free the pages. On some architectures (e.g. x86), this is a no-op, but
491+
// on others (e.g. AARCH64) this will remove the image memory protections set on the region so that when the
492+
// memory is freed, it has the default attributes set and can be used generically
493+
PeCoffLoaderUnloadImage (&ImageContext);
480494
MmFreePages (DriverEntry->ImageBuffer, DriverEntry->NumberOfPage);
481495
}
482496
}

0 commit comments

Comments
 (0)