From patchwork Fri Feb 24 15:04:59 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ard Biesheuvel X-Patchwork-Id: 94488 Delivered-To: patch@linaro.org Received: by 10.140.20.99 with SMTP id 90csp728428qgi; Fri, 24 Feb 2017 07:05:24 -0800 (PST) X-Received: by 10.98.11.9 with SMTP id t9mr3937617pfi.123.1487948724177; Fri, 24 Feb 2017 07:05:24 -0800 (PST) Return-Path: Received: from ml01.01.org (ml01.01.org. [198.145.21.10]) by mx.google.com with ESMTPS id b69si7596111pli.199.2017.02.24.07.05.23 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 24 Feb 2017 07:05:24 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 198.145.21.10 as permitted sender) client-ip=198.145.21.10; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of edk2-devel-bounces@lists.01.org designates 198.145.21.10 as permitted sender) smtp.mailfrom=edk2-devel-bounces@lists.01.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 640458215F; Fri, 24 Feb 2017 07:05:22 -0800 (PST) X-Original-To: edk2-devel@lists.01.org Delivered-To: edk2-devel@lists.01.org Received: from mail-wm0-x229.google.com (mail-wm0-x229.google.com [IPv6:2a00:1450:400c:c09::229]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 52F3882199 for ; Fri, 24 Feb 2017 07:05:20 -0800 (PST) Received: by mail-wm0-x229.google.com with SMTP id r141so16685910wmg.1 for ; Fri, 24 Feb 2017 07:05:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=b2JBYvdI4qmV/nPeMobfaSPi/vNCK8Qq4r8WPG95EkI=; b=KpTds9QGq157de0nVnnMESBtWXz2THeTkuMoRzAkPuzDGSlt1POT/bevRb64V0UNrv +Ap1Nl4EQGaaPp9nI3YEim43SscdhOH0W7aIko+qVjgA8OPdtzPqj7JjMn0d3dzsl+W3 ZZEUAVXm2YM9Y+mOOI6egI3nt4iTux9JPAXcw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=b2JBYvdI4qmV/nPeMobfaSPi/vNCK8Qq4r8WPG95EkI=; b=IwxBcz0Jbb9dDpTFdn1/1Idtv15lRzXwhPl8qWCi1kQVIS5DTeyCKF7pX7eqi0QtKP yZbSOM5GTuZTNOFlwY02NRo+u7de1HpjlZ2GkkDeNZzLSmM+objp5RKzjpOBReuRPk3E Xch3hjkznO1/jCIEWeEIX+Jas0Aj0q4ticOnCQ/3Bvq8EVEUgqPsMbVsyLa4C36BJ9nW GA3JsJjHn/szXG0gHQonxX1u1UbRwj9KXUYOnEH2tr/XbGLkCrjcSCu7AR12RRo30ugw j7bbLKo/I4BRdWQkBaJh4KlR1sWfdmHdFtw4onebjf5Y7D18PqfTNvdz8ZxkpWYN6Gsz rbvA== X-Gm-Message-State: AMke39n1VDzuMYZcxTWLfIRwJUY0mwIfSoL4Q4bb8xOyhBdGAXU1DueyAUeIEUl1q7yEd0Io X-Received: by 10.28.217.136 with SMTP id q130mr3237902wmg.13.1487948718719; Fri, 24 Feb 2017 07:05:18 -0800 (PST) Received: from localhost.localdomain ([105.149.201.216]) by smtp.gmail.com with ESMTPSA id w207sm2687048wmw.1.2017.02.24.07.05.16 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 24 Feb 2017 07:05:17 -0800 (PST) From: Ard Biesheuvel To: edk2-devel@lists.01.org, afish@apple.com, leif.lindholm@linaro.org, michael.d.kinney@intel.com, liming.gao@intel.com, jiewen.yao@intel.com Date: Fri, 24 Feb 2017 15:04:59 +0000 Message-Id: <1487948699-3179-6-git-send-email-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1487948699-3179-1-git-send-email-ard.biesheuvel@linaro.org> References: <1487948699-3179-1-git-send-email-ard.biesheuvel@linaro.org> Subject: [edk2] [PATCH v2 5/5] MdeModulePkg/DxeCore: implement memory protection policy X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: feng.tian@intel.com, lersek@redhat.com, star.zeng@intel.com, Ard Biesheuvel MIME-Version: 1.0 Errors-To: edk2-devel-bounces@lists.01.org Sender: "edk2-devel" This implements a DXE memory protection policy that ensure that regions that don't require executable permissions are mapped with the non-exec attribute set. First of all, it iterates over all entries in the UEFI memory map, and removes executable permissions according to the configured DXE memory protection policy, as recorded in PcdDxeMemoryProtectionPolicy. Secondly, it sets or clears the non-executable attribute when allocating or freeing pages, both for page based or pool based allocations. Note that this complements the image protection facility, which applies strict permissions to BootServicesCode/RuntimeServicesCode regions when the section alignment allows it. The memory protection configured by this patch operates on non-code regions only. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Ard Biesheuvel --- MdeModulePkg/Core/Dxe/DxeMain.inf | 1 + MdeModulePkg/Core/Dxe/Mem/Page.c | 105 ++++++++++++++++++++ MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 104 ++++++++++++++++++- 3 files changed, 209 insertions(+), 1 deletion(-) -- 2.7.4 _______________________________________________ edk2-devel mailing list edk2-devel@lists.01.org https://lists.01.org/mailman/listinfo/edk2-devel diff --git a/MdeModulePkg/Core/Dxe/DxeMain.inf b/MdeModulePkg/Core/Dxe/DxeMain.inf index 371e91cb0d7e..871868dbf305 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.inf +++ b/MdeModulePkg/Core/Dxe/DxeMain.inf @@ -191,6 +191,7 @@ [Pcd] gEfiMdeModulePkgTokenSpaceGuid.PcdMemoryProfileDriverPath ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdPropertiesTableEnable ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdImageProtectionPolicy ## CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdDxeMemoryProtectionPolicy ## CONSUMES # [Hob] # RESOURCE_DESCRIPTOR ## CONSUMES diff --git a/MdeModulePkg/Core/Dxe/Mem/Page.c b/MdeModulePkg/Core/Dxe/Mem/Page.c index 6330d41e7b3b..569238c77f98 100644 --- a/MdeModulePkg/Core/Dxe/Mem/Page.c +++ b/MdeModulePkg/Core/Dxe/Mem/Page.c @@ -1305,6 +1305,101 @@ Done: } /** + Return the EFI memory permission attribute associated with memory type 'Type' + under the configured DXE memory protection policy. +**/ +STATIC +UINT64 +EFIAPI +GetPermissionAttributeForMemoryType ( + IN EFI_MEMORY_TYPE Type + ) +{ + switch (Type) { + case EfiBootServicesCode: + case EfiRuntimeServicesCode: + case EfiLoaderCode: + break; + + default: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT0) != 0) { + return EFI_MEMORY_XP; + } + break; + case EfiReservedMemoryType: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT1) != 0) { + return EFI_MEMORY_XP; + } + break; + case EfiACPIMemoryNVS: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT2) != 0) { + return EFI_MEMORY_XP; + } + break; + } + return 0; +} + +/** + 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 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() + +**/ +STATIC +EFI_STATUS +EFIAPI +ApplyMemoryProtection ( + IN EFI_MEMORY_TYPE OldType, + IN EFI_MEMORY_TYPE NewType, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINT64 Length + ) +{ + UINT64 OldAttributes; + UINT64 NewAttributes; + + // + // 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 (PcdGet32 (PcdDxeMemoryProtectionPolicy) == 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. + // + OldAttributes = GetPermissionAttributeForMemoryType (OldType); + NewAttributes = GetPermissionAttributeForMemoryType (NewType); + + if (OldAttributes == NewAttributes) { + return EFI_SUCCESS; + } + + return gCpu->SetMemoryAttributes (gCpu, Memory, Length, NewAttributes); +} + +/** Allocates pages from the memory map. @param Type The type of allocation to perform @@ -1344,6 +1439,8 @@ CoreAllocatePages ( NULL ); InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); + ApplyMemoryProtection (EfiConventionalMemory, MemoryType, *Memory, + EFI_PAGES_TO_SIZE (NumberOfPages)); } return Status; } @@ -1460,6 +1557,8 @@ CoreFreePages ( NULL ); InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); + ApplyMemoryProtection (MemoryType, EfiConventionalMemory, Memory, + EFI_PAGES_TO_SIZE (NumberOfPages)); } return Status; } @@ -1856,6 +1955,9 @@ CoreAllocatePoolPages ( DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages)); } else { CoreConvertPages (Start, NumberOfPages, PoolType); + + ApplyMemoryProtection (EfiConventionalMemory, PoolType, Start, + EFI_PAGES_TO_SIZE (NumberOfPages)); } return (VOID *)(UINTN) Start; @@ -1877,6 +1979,9 @@ CoreFreePoolPages ( ) { CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory); + + ApplyMemoryProtection (PoolType, EfiConventionalMemory, Memory, + EFI_PAGES_TO_SIZE (NumberOfPages)); } diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c index c36612a1b1f2..1142dcc5a83d 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c @@ -639,6 +639,97 @@ UnprotectUefiImage ( } /** + Remove exec permissions from all regions whose type is identified by + PcdDxeMemoryProtectionPolicy +**/ +STATIC +VOID +ApplyDxeMemoryProtectionPolicy ( + VOID + ) +{ + UINTN MemoryMapSize; + UINTN MapKey; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; + EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; + EFI_STATUS Status; + + // + // 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: removing exec permissions from memory regions\n", + __FUNCTION__)); + + for (MemoryMapEntry = MemoryMap, + MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize); + (UINTN) MemoryMapEntry < (UINTN) MemoryMapEnd; + MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize)) { + + switch (MemoryMapEntry->Type) { + case EfiBootServicesCode: + case EfiRuntimeServicesCode: + case EfiLoaderCode: + continue; + + default: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT0) == 0) { + continue; + } + break; + + case EfiReservedMemoryType: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT1) == 0) { + continue; + } + break; + + case EfiACPIMemoryNVS: + if ((PcdGet32 (PcdDxeMemoryProtectionPolicy) & BIT2) == 0) { + continue; + } + break; + } + + SetUefiImageMemoryAttributes ( + MemoryMapEntry->PhysicalStart, + EFI_PAGES_TO_SIZE (MemoryMapEntry->NumberOfPages), + EFI_MEMORY_XP); + } + FreePool (MemoryMap); +} + + +/** A notification for CPU_ARCH protocol. @param[in] Event Event whose notification function is being invoked. @@ -666,6 +757,17 @@ MemoryProtectionCpuArchProtocolNotify ( return; } + // + // Apply the memory protection policy on non-BScode/RTcode regions. + // + if (PcdGet32 (PcdDxeMemoryProtectionPolicy) != 0) { + ApplyDxeMemoryProtectionPolicy (); + } + + if (mImageProtectionPolicy == 0) { + return; + } + Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiLoadedImageProtocolGuid, @@ -745,7 +847,7 @@ CoreInitializeMemoryProtection ( mImageProtectionPolicy = PcdGet32(PcdImageProtectionPolicy); - if (mImageProtectionPolicy != 0) { + if (mImageProtectionPolicy != 0 || PcdGet32 (PcdDxeMemoryProtectionPolicy) != 0) { Status = CoreCreateEvent ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK,