This feature makes use of paging mechanism to add a hidden (not present) page just before and after the allocated memory block. If the code tries to access memory outside of the allocated part, page fault exception will be triggered. This feature is controlled by three PCDs: gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPoolType gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPageType BIT0 and BIT1 of PcdHeapGuardPropertyMask can be used to enable or disable memory guard for page and pool respectively. PcdHeapGuardPoolType and/or PcdHeapGuardPageType are used to enable or disable guard for specific type of memory. For example, we can turn on guard only for EfiBootServicesData and EfiRuntimeServicesData by setting the PCD with value 0x50. Pool memory is not ususally integer multiple of one page, and is more likely less than a page. There's no way to monitor the overflow at both top and bottom of pool memory. BIT7 of PcdHeapGuardPropertyMask is used to control how to position the head of pool memory so that it's easier to catch memory overflow in memory growing direction or in decreasing direction. Note1: Turning on heap guard, especially pool guard, will introduce too many memory fragments. Windows 10 has a limitation in its boot loader, which accepts at most 512 memory descriptors passed from BIOS. This will prevent Windows 10 from booting if heap guard is enabled. The latest Linux distribution with grub boot loader has no such issue. Normally it's not recommended to enable this feature in production build of BIOS. Note2: Don't enable this feature for NT32 emulation platform which doesn't support paging. Cc: Star Zeng <star.zeng@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Suggested-by: Ayellet Wolman <ayellet.wolman@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jian J Wang <jian.j.wang@intel.com> Reviewed-by: Jiewen Yao <jiewen.yao@intel.com> Regression-tested-by: Laszlo Ersek <lersek@redhat.com>
1210 lines
39 KiB
C
1210 lines
39 KiB
C
/** @file
|
|
UEFI Memory Protection support.
|
|
|
|
If the UEFI image is page aligned, the image code section is set to read only
|
|
and the image data section is set to non-executable.
|
|
|
|
1) This policy is applied for all UEFI image including boot service driver,
|
|
runtime driver or application.
|
|
2) This policy is applied only if the UEFI image meets the page alignment
|
|
requirement.
|
|
3) This policy is applied only if the Source UEFI image matches the
|
|
PcdImageProtectionPolicy definition.
|
|
4) This policy is not applied to the non-PE image region.
|
|
|
|
The DxeCore calls CpuArchProtocol->SetMemoryAttributes() to protect
|
|
the image. If the CpuArch protocol is not installed yet, the DxeCore
|
|
enqueues the protection request. Once the CpuArch is installed, the
|
|
DxeCore dequeues the protection request and applies policy.
|
|
|
|
Once the image is unloaded, the protection is removed automatically.
|
|
|
|
Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <PiDxe.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DxeServicesTableLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/UefiLib.h>
|
|
|
|
#include <Guid/EventGroup.h>
|
|
#include <Guid/MemoryAttributesTable.h>
|
|
#include <Guid/PropertiesTable.h>
|
|
|
|
#include <Protocol/FirmwareVolume2.h>
|
|
#include <Protocol/BlockIo.h>
|
|
#include <Protocol/SimpleFileSystem.h>
|
|
|
|
#include "DxeMain.h"
|
|
|
|
#define CACHE_ATTRIBUTE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB | EFI_MEMORY_UCE | EFI_MEMORY_WP)
|
|
#define MEMORY_ATTRIBUTE_MASK (EFI_MEMORY_RP | EFI_MEMORY_XP | EFI_MEMORY_RO)
|
|
|
|
//
|
|
// Image type definitions
|
|
//
|
|
#define IMAGE_UNKNOWN 0x00000001
|
|
#define IMAGE_FROM_FV 0x00000002
|
|
|
|
//
|
|
// Protection policy bit definition
|
|
//
|
|
#define DO_NOT_PROTECT 0x00000000
|
|
#define PROTECT_IF_ALIGNED_ELSE_ALLOW 0x00000001
|
|
|
|
#define MEMORY_TYPE_OS_RESERVED_MIN 0x80000000
|
|
#define MEMORY_TYPE_OEM_RESERVED_MIN 0x70000000
|
|
|
|
#define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
|
|
((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
|
|
|
|
UINT32 mImageProtectionPolicy;
|
|
|
|
extern LIST_ENTRY mGcdMemorySpaceMap;
|
|
|
|
STATIC LIST_ENTRY mProtectedImageRecordList;
|
|
|
|
/**
|
|
Sort code section in image record, based upon CodeSegmentBase from low to high.
|
|
|
|
@param ImageRecord image record to be sorted
|
|
**/
|
|
VOID
|
|
SortImageRecordCodeSection (
|
|
IN IMAGE_PROPERTIES_RECORD *ImageRecord
|
|
);
|
|
|
|
/**
|
|
Check if code section in image record is valid.
|
|
|
|
@param ImageRecord image record to be checked
|
|
|
|
@retval TRUE image record is valid
|
|
@retval FALSE image record is invalid
|
|
**/
|
|
BOOLEAN
|
|
IsImageRecordCodeSectionValid (
|
|
IN IMAGE_PROPERTIES_RECORD *ImageRecord
|
|
);
|
|
|
|
/**
|
|
Get the image type.
|
|
|
|
@param[in] File This is a pointer to the device path of the file that is
|
|
being dispatched.
|
|
|
|
@return UINT32 Image Type
|
|
**/
|
|
UINT32
|
|
GetImageType (
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *File
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE DeviceHandle;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
|
|
if (File == NULL) {
|
|
return IMAGE_UNKNOWN;
|
|
}
|
|
|
|
//
|
|
// First check to see if File is from a Firmware Volume
|
|
//
|
|
DeviceHandle = NULL;
|
|
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
|
Status = gBS->LocateDevicePath (
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
&TempDevicePath,
|
|
&DeviceHandle
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->OpenProtocol (
|
|
DeviceHandle,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
return IMAGE_FROM_FV;
|
|
}
|
|
}
|
|
return IMAGE_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
Get UEFI image protection policy based upon image type.
|
|
|
|
@param[in] ImageType The UEFI image type
|
|
|
|
@return UEFI image protection policy
|
|
**/
|
|
UINT32
|
|
GetProtectionPolicyFromImageType (
|
|
IN UINT32 ImageType
|
|
)
|
|
{
|
|
if ((ImageType & mImageProtectionPolicy) == 0) {
|
|
return DO_NOT_PROTECT;
|
|
} else {
|
|
return PROTECT_IF_ALIGNED_ELSE_ALLOW;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Get UEFI image protection policy based upon loaded image device path.
|
|
|
|
@param[in] LoadedImage The loaded image protocol
|
|
@param[in] LoadedImageDevicePath The loaded image device path protocol
|
|
|
|
@return UEFI image protection policy
|
|
**/
|
|
UINT32
|
|
GetUefiImageProtectionPolicy (
|
|
IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
|
|
)
|
|
{
|
|
BOOLEAN InSmm;
|
|
UINT32 ImageType;
|
|
UINT32 ProtectionPolicy;
|
|
|
|
//
|
|
// Check SMM
|
|
//
|
|
InSmm = FALSE;
|
|
if (gSmmBase2 != NULL) {
|
|
gSmmBase2->InSmm (gSmmBase2, &InSmm);
|
|
}
|
|
if (InSmm) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check DevicePath
|
|
//
|
|
if (LoadedImage == gDxeCoreLoadedImage) {
|
|
ImageType = IMAGE_FROM_FV;
|
|
} else {
|
|
ImageType = GetImageType (LoadedImageDevicePath);
|
|
}
|
|
ProtectionPolicy = GetProtectionPolicyFromImageType (ImageType);
|
|
return ProtectionPolicy;
|
|
}
|
|
|
|
|
|
/**
|
|
Set UEFI image memory attributes.
|
|
|
|
@param[in] BaseAddress Specified start address
|
|
@param[in] Length Specified length
|
|
@param[in] Attributes Specified attributes
|
|
**/
|
|
VOID
|
|
SetUefiImageMemoryAttributes (
|
|
IN UINT64 BaseAddress,
|
|
IN UINT64 Length,
|
|
IN UINT64 Attributes
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
|
|
UINT64 FinalAttributes;
|
|
|
|
Status = CoreGetMemorySpaceDescriptor(BaseAddress, &Descriptor);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
FinalAttributes = (Descriptor.Attributes & CACHE_ATTRIBUTE_MASK) | (Attributes & MEMORY_ATTRIBUTE_MASK);
|
|
|
|
DEBUG ((DEBUG_INFO, "SetUefiImageMemoryAttributes - 0x%016lx - 0x%016lx (0x%016lx)\n", BaseAddress, Length, FinalAttributes));
|
|
|
|
ASSERT(gCpu != NULL);
|
|
gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
|
|
}
|
|
|
|
/**
|
|
Set UEFI image protection attributes.
|
|
|
|
@param[in] ImageRecord A UEFI image record
|
|
**/
|
|
VOID
|
|
SetUefiImageProtectionAttributes (
|
|
IN IMAGE_PROPERTIES_RECORD *ImageRecord
|
|
)
|
|
{
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
|
|
LIST_ENTRY *ImageRecordCodeSectionLink;
|
|
LIST_ENTRY *ImageRecordCodeSectionEndLink;
|
|
LIST_ENTRY *ImageRecordCodeSectionList;
|
|
UINT64 CurrentBase;
|
|
UINT64 ImageEnd;
|
|
|
|
ImageRecordCodeSectionList = &ImageRecord->CodeSegmentList;
|
|
|
|
CurrentBase = ImageRecord->ImageBase;
|
|
ImageEnd = ImageRecord->ImageBase + ImageRecord->ImageSize;
|
|
|
|
ImageRecordCodeSectionLink = ImageRecordCodeSectionList->ForwardLink;
|
|
ImageRecordCodeSectionEndLink = ImageRecordCodeSectionList;
|
|
while (ImageRecordCodeSectionLink != ImageRecordCodeSectionEndLink) {
|
|
ImageRecordCodeSection = CR (
|
|
ImageRecordCodeSectionLink,
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION,
|
|
Link,
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
|
|
);
|
|
ImageRecordCodeSectionLink = ImageRecordCodeSectionLink->ForwardLink;
|
|
|
|
ASSERT (CurrentBase <= ImageRecordCodeSection->CodeSegmentBase);
|
|
if (CurrentBase < ImageRecordCodeSection->CodeSegmentBase) {
|
|
//
|
|
// DATA
|
|
//
|
|
SetUefiImageMemoryAttributes (
|
|
CurrentBase,
|
|
ImageRecordCodeSection->CodeSegmentBase - CurrentBase,
|
|
EFI_MEMORY_XP
|
|
);
|
|
}
|
|
//
|
|
// CODE
|
|
//
|
|
SetUefiImageMemoryAttributes (
|
|
ImageRecordCodeSection->CodeSegmentBase,
|
|
ImageRecordCodeSection->CodeSegmentSize,
|
|
EFI_MEMORY_RO
|
|
);
|
|
CurrentBase = ImageRecordCodeSection->CodeSegmentBase + ImageRecordCodeSection->CodeSegmentSize;
|
|
}
|
|
//
|
|
// Last DATA
|
|
//
|
|
ASSERT (CurrentBase <= ImageEnd);
|
|
if (CurrentBase < ImageEnd) {
|
|
//
|
|
// DATA
|
|
//
|
|
SetUefiImageMemoryAttributes (
|
|
CurrentBase,
|
|
ImageEnd - CurrentBase,
|
|
EFI_MEMORY_XP
|
|
);
|
|
}
|
|
return ;
|
|
}
|
|
|
|
/**
|
|
Return if the PE image section is aligned.
|
|
|
|
@param[in] SectionAlignment PE/COFF section alignment
|
|
@param[in] MemoryType PE/COFF image memory type
|
|
|
|
@retval TRUE The PE image section is aligned.
|
|
@retval FALSE The PE image section is not aligned.
|
|
**/
|
|
BOOLEAN
|
|
IsMemoryProtectionSectionAligned (
|
|
IN UINT32 SectionAlignment,
|
|
IN EFI_MEMORY_TYPE MemoryType
|
|
)
|
|
{
|
|
UINT32 PageAlignment;
|
|
|
|
switch (MemoryType) {
|
|
case EfiRuntimeServicesCode:
|
|
case EfiACPIMemoryNVS:
|
|
PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
|
|
break;
|
|
case EfiRuntimeServicesData:
|
|
case EfiACPIReclaimMemory:
|
|
ASSERT (FALSE);
|
|
PageAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
|
|
break;
|
|
case EfiBootServicesCode:
|
|
case EfiLoaderCode:
|
|
case EfiReservedMemoryType:
|
|
PageAlignment = EFI_PAGE_SIZE;
|
|
break;
|
|
default:
|
|
ASSERT (FALSE);
|
|
PageAlignment = EFI_PAGE_SIZE;
|
|
break;
|
|
}
|
|
|
|
if ((SectionAlignment & (PageAlignment - 1)) != 0) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Free Image record.
|
|
|
|
@param[in] ImageRecord A UEFI image record
|
|
**/
|
|
VOID
|
|
FreeImageRecord (
|
|
IN IMAGE_PROPERTIES_RECORD *ImageRecord
|
|
)
|
|
{
|
|
LIST_ENTRY *CodeSegmentListHead;
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
|
|
|
|
CodeSegmentListHead = &ImageRecord->CodeSegmentList;
|
|
while (!IsListEmpty (CodeSegmentListHead)) {
|
|
ImageRecordCodeSection = CR (
|
|
CodeSegmentListHead->ForwardLink,
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION,
|
|
Link,
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE
|
|
);
|
|
RemoveEntryList (&ImageRecordCodeSection->Link);
|
|
FreePool (ImageRecordCodeSection);
|
|
}
|
|
|
|
if (ImageRecord->Link.ForwardLink != NULL) {
|
|
RemoveEntryList (&ImageRecord->Link);
|
|
}
|
|
FreePool (ImageRecord);
|
|
}
|
|
|
|
/**
|
|
Protect UEFI PE/COFF image.
|
|
|
|
@param[in] LoadedImage The loaded image protocol
|
|
@param[in] LoadedImageDevicePath The loaded image device path protocol
|
|
**/
|
|
VOID
|
|
ProtectUefiImage (
|
|
IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
|
|
)
|
|
{
|
|
VOID *ImageAddress;
|
|
EFI_IMAGE_DOS_HEADER *DosHdr;
|
|
UINT32 PeCoffHeaderOffset;
|
|
UINT32 SectionAlignment;
|
|
EFI_IMAGE_SECTION_HEADER *Section;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
|
|
UINT8 *Name;
|
|
UINTN Index;
|
|
IMAGE_PROPERTIES_RECORD *ImageRecord;
|
|
CHAR8 *PdbPointer;
|
|
IMAGE_PROPERTIES_RECORD_CODE_SECTION *ImageRecordCodeSection;
|
|
UINT16 Magic;
|
|
BOOLEAN IsAligned;
|
|
UINT32 ProtectionPolicy;
|
|
|
|
DEBUG ((DEBUG_INFO, "ProtectUefiImageCommon - 0x%x\n", LoadedImage));
|
|
DEBUG ((DEBUG_INFO, " - 0x%016lx - 0x%016lx\n", (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase, LoadedImage->ImageSize));
|
|
|
|
if (gCpu == NULL) {
|
|
return ;
|
|
}
|
|
|
|
ProtectionPolicy = GetUefiImageProtectionPolicy (LoadedImage, LoadedImageDevicePath);
|
|
switch (ProtectionPolicy) {
|
|
case DO_NOT_PROTECT:
|
|
return ;
|
|
case PROTECT_IF_ALIGNED_ELSE_ALLOW:
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
return ;
|
|
}
|
|
|
|
ImageRecord = AllocateZeroPool (sizeof(*ImageRecord));
|
|
if (ImageRecord == NULL) {
|
|
return ;
|
|
}
|
|
ImageRecord->Signature = IMAGE_PROPERTIES_RECORD_SIGNATURE;
|
|
|
|
//
|
|
// Step 1: record whole region
|
|
//
|
|
ImageRecord->ImageBase = (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase;
|
|
ImageRecord->ImageSize = LoadedImage->ImageSize;
|
|
|
|
ImageAddress = LoadedImage->ImageBase;
|
|
|
|
PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
|
|
if (PdbPointer != NULL) {
|
|
DEBUG ((DEBUG_VERBOSE, " Image - %a\n", PdbPointer));
|
|
}
|
|
|
|
//
|
|
// Check PE/COFF image
|
|
//
|
|
DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress;
|
|
PeCoffHeaderOffset = 0;
|
|
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
PeCoffHeaderOffset = DosHdr->e_lfanew;
|
|
}
|
|
|
|
Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset);
|
|
if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
|
|
DEBUG ((DEBUG_VERBOSE, "Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature));
|
|
// It might be image in SMM.
|
|
goto Finish;
|
|
}
|
|
|
|
//
|
|
// Get SectionAlignment
|
|
//
|
|
if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
//
|
|
// NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value
|
|
// in the PE/COFF Header. If the MachineType is Itanium(IA64) and the
|
|
// Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC
|
|
// then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC
|
|
//
|
|
Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC;
|
|
} else {
|
|
//
|
|
// Get the magic value from the PE/COFF Optional Header
|
|
//
|
|
Magic = Hdr.Pe32->OptionalHeader.Magic;
|
|
}
|
|
if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment;
|
|
} else {
|
|
SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment;
|
|
}
|
|
|
|
IsAligned = IsMemoryProtectionSectionAligned (SectionAlignment, LoadedImage->ImageCodeType);
|
|
if (!IsAligned) {
|
|
DEBUG ((DEBUG_VERBOSE, "!!!!!!!! ProtectUefiImageCommon - Section Alignment(0x%x) is incorrect !!!!!!!!\n",
|
|
SectionAlignment));
|
|
PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
|
|
if (PdbPointer != NULL) {
|
|
DEBUG ((DEBUG_VERBOSE, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));
|
|
}
|
|
goto Finish;
|
|
}
|
|
|
|
Section = (EFI_IMAGE_SECTION_HEADER *) (
|
|
(UINT8 *) (UINTN) ImageAddress +
|
|
PeCoffHeaderOffset +
|
|
sizeof(UINT32) +
|
|
sizeof(EFI_IMAGE_FILE_HEADER) +
|
|
Hdr.Pe32->FileHeader.SizeOfOptionalHeader
|
|
);
|
|
ImageRecord->CodeSegmentCount = 0;
|
|
InitializeListHead (&ImageRecord->CodeSegmentList);
|
|
for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) {
|
|
Name = Section[Index].Name;
|
|
DEBUG ((
|
|
DEBUG_VERBOSE,
|
|
" Section - '%c%c%c%c%c%c%c%c'\n",
|
|
Name[0],
|
|
Name[1],
|
|
Name[2],
|
|
Name[3],
|
|
Name[4],
|
|
Name[5],
|
|
Name[6],
|
|
Name[7]
|
|
));
|
|
|
|
//
|
|
// Instead of assuming that a PE/COFF section of type EFI_IMAGE_SCN_CNT_CODE
|
|
// can always be mapped read-only, classify a section as a code section only
|
|
// if it has the executable attribute set and the writable attribute cleared.
|
|
//
|
|
// This adheres more closely to the PE/COFF spec, and avoids issues with
|
|
// Linux OS loaders that may consist of a single read/write/execute section.
|
|
//
|
|
if ((Section[Index].Characteristics & (EFI_IMAGE_SCN_MEM_WRITE | EFI_IMAGE_SCN_MEM_EXECUTE)) == EFI_IMAGE_SCN_MEM_EXECUTE) {
|
|
DEBUG ((DEBUG_VERBOSE, " VirtualSize - 0x%08x\n", Section[Index].Misc.VirtualSize));
|
|
DEBUG ((DEBUG_VERBOSE, " VirtualAddress - 0x%08x\n", Section[Index].VirtualAddress));
|
|
DEBUG ((DEBUG_VERBOSE, " SizeOfRawData - 0x%08x\n", Section[Index].SizeOfRawData));
|
|
DEBUG ((DEBUG_VERBOSE, " PointerToRawData - 0x%08x\n", Section[Index].PointerToRawData));
|
|
DEBUG ((DEBUG_VERBOSE, " PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations));
|
|
DEBUG ((DEBUG_VERBOSE, " PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers));
|
|
DEBUG ((DEBUG_VERBOSE, " NumberOfRelocations - 0x%08x\n", Section[Index].NumberOfRelocations));
|
|
DEBUG ((DEBUG_VERBOSE, " NumberOfLinenumbers - 0x%08x\n", Section[Index].NumberOfLinenumbers));
|
|
DEBUG ((DEBUG_VERBOSE, " Characteristics - 0x%08x\n", Section[Index].Characteristics));
|
|
|
|
//
|
|
// Step 2: record code section
|
|
//
|
|
ImageRecordCodeSection = AllocatePool (sizeof(*ImageRecordCodeSection));
|
|
if (ImageRecordCodeSection == NULL) {
|
|
return ;
|
|
}
|
|
ImageRecordCodeSection->Signature = IMAGE_PROPERTIES_RECORD_CODE_SECTION_SIGNATURE;
|
|
|
|
ImageRecordCodeSection->CodeSegmentBase = (UINTN)ImageAddress + Section[Index].VirtualAddress;
|
|
ImageRecordCodeSection->CodeSegmentSize = ALIGN_VALUE(Section[Index].SizeOfRawData, SectionAlignment);
|
|
|
|
DEBUG ((DEBUG_VERBOSE, "ImageCode: 0x%016lx - 0x%016lx\n", ImageRecordCodeSection->CodeSegmentBase, ImageRecordCodeSection->CodeSegmentSize));
|
|
|
|
InsertTailList (&ImageRecord->CodeSegmentList, &ImageRecordCodeSection->Link);
|
|
ImageRecord->CodeSegmentCount++;
|
|
}
|
|
}
|
|
|
|
if (ImageRecord->CodeSegmentCount == 0) {
|
|
//
|
|
// If a UEFI executable consists of a single read+write+exec PE/COFF
|
|
// section, that isn't actually an error. The image can be launched
|
|
// alright, only image protection cannot be applied to it fully.
|
|
//
|
|
// One example that elicits this is (some) Linux kernels (with the EFI stub
|
|
// of course).
|
|
//
|
|
DEBUG ((DEBUG_WARN, "!!!!!!!! ProtectUefiImageCommon - CodeSegmentCount is 0 !!!!!!!!\n"));
|
|
PdbPointer = PeCoffLoaderGetPdbPointer ((VOID*) (UINTN) ImageAddress);
|
|
if (PdbPointer != NULL) {
|
|
DEBUG ((DEBUG_WARN, "!!!!!!!! Image - %a !!!!!!!!\n", PdbPointer));
|
|
}
|
|
goto Finish;
|
|
}
|
|
|
|
//
|
|
// Final
|
|
//
|
|
SortImageRecordCodeSection (ImageRecord);
|
|
//
|
|
// Check overlap all section in ImageBase/Size
|
|
//
|
|
if (!IsImageRecordCodeSectionValid (ImageRecord)) {
|
|
DEBUG ((DEBUG_ERROR, "IsImageRecordCodeSectionValid - FAIL\n"));
|
|
goto Finish;
|
|
}
|
|
|
|
//
|
|
// Round up the ImageSize, some CPU arch may return EFI_UNSUPPORTED if ImageSize is not aligned.
|
|
// Given that the loader always allocates full pages, we know the space after the image is not used.
|
|
//
|
|
ImageRecord->ImageSize = ALIGN_VALUE(LoadedImage->ImageSize, EFI_PAGE_SIZE);
|
|
|
|
//
|
|
// CPU ARCH present. Update memory attribute directly.
|
|
//
|
|
SetUefiImageProtectionAttributes (ImageRecord);
|
|
|
|
//
|
|
// Record the image record in the list so we can undo the protections later
|
|
//
|
|
InsertTailList (&mProtectedImageRecordList, &ImageRecord->Link);
|
|
|
|
Finish:
|
|
return ;
|
|
}
|
|
|
|
/**
|
|
Unprotect UEFI image.
|
|
|
|
@param[in] LoadedImage The loaded image protocol
|
|
@param[in] LoadedImageDevicePath The loaded image device path protocol
|
|
**/
|
|
VOID
|
|
UnprotectUefiImage (
|
|
IN EFI_LOADED_IMAGE_PROTOCOL *LoadedImage,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath
|
|
)
|
|
{
|
|
IMAGE_PROPERTIES_RECORD *ImageRecord;
|
|
LIST_ENTRY *ImageRecordLink;
|
|
|
|
if (PcdGet32(PcdImageProtectionPolicy) != 0) {
|
|
for (ImageRecordLink = mProtectedImageRecordList.ForwardLink;
|
|
ImageRecordLink != &mProtectedImageRecordList;
|
|
ImageRecordLink = ImageRecordLink->ForwardLink) {
|
|
ImageRecord = CR (
|
|
ImageRecordLink,
|
|
IMAGE_PROPERTIES_RECORD,
|
|
Link,
|
|
IMAGE_PROPERTIES_RECORD_SIGNATURE
|
|
);
|
|
|
|
if (ImageRecord->ImageBase == (EFI_PHYSICAL_ADDRESS)(UINTN)LoadedImage->ImageBase) {
|
|
SetUefiImageMemoryAttributes (ImageRecord->ImageBase,
|
|
ImageRecord->ImageSize,
|
|
0);
|
|
FreeImageRecord (ImageRecord);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Return the EFI memory permission attribute associated with memory
|
|
type 'MemoryType' under the configured DXE memory protection policy.
|
|
|
|
@param MemoryType Memory type.
|
|
**/
|
|
STATIC
|
|
UINT64
|
|
GetPermissionAttributeForMemoryType (
|
|
IN EFI_MEMORY_TYPE MemoryType
|
|
)
|
|
{
|
|
UINT64 TestBit;
|
|
|
|
if ((UINT32)MemoryType >= MEMORY_TYPE_OS_RESERVED_MIN) {
|
|
TestBit = BIT63;
|
|
} else if ((UINT32)MemoryType >= MEMORY_TYPE_OEM_RESERVED_MIN) {
|
|
TestBit = BIT62;
|
|
} else {
|
|
TestBit = LShiftU64 (1, MemoryType);
|
|
}
|
|
|
|
if ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & TestBit) != 0) {
|
|
return EFI_MEMORY_XP;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Sort memory map entries based upon PhysicalStart, from low to high.
|
|
|
|
@param MemoryMap A pointer to the buffer in which firmware places
|
|
the current memory map.
|
|
@param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
|
|
@param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
SortMemoryMap (
|
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
|
|
IN UINTN MemoryMapSize,
|
|
IN UINTN DescriptorSize
|
|
)
|
|
{
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
|
EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
|
|
EFI_MEMORY_DESCRIPTOR TempMemoryMap;
|
|
|
|
MemoryMapEntry = MemoryMap;
|
|
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
|
|
while (MemoryMapEntry < MemoryMapEnd) {
|
|
while (NextMemoryMapEntry < MemoryMapEnd) {
|
|
if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
|
|
CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
|
|
CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
|
|
CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
|
|
}
|
|
|
|
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
|
|
}
|
|
|
|
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Merge adjacent memory map entries if they use the same memory protection policy
|
|
|
|
@param[in, out] MemoryMap A pointer to the buffer in which firmware places
|
|
the current memory map.
|
|
@param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
|
|
MemoryMap buffer. On input, this is the size of
|
|
the current memory map. On output,
|
|
it is the size of new memory map after merge.
|
|
@param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
MergeMemoryMapForProtectionPolicy (
|
|
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
|
|
IN OUT UINTN *MemoryMapSize,
|
|
IN UINTN DescriptorSize
|
|
)
|
|
{
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
|
|
UINT64 MemoryBlockLength;
|
|
EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
|
|
EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
|
|
UINT64 Attributes;
|
|
|
|
SortMemoryMap (MemoryMap, *MemoryMapSize, DescriptorSize);
|
|
|
|
MemoryMapEntry = MemoryMap;
|
|
NewMemoryMapEntry = MemoryMap;
|
|
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
|
|
while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
|
|
CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
|
|
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
|
|
do {
|
|
MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
|
|
Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
|
|
|
|
if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
|
|
Attributes == GetPermissionAttributeForMemoryType (NextMemoryMapEntry->Type) &&
|
|
((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
|
|
MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
|
|
if (NewMemoryMapEntry != MemoryMapEntry) {
|
|
NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
|
|
}
|
|
|
|
NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
|
|
continue;
|
|
} else {
|
|
MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
|
|
break;
|
|
}
|
|
} while (TRUE);
|
|
|
|
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
|
|
}
|
|
|
|
*MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
|
|
|
|
return ;
|
|
}
|
|
|
|
|
|
/**
|
|
Remove exec permissions from all regions whose type is identified by
|
|
PcdDxeNxMemoryProtectionPolicy.
|
|
**/
|
|
STATIC
|
|
VOID
|
|
InitializeDxeNxMemoryProtectionPolicy (
|
|
VOID
|
|
)
|
|
{
|
|
UINTN MemoryMapSize;
|
|
UINTN MapKey;
|
|
UINTN DescriptorSize;
|
|
UINT32 DescriptorVersion;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMap;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
|
|
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
|
|
EFI_STATUS Status;
|
|
UINT64 Attributes;
|
|
LIST_ENTRY *Link;
|
|
EFI_GCD_MAP_ENTRY *Entry;
|
|
|
|
//
|
|
// Get the EFI memory map.
|
|
//
|
|
MemoryMapSize = 0;
|
|
MemoryMap = NULL;
|
|
|
|
Status = gBS->GetMemoryMap (
|
|
&MemoryMapSize,
|
|
MemoryMap,
|
|
&MapKey,
|
|
&DescriptorSize,
|
|
&DescriptorVersion
|
|
);
|
|
ASSERT (Status == EFI_BUFFER_TOO_SMALL);
|
|
do {
|
|
MemoryMap = (EFI_MEMORY_DESCRIPTOR *) AllocatePool (MemoryMapSize);
|
|
ASSERT (MemoryMap != NULL);
|
|
Status = gBS->GetMemoryMap (
|
|
&MemoryMapSize,
|
|
MemoryMap,
|
|
&MapKey,
|
|
&DescriptorSize,
|
|
&DescriptorVersion
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (MemoryMap);
|
|
}
|
|
} while (Status == EFI_BUFFER_TOO_SMALL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
DEBUG((DEBUG_ERROR, "%a: applying strict permissions to active memory regions\n",
|
|
__FUNCTION__));
|
|
|
|
MergeMemoryMapForProtectionPolicy (MemoryMap, &MemoryMapSize, DescriptorSize);
|
|
|
|
MemoryMapEntry = MemoryMap;
|
|
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
|
|
while ((UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd) {
|
|
|
|
Attributes = GetPermissionAttributeForMemoryType (MemoryMapEntry->Type);
|
|
if (Attributes != 0) {
|
|
SetUefiImageMemoryAttributes (
|
|
MemoryMapEntry->PhysicalStart,
|
|
LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT),
|
|
Attributes);
|
|
}
|
|
MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
|
|
}
|
|
FreePool (MemoryMap);
|
|
|
|
//
|
|
// Apply the policy for RAM regions that we know are present and
|
|
// accessible, but have not been added to the UEFI memory map (yet).
|
|
//
|
|
if (GetPermissionAttributeForMemoryType (EfiConventionalMemory) != 0) {
|
|
DEBUG((DEBUG_ERROR,
|
|
"%a: applying strict permissions to inactive memory regions\n",
|
|
__FUNCTION__));
|
|
|
|
CoreAcquireGcdMemoryLock ();
|
|
|
|
Link = mGcdMemorySpaceMap.ForwardLink;
|
|
while (Link != &mGcdMemorySpaceMap) {
|
|
|
|
Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
|
|
|
|
if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&
|
|
Entry->EndAddress < MAX_ADDRESS &&
|
|
(Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
|
|
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {
|
|
|
|
Attributes = GetPermissionAttributeForMemoryType (EfiConventionalMemory) |
|
|
(Entry->Attributes & CACHE_ATTRIBUTE_MASK);
|
|
|
|
DEBUG ((DEBUG_INFO,
|
|
"Untested GCD memory space region: - 0x%016lx - 0x%016lx (0x%016lx)\n",
|
|
Entry->BaseAddress, Entry->EndAddress - Entry->BaseAddress + 1,
|
|
Attributes));
|
|
|
|
ASSERT(gCpu != NULL);
|
|
gCpu->SetMemoryAttributes (gCpu, Entry->BaseAddress,
|
|
Entry->EndAddress - Entry->BaseAddress + 1, Attributes);
|
|
}
|
|
|
|
Link = Link->ForwardLink;
|
|
}
|
|
CoreReleaseGcdMemoryLock ();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
A notification for CPU_ARCH protocol.
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
@param[in] Context Pointer to the notification function's context,
|
|
which is implementation-dependent.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
MemoryProtectionCpuArchProtocolNotify (
|
|
IN EFI_EVENT Event,
|
|
IN VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
EFI_DEVICE_PATH_PROTOCOL *LoadedImageDevicePath;
|
|
UINTN NoHandles;
|
|
EFI_HANDLE *HandleBuffer;
|
|
UINTN Index;
|
|
|
|
DEBUG ((DEBUG_INFO, "MemoryProtectionCpuArchProtocolNotify:\n"));
|
|
Status = CoreLocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&gCpu);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Apply the memory protection policy on non-BScode/RTcode regions.
|
|
//
|
|
if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
|
|
InitializeDxeNxMemoryProtectionPolicy ();
|
|
}
|
|
|
|
if (mImageProtectionPolicy == 0) {
|
|
return;
|
|
}
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
NULL,
|
|
&NoHandles,
|
|
&HandleBuffer
|
|
);
|
|
if (EFI_ERROR (Status) && (NoHandles == 0)) {
|
|
return ;
|
|
}
|
|
|
|
for (Index = 0; Index < NoHandles; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&LoadedImage
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
continue;
|
|
}
|
|
Status = gBS->HandleProtocol (
|
|
HandleBuffer[Index],
|
|
&gEfiLoadedImageDevicePathProtocolGuid,
|
|
(VOID **)&LoadedImageDevicePath
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
LoadedImageDevicePath = NULL;
|
|
}
|
|
|
|
ProtectUefiImage (LoadedImage, LoadedImageDevicePath);
|
|
}
|
|
|
|
CoreCloseEvent (Event);
|
|
return;
|
|
}
|
|
|
|
/**
|
|
ExitBootServices Callback function for memory protection.
|
|
**/
|
|
VOID
|
|
MemoryProtectionExitBootServicesCallback (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_RUNTIME_IMAGE_ENTRY *RuntimeImage;
|
|
LIST_ENTRY *Link;
|
|
|
|
//
|
|
// We need remove the RT protection, because RT relocation need write code segment
|
|
// at SetVirtualAddressMap(). We cannot assume OS/Loader has taken over page table at that time.
|
|
//
|
|
// Firmware does not own page tables after ExitBootServices(), so the OS would
|
|
// have to relax protection of RT code pages across SetVirtualAddressMap(), or
|
|
// delay setting protections on RT code pages until after SetVirtualAddressMap().
|
|
// OS may set protection on RT based upon EFI_MEMORY_ATTRIBUTES_TABLE later.
|
|
//
|
|
if (mImageProtectionPolicy != 0) {
|
|
for (Link = gRuntime->ImageHead.ForwardLink; Link != &gRuntime->ImageHead; Link = Link->ForwardLink) {
|
|
RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
|
|
SetUefiImageMemoryAttributes ((UINT64)(UINTN)RuntimeImage->ImageBase, ALIGN_VALUE(RuntimeImage->ImageSize, EFI_PAGE_SIZE), 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Disable NULL pointer detection after EndOfDxe. This is a workaround resort in
|
|
order to skip unfixable NULL pointer access issues detected in OptionROM or
|
|
boot loaders.
|
|
|
|
@param[in] Event The Event this notify function registered to.
|
|
@param[in] Context Pointer to the context data registered to the Event.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DisableNullDetectionAtTheEndOfDxe (
|
|
EFI_EVENT Event,
|
|
VOID *Context
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Desc;
|
|
|
|
DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): start\r\n"));
|
|
//
|
|
// Disable NULL pointer detection by enabling first 4K page
|
|
//
|
|
Status = CoreGetMemorySpaceDescriptor (0, &Desc);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
if ((Desc.Capabilities & EFI_MEMORY_RP) == 0) {
|
|
Status = CoreSetMemorySpaceCapabilities (
|
|
0,
|
|
EFI_PAGE_SIZE,
|
|
Desc.Capabilities | EFI_MEMORY_RP
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
Status = CoreSetMemorySpaceAttributes (
|
|
0,
|
|
EFI_PAGE_SIZE,
|
|
Desc.Attributes & ~EFI_MEMORY_RP
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
CoreCloseEvent (Event);
|
|
DEBUG ((DEBUG_INFO, "DisableNullDetectionAtTheEndOfDxe(): end\r\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
Initialize Memory Protection support.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
CoreInitializeMemoryProtection (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_EVENT Event;
|
|
EFI_EVENT EndOfDxeEvent;
|
|
VOID *Registration;
|
|
|
|
mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy);
|
|
|
|
InitializeListHead (&mProtectedImageRecordList);
|
|
|
|
//
|
|
// Sanity check the PcdDxeNxMemoryProtectionPolicy setting:
|
|
// - code regions should have no EFI_MEMORY_XP attribute
|
|
// - EfiConventionalMemory and EfiBootServicesData should use the
|
|
// same attribute
|
|
// - heap guard should not be enabled for the same type of memory
|
|
//
|
|
ASSERT ((GetPermissionAttributeForMemoryType (EfiBootServicesCode) & EFI_MEMORY_XP) == 0);
|
|
ASSERT ((GetPermissionAttributeForMemoryType (EfiRuntimeServicesCode) & EFI_MEMORY_XP) == 0);
|
|
ASSERT ((GetPermissionAttributeForMemoryType (EfiLoaderCode) & EFI_MEMORY_XP) == 0);
|
|
ASSERT (GetPermissionAttributeForMemoryType (EfiBootServicesData) ==
|
|
GetPermissionAttributeForMemoryType (EfiConventionalMemory));
|
|
ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPoolType)) == 0);
|
|
ASSERT ((PcdGet64 (PcdDxeNxMemoryProtectionPolicy) & PcdGet64 (PcdHeapGuardPageType)) == 0);
|
|
|
|
if (mImageProtectionPolicy != 0 || PcdGet64 (PcdDxeNxMemoryProtectionPolicy) != 0) {
|
|
Status = CoreCreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_CALLBACK,
|
|
MemoryProtectionCpuArchProtocolNotify,
|
|
NULL,
|
|
&Event
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
//
|
|
// Register for protocol notifactions on this event
|
|
//
|
|
Status = CoreRegisterProtocolNotify (
|
|
&gEfiCpuArchProtocolGuid,
|
|
Event,
|
|
&Registration
|
|
);
|
|
ASSERT_EFI_ERROR(Status);
|
|
}
|
|
|
|
//
|
|
// Register a callback to disable NULL pointer detection at EndOfDxe
|
|
//
|
|
if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT0|BIT7))
|
|
== (BIT0|BIT7)) {
|
|
Status = CoreCreateEventEx (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
DisableNullDetectionAtTheEndOfDxe,
|
|
NULL,
|
|
&gEfiEndOfDxeEventGroupGuid,
|
|
&EndOfDxeEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
/**
|
|
Returns whether we are currently executing in SMM mode.
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsInSmm (
|
|
VOID
|
|
)
|
|
{
|
|
BOOLEAN InSmm;
|
|
|
|
InSmm = FALSE;
|
|
if (gSmmBase2 != NULL) {
|
|
gSmmBase2->InSmm (gSmmBase2, &InSmm);
|
|
}
|
|
return InSmm;
|
|
}
|
|
|
|
/**
|
|
Manage memory permission attributes on a memory range, according to the
|
|
configured DXE memory protection policy.
|
|
|
|
@param OldType The old memory type of the range
|
|
@param NewType The new memory type of the range
|
|
@param Memory The base address of the range
|
|
@param Length The size of the range (in bytes)
|
|
|
|
@return EFI_SUCCESS If we are executing in SMM mode. No permission attributes
|
|
are updated in this case
|
|
@return EFI_SUCCESS If the the CPU arch protocol is not installed yet
|
|
@return EFI_SUCCESS If no DXE memory protection policy has been configured
|
|
@return EFI_SUCCESS If OldType and NewType use the same permission attributes
|
|
@return other Return value of gCpu->SetMemoryAttributes()
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
ApplyMemoryProtectionPolicy (
|
|
IN EFI_MEMORY_TYPE OldType,
|
|
IN EFI_MEMORY_TYPE NewType,
|
|
IN EFI_PHYSICAL_ADDRESS Memory,
|
|
IN UINT64 Length
|
|
)
|
|
{
|
|
UINT64 OldAttributes;
|
|
UINT64 NewAttributes;
|
|
|
|
//
|
|
// The policy configured in PcdDxeNxMemoryProtectionPolicy
|
|
// does not apply to allocations performed in SMM mode.
|
|
//
|
|
if (IsInSmm ()) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If the CPU arch protocol is not installed yet, we cannot manage memory
|
|
// permission attributes, and it is the job of the driver that installs this
|
|
// protocol to set the permissions on existing allocations.
|
|
//
|
|
if (gCpu == NULL) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Check if a DXE memory protection policy has been configured
|
|
//
|
|
if (PcdGet64 (PcdDxeNxMemoryProtectionPolicy) == 0) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Update the executable permissions according to the DXE memory
|
|
// protection policy, but only if
|
|
// - the policy is different between the old and the new type, or
|
|
// - this is a newly added region (OldType == EfiMaxMemoryType)
|
|
//
|
|
NewAttributes = GetPermissionAttributeForMemoryType (NewType);
|
|
|
|
if (OldType != EfiMaxMemoryType) {
|
|
OldAttributes = GetPermissionAttributeForMemoryType (OldType);
|
|
if (OldAttributes == NewAttributes) {
|
|
// policy is the same between OldType and NewType
|
|
return EFI_SUCCESS;
|
|
}
|
|
} else if (NewAttributes == 0) {
|
|
// newly added region of a type that does not require protection
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes);
|
|
}
|