From 40b43134e35dd1423a6d0d73addda00864fe2938 Mon Sep 17 00:00:00 2001 From: vit9696 Date: Mon, 5 Aug 2019 23:46:28 +0300 Subject: [PATCH] OcAppleBootCompatLib: Implement custom slide support --- Include/Library/OcAppleBootCompatLib.h | 1 + Include/Library/OcCpuLib.h | 12 +- Include/Library/OcMemoryLib.h | 49 +- .../OcAppleBootCompatLib/BootCompatInternal.h | 140 +++- Library/OcAppleBootCompatLib/CustomSlide.c | 783 ++++++++++++++++++ Library/OcAppleBootCompatLib/KernelSupport.c | 7 +- .../OcAppleBootCompatLib.inf | 9 +- .../OcAppleBootCompatLib/ServiceOverrides.c | 39 +- .../OcAppleBootCompatLib/X64/ContextSwitch.h | 5 + Library/OcCpuLib/OcCpuLib.c | 56 +- Library/OcMemoryLib/MemoryMap.c | 42 +- 11 files changed, 1090 insertions(+), 53 deletions(-) create mode 100644 Library/OcAppleBootCompatLib/CustomSlide.c diff --git a/Include/Library/OcAppleBootCompatLib.h b/Include/Library/OcAppleBootCompatLib.h index 80e3b4df..586c0870 100644 --- a/Include/Library/OcAppleBootCompatLib.h +++ b/Include/Library/OcAppleBootCompatLib.h @@ -26,6 +26,7 @@ typedef struct OC_ABC_SETTINGS_ { BOOLEAN SetupAppleMap; /// /// Provide custom Apple KASLR slide calculation for firmwares with polluted low memory ranges. + /// This also ensures that slide= argument is never passed to the operating system. /// BOOLEAN SetupAppleSlide; /// diff --git a/Include/Library/OcCpuLib.h b/Include/Library/OcCpuLib.h index 81c11728..a92d639c 100755 --- a/Include/Library/OcCpuLib.h +++ b/Include/Library/OcCpuLib.h @@ -101,11 +101,21 @@ OcCpuCorrectFlexRatio ( @param[in] VersionEax CPUID (1) EAX value. - @return Apple Family (e.g. CPUFAMILY_UNKNOWN) + @retval Apple Family (e.g. CPUFAMILY_UNKNOWN) **/ UINT32 OcCpuModelToAppleFamily ( IN CPUID_VERSION_INFO_EAX VersionEax ); +/** + Special Intel Sandy Bridge and Ivy Bridge detection code. + + @retval TRUE when running with Sandy Bridge or Ivy Bridge CPU. +**/ +BOOLEAN +OcIsSandyOrIvy ( + VOID + ); + #endif // OC_CPU_LIB_H_ diff --git a/Include/Library/OcMemoryLib.h b/Include/Library/OcMemoryLib.h index 8b6d1acb..3d60ba8a 100644 --- a/Include/Library/OcMemoryLib.h +++ b/Include/Library/OcMemoryLib.h @@ -18,6 +18,32 @@ #include #include +/** + Reverse equivalent of NEXT_MEMORY_DESCRIPTOR. +**/ +#define PREV_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ + ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) + + +/** + Get last descriptor address. + It is assumed that the descriptor contains pages. +**/ +#define LAST_DESCRIPTOR_ADDR(Desc) \ + ((Desc)->PhysicalStart + (EFI_PAGES_TO_SIZE ((UINTN) (Desc)->NumberOfPages) - 1)) + +/** + Check if area is within the specified descriptor. + It is assumed that the descriptor contains pages and AreaSize is not 0. +**/ +#define AREA_WITHIN_DESCRIPTOR(Desc, Area, AreaSize) \ + ((Area) >= (Desc)->PhysicalStart && ((Area) + ((AreaSize) - 1)) <= LAST_DESCRIPTOR_ADDR (Desc)) + +/** + Reasonable default virtual memory page pool size (2 MB). +**/ +#define OC_DEFAULT_VMEM_PAGE_COUNT 0x200 + /** Lock the legacy region specified to enable modification. @@ -138,6 +164,24 @@ AllocatePagesFromTop ( IN CHECK_ALLOCATION_RANGE CheckRange OPTIONAL ); +/** + Calculate number of runtime pages in the memory map. + + @param[in] MemoryMapSize Memory map size in bytes. + @param[in] MemoryMap Memory map to inspect. + @param[in] DescriptorSize Memory map descriptor size in bytes. + @param[out] DescriptorCount Number of relevant descriptors, optional. + + @retval Number of runtime pages. +**/ +UINTN +CountRuntimePages ( + IN UINTN MemoryMapSize, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize, + OUT UINTN *DescriptorCount OPTIONAL + ); + /** Return pointer to PML4 table in PageTable and PWT and PCD flags in Flags. @@ -180,11 +224,6 @@ typedef struct OC_VMEM_CONTEXT_ { UINTN FreePages; } OC_VMEM_CONTEXT; -/** - Reasonable default virtual memory page pool size (2 MB). -**/ -#define OC_DEFAULT_VMEM_PAGE_COUNT 0x200 - /** Allocate EfiBootServicesData virtual memory pool from boot services in the end of BASE_4GB of RAM. Should be called while boot services are diff --git a/Library/OcAppleBootCompatLib/BootCompatInternal.h b/Library/OcAppleBootCompatLib/BootCompatInternal.h index cb1e7598..9835858f 100644 --- a/Library/OcAppleBootCompatLib/BootCompatInternal.h +++ b/Library/OcAppleBootCompatLib/BootCompatInternal.h @@ -16,9 +16,14 @@ #define BOOT_COMPAT_INTERNAL_H #include + +#include + #include +#include #include #include + #include #ifdef MDE_CPU_X64 @@ -42,35 +47,42 @@ /** Kernel __HIB segment virtual address. **/ -#define KERNEL_HIB_VADDR ((UINTN)0xFFFFFF8000100000ULL) +#define KERNEL_HIB_VADDR ((UINTN) 0xFFFFFF8000100000ULL) /** Kernel __TEXT segment virtual address. **/ -#define KERNEL_TEXT_VADDR ((UINTN)0xFFFFFF8000200000ULL) +#define KERNEL_TEXT_VADDR ((UINTN) 0xFFFFFF8000200000ULL) + +/** + Kernel physical base address. +**/ +#define KERNEL_BASE_PADDR (KERNEL_TEXT_VADDR - KERNEL_HIB_VADDR) + /** Slide offset per slide entry **/ -#define SLIDE_GRANULARITY ((UINTN)0x200000) +#define SLIDE_GRANULARITY ((UINTN) 0x200000) /** Total possible number of KASLR slide offsets. **/ -#define TOTAL_SLIDE_NUM 256 +#define TOTAL_SLIDE_NUM ((UINTN) 0x100) /** - Get last descriptor address. - It is assumed that the descriptor contains pages. + Slide errate number to skip range from. **/ -#define LAST_DESCRIPTOR_ADDR(Desc) \ - ((Desc)->PhysicalStart + (EFI_PAGES_TO_SIZE ((UINTN) (Desc)->NumberOfPages) - 1)) +#define SLIDE_ERRATA_NUM ((UINTN) 0x80) /** - Check if area is within the specified descriptor. - It is assumed that the descriptor contains pages and AreaSize is not 0. + Sandy/Ivy skip slide range for Intel HD graphics. **/ -#define AREA_WITHIN_DESCRIPTOR(Desc, Area, AreaSize) \ - ((Area) >= (Desc)->PhysicalStart && ((Area) + ((AreaSize) - 1)) <= LAST_DESCRIPTOR_ADDR (Desc)) +#define SLIDE_ERRATA_SKIP_RANGE ((UINTN) 0x10200000) + +/** + Assume the kernel is roughly 128 MBs. +**/ +#define ESTIMATED_KERNEL_SIZE ((UINTN) 0x8000000) /** Preserved relocation entry. @@ -220,6 +232,53 @@ typedef struct KERNEL_SUPPORT_STATE_ { UINTN VmMapDescSize; } KERNEL_SUPPORT_STATE; +/** + Apple booter KASLR slide support internal state. +**/ +typedef struct SLIDE_SUPPORT_STATE_ { + /// + /// Memory map analysis status determining slide usage. + /// + BOOLEAN HasMemoryMapAnalysis; + /// + /// TRUE if we are running on Intel Sandy or Ivy bridge. + /// + BOOLEAN HasSandyOrIvy; + /// + /// TRUE if CsrActiveConfig was set. + /// + BOOLEAN HasCsrActiveConfig; + /// + /// TRUE if BootArgs was set. + /// + BOOLEAN HasBootArgs; + /// + /// Read or assumed csr-arctive-config variable value. + /// + UINT32 CsrActiveConfig; + /// + /// Valid slides to choose from when using custom slide. + /// + UINT8 ValidSlides[TOTAL_SLIDE_NUM]; + /// + /// Number of entries in ValidSlides. + /// + UINT32 ValidSlideCount; + /// + /// Apple kernel boot arguments read from boot-args variable and then + /// modified with an additional slide parameter in case custom slide is used. + /// + CHAR8 BootArgs[BOOT_LINE_LENGTH]; + /// + /// BootArgs data size. + /// + UINTN BootArgsSize; + /// + /// Estimated size for kernel itself, device tree, memory map, and rt pages. + /// + UINTN EstimatedKernelArea; +} SLIDE_SUPPORT_STATE; + /** Apple Boot Compatibility context. **/ @@ -244,6 +303,10 @@ typedef struct BOOT_COMPAT_CONTEXT_ { /// Apple kernel support internal state. /// KERNEL_SUPPORT_STATE KernelState; + /// + /// Apple booter KASLR slide support internal state. + /// + SLIDE_SUPPORT_STATE SlideSupport; } BOOT_COMPAT_CONTEXT; /** @@ -350,4 +413,57 @@ AppleMapPrepareKernelState ( IN BOOLEAN ModeX64 ); +/** + Patch boot.efi to support random and passed slide values in safe mode. + + @param[in,out] ImageBase Apple booter image base. + @param[in] ImageSize Apple booter image size. +**/ +VOID +AppleSlideUnlockForSafeMode ( + IN OUT UINT8 *ImageBase, + IN UINTN ImageSize + ); + +/** + Primary custom KASLR support handler. This gets called on every + UEFI RuntimeServices GetVariable call and thus is useful to + perform KASLR slide injection through boot-args. + + @param[in,out] BootCompat Boot compatibility context. + @param[in] GetVariable Original UEFI GetVariable service. + @param[in] GetMemoryMap Unmodified GetMemoryMap pointer, optional. + @param[in] VariableName GetVariable variable name argument. + @param[in] VendorGuid GetVariable vendor GUID argument. + @param[out] Attributes GetVariable attributes argument. + @param[in,out] DataSize GetVariable data size argument. + @param[out] Data GetVariable data argument. + + @retval GetVariable status code. +**/ +EFI_STATUS +AppleSlideGetVariable ( + IN OUT BOOT_COMPAT_CONTEXT *BootCompat, + IN EFI_GET_VARIABLE GetVariable, + IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ); + +/** + Ensures that the original csr-active-config is passed to the kernel, + and removes customised slide value for security reasons. + + @param[in,out] BootCompat Boot compatibility context. + @param[in,out] BootArgs Apple kernel boot arguments. +**/ +VOID +AppleSlideRestore ( + IN OUT BOOT_COMPAT_CONTEXT *BootCompat, + IN OUT OC_BOOT_ARGUMENTS *BootArgs + ); + #endif // BOOT_COMPAT_INTERNAL_H diff --git a/Library/OcAppleBootCompatLib/CustomSlide.c b/Library/OcAppleBootCompatLib/CustomSlide.c new file mode 100644 index 00000000..b2697d97 --- /dev/null +++ b/Library/OcAppleBootCompatLib/CustomSlide.c @@ -0,0 +1,783 @@ +/** @file + Copyright (C) 2018, Downlod-Fritz. All rights reserved. + Copyright (C) 2018, vit9696. All rights reserved. + + All rights reserved. + + 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 "BootCompatInternal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + Obtain estimated kernel area start and end addresses for + specified slide number. + + @param[in] SlideSupport Slide support state. + @param[in] Slide Slide number. + @param[out] StartAddr Starting address. + @param[out] EndAddr Ending address (not inclusive). +**/ +STATIC +VOID +GetSlideRangeForValue ( + IN SLIDE_SUPPORT_STATE *SlideSupport, + IN UINTN Slide, + OUT UINTN *StartAddr, + OUT UINTN *EndAddr + ) +{ + *StartAddr = Slide * SLIDE_GRANULARITY + KERNEL_BASE_PADDR; + + // + // Skip ranges used by Intel HD 2000/3000. + // + if (Slide >= SLIDE_ERRATA_NUM && SlideSupport->HasSandyOrIvy) { + *StartAddr += SLIDE_ERRATA_SKIP_RANGE; + } + + *EndAddr = *StartAddr + SlideSupport->EstimatedKernelArea; +} + +/** + Generate more or less random slide value. + + @param[in] SlideSupport Slide support state. +**/ +STATIC +UINT8 +GenerateSlideValue ( + IN SLIDE_SUPPORT_STATE *SlideSupport + ) +{ + UINT64 Slide; + + do { + Slide = GetPseudoRandomNumber64 () % SlideSupport->ValidSlideCount; + } while (Slide == 0); + + return SlideSupport->ValidSlides[Slide]; +} + +/** + Decide on whether to use custom slide based on memory map analysis. + This additionally logs the decision through standard services. + + @param[in,out] SlideSupport Slide support state. + @param[in] FallbackSlide Fallback slide number with largest area. + @param[in] MaxAvailableSize Maximum available contiguous area. + + @retval TRUE in case custom slide is to be used. +**/ +STATIC +BOOLEAN +ShouldUseCustomSlideOffsetDecision ( + IN OUT SLIDE_SUPPORT_STATE *SlideSupport, + IN UINT8 FallbackSlide, + IN UINTN MaxAvailableSize + ) +{ + UINTN Index; + UINTN NumEntries; + CHAR8 SlideList[256]; + CHAR8 Temp[32]; + + // + // All slides are available. + // + if (SlideSupport->ValidSlideCount == TOTAL_SLIDE_NUM) { + return FALSE; + } + + // + // No slides are available, fallback to largest. + // + if (SlideSupport->ValidSlideCount == 0) { + DEBUG (( + DEBUG_WARN, + "OCABC: No slide values are usable! Falling back to %u with 0x%08X bytes!\n", + (UINT32) FallbackSlide, + MaxAvailableSize + )); + SlideSupport->ValidSlides[SlideSupport->ValidSlideCount++] = (UINT8) FallbackSlide; + return TRUE; + } + + // + // Not all slides are available and thus we have to pass a custom slide + // value through boot-args to boot reliably. + // + // Pretty-print valid slides as ranges. + // For example, 1, 2, 3, 4, 5 will become 1-5. + // + DEBUG (( + DEBUG_WARN, + "OCABC: Only %u/%u slide values are usable!\n", + (UINT32) SlideSupport->ValidSlideCount, + (UINT32) TOTAL_SLIDE_NUM + )); + + SlideList[0] = '\0'; + + NumEntries = 0; + for (Index = 0; Index <= SlideSupport->ValidSlideCount; ++Index) { + if (Index == 0) { + AsciiSPrint ( + Temp, + sizeof (Temp), + "Valid slides - %d", + SlideSupport->ValidSlides[Index] + ); + AsciiStrCatS (SlideList, sizeof (SlideList), Temp); + } else if (Index == SlideSupport->ValidSlideCount + || SlideSupport->ValidSlides[Index - 1] + 1 != SlideSupport->ValidSlides[Index]) { + + if (NumEntries == 1) { + AsciiSPrint ( + Temp, + sizeof (Temp), + ", %d", + SlideSupport->ValidSlides[Index - 1] + ); + AsciiStrCatS (SlideList, sizeof (SlideList), Temp); + } else if (NumEntries > 1) { + AsciiSPrint ( + Temp, + sizeof (Temp), + "-%d", + SlideSupport->ValidSlides[Index - 1] + ); + AsciiStrCatS (SlideList, sizeof (SlideList), Temp); + } + + if (Index != SlideSupport->ValidSlideCount) { + AsciiSPrint ( + Temp, + sizeof (Temp), + ", %d", + SlideSupport->ValidSlides[Index] + ); + AsciiStrCatS (SlideList, sizeof (SlideList), Temp); + } + + NumEntries = 0; + } else { + NumEntries++; + } + } + + DEBUG ((DEBUG_WARN, "OCABC: %a\n", SlideList)); + + return TRUE; +} + +/** + Return cached decision or perform memory map analysis to decide + whether to use custom slide for reliable kernel booting or not. + + @param[in,out] SlideSupport Slide support state. + @param[in] FallbackSlide Fallback slide number with largest area. + @param[in] MaxAvailableSize Maximum available contiguous area. + + @retval TRUE in case custom slide is to be used. +**/ +STATIC +BOOLEAN +ShouldUseCustomSlideOffset ( + IN OUT SLIDE_SUPPORT_STATE *SlideSupport, + IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL + ) +{ + UINTN AllocatedMapPages; + UINTN MemoryMapSize; + EFI_MEMORY_DESCRIPTOR *MemoryMap; + EFI_MEMORY_DESCRIPTOR *Desc; + UINTN MapKey; + EFI_STATUS Status; + UINTN DescriptorSize; + UINT32 DescriptorVersion; + UINTN Index; + UINTN Slide; + UINTN NumEntries; + UINTN MaxAvailableSize; + UINT8 FallbackSlide; + BOOLEAN Supported; + UINTN StartAddr; + UINTN EndAddr; + UINTN DescEndAddr; + UINTN AvailableSize; + + MaxAvailableSize = 0; + FallbackSlide = 0; + + if (SlideSupport->HasMemoryMapAnalysis) { + return SlideSupport->ValidSlideCount > 0 + && SlideSupport->ValidSlideCount < TOTAL_SLIDE_NUM; + } + + AllocatedMapPages = BASE_4GB; + Status = GetCurrentMemoryMapAlloc ( + &MemoryMapSize, + &MemoryMap, + &MapKey, + &DescriptorSize, + &DescriptorVersion, + GetMemoryMap, + &AllocatedMapPages + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OCABC: Failed to obtain memory map for KASLR - %r\n", Status)); + return FALSE; + } + + SlideSupport->HasSandyOrIvy = OcIsSandyOrIvy (); + SlideSupport->EstimatedKernelArea = EFI_PAGES_TO_SIZE ( + CountRuntimePages (MemoryMapSize, MemoryMap, DescriptorSize, NULL) + ) + ESTIMATED_KERNEL_SIZE; + + // + // At this point we have a memory map that we could use to + // determine what slide values are allowed. + // + NumEntries = MemoryMapSize / DescriptorSize; + + // + // Reset valid slides to zero and find actually working ones. + // + SlideSupport->ValidSlideCount = 0; + + for (Slide = 0; Slide < TOTAL_SLIDE_NUM; ++Slide) { + Desc = MemoryMap; + Supported = TRUE; + + GetSlideRangeForValue ( + SlideSupport, + Slide, + &StartAddr, + &EndAddr + ); + + AvailableSize = 0; + + for (Index = 0; Index < NumEntries; ++Index) { + if (Desc->NumberOfPages == 0) { + continue; + } + + DescEndAddr = LAST_DESCRIPTOR_ADDR (Desc) + 1; + + if ((Desc->PhysicalStart < EndAddr) && (DescEndAddr > StartAddr)) { + // + // The memory overlaps with the slide region. + // + if (Desc->Type != EfiConventionalMemory) { + // + // The memory is unusable atm. + // + Supported = FALSE; + break; + } else { + // + // The memory will be available for the kernel. + // + AvailableSize += EFI_PAGES_TO_SIZE (Desc->NumberOfPages); + + if (Desc->PhysicalStart < StartAddr) { + // + // The region starts before the slide region. + // Subtract the memory that is located before the slide region. + // + AvailableSize -= (StartAddr - Desc->PhysicalStart); + } + + if (DescEndAddr > EndAddr) { + // + // The region ends after the slide region. + // Subtract the memory that is located after the slide region. + // + AvailableSize -= (DescEndAddr - EndAddr); + } + } + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } + + if (AvailableSize > MaxAvailableSize) { + MaxAvailableSize = AvailableSize; + FallbackSlide = (UINT8) Slide; + } + + if ((StartAddr + AvailableSize) != EndAddr) { + // + // The slide region is not continuous. + // + Supported = FALSE; + } + + if (Supported) { + SlideSupport->ValidSlides[SlideSupport->ValidSlideCount++] = (UINT8) Slide; + } + } + + // + // Okay, we are done. + // + + SlideSupport->HasMemoryMapAnalysis = TRUE; + + gBS->FreePages ( + (EFI_PHYSICAL_ADDRESS) MemoryMap, + AllocatedMapPages + ); + + return ShouldUseCustomSlideOffsetDecision ( + SlideSupport, + FallbackSlide, + MaxAvailableSize + ); +} + +/** + UEFI GetVariable override specific to csr-active-config. + See caller for more details. + + @param[in,out] SlideSupport Slide support state. + @param[in] GetVariable Original UEFI GetVariable service. + @param[in] VariableName GetVariable variable name argument. + @param[in] VendorGuid GetVariable vendor GUID argument. + @param[out] Attributes GetVariable attributes argument. + @param[in,out] DataSize GetVariable data size argument. + @param[out] Data GetVariable data argument. + + @retval GetVariable status code. +**/ +STATIC +EFI_STATUS +GetVariableCsrActiveConfig ( + IN OUT SLIDE_SUPPORT_STATE *SlideSupport, + IN EFI_GET_VARIABLE GetVariable, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINT32 *Config; + + // + // If we were asked for the size, just return it right away. + // + if (Data == NULL || *DataSize < sizeof (UINT32)) { + *DataSize = sizeof (UINT32); + return EFI_BUFFER_TOO_SMALL; + } + + Config = (UINT32 *) Data; + + // + // Otherwise call the original function. + // + Status = GetVariable (VariableName, VendorGuid, Attributes, DataSize, Data); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OCABC: GetVariable csr-active-config - %r\n", Status)); + + *Config = 0; + Status = EFI_SUCCESS; + if (Attributes != NULL) { + *Attributes = + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE; + } + } + + // + // We must unrestrict NVRAM from SIP or slide=X will not be supported. + // + SlideSupport->CsrActiveConfig = *Config; + SlideSupport->HasCsrActiveConfig = TRUE; + *Config |= CSR_ALLOW_UNRESTRICTED_NVRAM; + + return Status; +} + +/** + UEFI GetVariable override specific to boot-args. + See caller for more details. + + @param[in,out] SlideSupport Slide support state. + @param[in] GetVariable Original UEFI GetVariable service. + @param[in] VariableName GetVariable variable name argument. + @param[in] VendorGuid GetVariable vendor GUID argument. + @param[out] Attributes GetVariable attributes argument. + @param[in,out] DataSize GetVariable data size argument. + @param[out] Data GetVariable data argument. + + @retval GetVariable status code. +**/ +STATIC +EFI_STATUS +GetVariableBootArgs ( + IN OUT SLIDE_SUPPORT_STATE *SlideSupport, + IN EFI_GET_VARIABLE GetVariable, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + EFI_STATUS Status; + UINTN StoredBootArgsSize; + UINT8 Slide; + CHAR8 SlideArgument[10]; + UINTN SlideArgumentLength; + + StoredBootArgsSize = BOOT_LINE_LENGTH; + SlideArgumentLength = ARRAY_SIZE (SlideArgument) - 1; + + if (!SlideSupport->HasBootArgs) { + Slide = GenerateSlideValue (SlideSupport); + + // + // boot-args normally arrives non-null terminated. + // + Status = GetVariable ( + VariableName, + VendorGuid, + Attributes, + &StoredBootArgsSize, + SlideSupport->BootArgs + ); + if (EFI_ERROR (Status)) { + SlideSupport->BootArgs[0] = '\0'; + } + + // + // Note, the point is to always pass 3 characters to avoid side attacks on value length. + // boot.efi always reads in decimal, so 008 and 8 are equivalent. + // + AsciiSPrint (SlideArgument, ARRAY_SIZE (SlideArgument), "slide=%-03d", Slide); + + if (!OcAppendArgumentToCmd (SlideSupport->BootArgs, SlideArgument, SlideArgumentLength)) { + // + // Broken boot-args, try to overwrite. + // + AsciiStrnCpyS ( + SlideSupport->BootArgs, + SlideArgumentLength + 1, + SlideArgument, + SlideArgumentLength + 1 + ); + } + + SlideSupport->BootArgsSize = AsciiStrLen (SlideSupport->BootArgs); + SlideSupport->HasBootArgs = TRUE; + } + + if (Attributes) { + *Attributes = + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE; + } + + if (*DataSize >= SlideSupport->BootArgsSize && Data != NULL) { + CopyMem ( + Data, + SlideSupport->BootArgs, + SlideSupport->BootArgsSize + ); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *DataSize = SlideSupport->BootArgsSize; + + return Status; +} + +/** + Erases customised slide value from everywhere accessible + for security purposes. + + @param[in,out] BootCompat Boot compatibility context. + @param[in,out] BootArgs Apple kernel boot arguments. +**/ +STATIC +VOID +HideSlideFromOs ( + IN OUT SLIDE_SUPPORT_STATE *SlideSupport, + IN OUT OC_BOOT_ARGUMENTS *BootArgs + ) +{ + EFI_STATUS Status; + DTEntry Chosen; + CHAR8 *ArgsStr; + UINT32 ArgsSize; + + // + // First, there is a BootArgs entry for XNU. + // + OcRemoveArgumentFromCmd (BootArgs->CommandLine, "slide="); + + // + // Second, there is a DT entry. + // + DTInit ((VOID *)(UINTN) (*BootArgs->DeviceTreeP), BootArgs->DeviceTreeLength); + Status = DTLookupEntry (NULL, "/chosen", &Chosen); + if (!EFI_ERROR (Status)) { + Status = DTGetProperty (Chosen, "boot-args", (VOID **)&ArgsStr, &ArgsSize); + if (!EFI_ERROR (Status) && ArgsSize > 0) { + OcRemoveArgumentFromCmd (ArgsStr, "slide="); + } + } + + // + // Third, clean the boot args just in case. + // + SlideSupport->ValidSlideCount = 0; + SlideSupport->BootArgsSize = 0; + ZeroMem (SlideSupport->ValidSlides, sizeof (SlideSupport->ValidSlides)); + ZeroMem (SlideSupport->BootArgs, sizeof (SlideSupport->BootArgs)); +} + +VOID +AppleSlideUnlockForSafeMode ( + IN OUT UINT8 *ImageBase, + IN UINTN ImageSize + ) +{ + // + // boot.efi performs the following check: + // if (State & (BOOT_MODE_SAFE | BOOT_MODE_ASLR)) == (BOOT_MODE_SAFE | BOOT_MODE_ASLR)) { + // * Disable KASLR * + // } + // We do not care about the asm it will use for it, but we could assume that the constants + // will be used twice and their location will be very close to each other. + // + // BOOT_MODE_SAFE | BOOT_MODE_ASLR constant is 0x4001 in hex. + // It has not changed since its appearance, so is most likely safe to look for. + // Furthermore, since boot.efi state mask uses higher bits, it is safe to assume that + // the comparison will be at least 32-bit. + // + // + // The new way patch is a workaround for 10.13.5 and newer, where the code got finally changed. + // if (State & BOOT_MODE_SAFE) { + // ReportFeature(FEATURE_BOOT_MODE_SAFE); + // if (State & BOOT_MODE_ASLR) { + // * Disable KASLR * + // } + // } + // + + // + // This is a reasonable maximum distance to expect between the instructions. + // + STATIC CONST UINTN MaxDist = 0x10; + STATIC CONST UINT8 SearchSeqNew[] = {0xF6, 0xC4, 0x40, 0x75}; + STATIC CONST UINT8 SearchSeqNew2[] = {0x0F, 0xBA, 0xE0, 0x0E, 0x72}; + STATIC CONST UINT8 SearchSeq[] = {0x01, 0x40, 0x00, 0x00}; + + UINT8 *StartOff; + UINT8 *EndOff; + UINTN FirstOff; + UINTN SecondOff; + UINTN SearchSeqNewSize; + BOOLEAN NewWay; + + + StartOff = ImageBase; + EndOff = StartOff + ImageSize - sizeof (SearchSeq) - MaxDist; + + FirstOff = 0; + SecondOff = 0; + NewWay = FALSE; + + do { + while (StartOff + FirstOff <= EndOff) { + if (StartOff + FirstOff <= EndOff - 1 + && CompareMem (StartOff + FirstOff, SearchSeqNew2, sizeof (SearchSeqNew2)) == 0) { + SearchSeqNewSize = sizeof (SearchSeqNew2); + NewWay = TRUE; + break; + } else if (CompareMem (StartOff + FirstOff, SearchSeqNew, sizeof (SearchSeqNew)) == 0) { + SearchSeqNewSize = sizeof (SearchSeqNew); + NewWay = TRUE; + break; + } else if (CompareMem (StartOff + FirstOff, SearchSeq, sizeof (SearchSeq)) == 0) { + break; + } + FirstOff++; + } + + DEBUG (( + DEBUG_VERBOSE, + "OCABC: Found first %d at off %X\n", + (UINT32) NewWay, + (UINT32) FirstOff + )); + + if (StartOff + FirstOff > EndOff) { + DEBUG (( + DEBUG_INFO, + "OCABC: Failed to find first BOOT_MODE_SAFE | BOOT_MODE_ASLR sequence\n" + )); + break; + } + + if (NewWay) { + // + // Here we just patch the comparison code and the check by straight nopping. + // + DEBUG ((DEBUG_VERBOSE, "OCABC: Patching new safe mode aslr check...\n")); + SetMem (StartOff + FirstOff, SearchSeqNewSize + 1, 0x90); + return; + } + + SecondOff = FirstOff + sizeof (SearchSeq); + + while ( + StartOff + SecondOff <= EndOff && FirstOff + MaxDist >= SecondOff && + CompareMem (StartOff + SecondOff, SearchSeq, sizeof (SearchSeq))) { + SecondOff++; + } + + DEBUG ((DEBUG_VERBOSE, "OCABC: Found second at off %X\n", (UINT32) SecondOff)); + + if (FirstOff + MaxDist < SecondOff) { + DEBUG ((DEBUG_VERBOSE, "OCABC: Trying next match...\n")); + SecondOff = 0; + FirstOff += sizeof (SearchSeq); + } + } while (SecondOff == 0); + + if (SecondOff != 0) { + // + // Here we use 0xFFFFFFFF constant as a replacement value. + // Since the state values are contradictive (e.g. safe & single at the same time) + // We are allowed to use this instead of to simulate if (false). + // + DEBUG ((DEBUG_VERBOSE, "OCABC: Patching safe mode aslr check...\n")); + SetMem (StartOff + FirstOff, sizeof (SearchSeq), 0xFF); + SetMem (StartOff + SecondOff, sizeof (SearchSeq), 0xFF); + } +} + +EFI_STATUS +AppleSlideGetVariable ( + IN OUT BOOT_COMPAT_CONTEXT *BootCompat, + IN EFI_GET_VARIABLE GetVariable, + IN EFI_GET_MEMORY_MAP GetMemoryMap OPTIONAL, + IN CHAR16 *VariableName, + IN EFI_GUID *VendorGuid, + OUT UINT32 *Attributes OPTIONAL, + IN OUT UINTN *DataSize, + OUT VOID *Data + ) +{ + if (VariableName != NULL && VendorGuid != NULL && DataSize != NULL + && CompareGuid (VendorGuid, &gAppleBootVariableGuid)) { + + if (StrCmp (VariableName, L"csr-active-config") == 0) { + // + // We override csr-active-config with CSR_ALLOW_UNRESTRICTED_NVRAM bit set + // to allow one to pass a custom slide value even when SIP is on. + // This original value of csr-active-config is returned to OS at XNU boot. + // This allows SIP to be fully enabled in the operating system. + // + return GetVariableCsrActiveConfig ( + &BootCompat->SlideSupport, + GetVariable, + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } else if (StrCmp (VariableName, L"boot-args") == 0 + && !BootCompat->ServiceState.AppleCustomSlide + && ShouldUseCustomSlideOffset (&BootCompat->SlideSupport, GetMemoryMap)) { + // + // When we cannot allow some KASLR values due to used address we generate + // a random slide value among the valid options, which we we pass via boot-args. + // See ShouldUseCustomSlideOffset for more details. + // + // We delay memory map analysis as much as we can, in case boot.efi or anything else allocates + // stuff with gBS->AllocatePool and it overlaps with the kernel area. + // Overriding AllocatePool with a custom allocator does not really improve the situation, + // because on older boards allocated memory above BASE_4GB causes instant reboots, and + // on the only (so far) problematic X99 and X299 we have no free region for our pool anyway. + // In any case, the current APTIOFIX_SPECULATED_KERNEL_SIZE value appears to work reliably. + // + return GetVariableBootArgs ( + &BootCompat->SlideSupport, + GetVariable, + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } + } + + return GetVariable (VariableName, VendorGuid, Attributes, DataSize, Data); +} + +VOID +AppleSlideRestore ( + IN OUT BOOT_COMPAT_CONTEXT *BootCompat, + IN OUT OC_BOOT_ARGUMENTS *BootArgs + ) +{ + SLIDE_SUPPORT_STATE *SlideSupport; + + SlideSupport = &BootCompat->SlideSupport; + + // + // Restore csr-active-config to a value it was before our slide=X alteration. + // + if (BootArgs->CsrActiveConfig != NULL && SlideSupport->HasCsrActiveConfig) { + *BootArgs->CsrActiveConfig = SlideSupport->CsrActiveConfig; + } + + // + // Having slide=X values visible in the operating system defeats the purpose of KASLR. + // Since our custom implementation works by passing random KASLR slide via boot-args, + // this is especially important. + // + HideSlideFromOs (SlideSupport, BootArgs); +} diff --git a/Library/OcAppleBootCompatLib/KernelSupport.c b/Library/OcAppleBootCompatLib/KernelSupport.c index 02a54e47..8c58d4c5 100644 --- a/Library/OcAppleBootCompatLib/KernelSupport.c +++ b/Library/OcAppleBootCompatLib/KernelSupport.c @@ -335,9 +335,9 @@ AppleMapPrepareForBooting ( OcParseBootArgs (&BA, BootArgs); // - // FIXME: Restore the variables we tempered with to support custom slides. + // Restore the variables we tampered with to support custom slides. // - // RestoreCustomSlideOverrides (&BA); + AppleSlideRestore (BootCompat, &BA); MemoryMapSize = *BA.MemoryMapSize; MemoryMap = (EFI_MEMORY_DESCRIPTOR *)(UINTN) (*BA.MemoryMap); @@ -401,6 +401,7 @@ AppleMapPrepareForHibernateWake ( if (BootCompat->Settings.DiscardAppleS4Map) { // // Route 1. Discard the new memory map here, and let XNU use what it had. + // It is unknown whether there still are any firmwares that need this. // Handoff->type = kIOHibernateHandoffType; } else { @@ -521,7 +522,7 @@ AppleMapPrepareKernelJump ( // ImageAddress points to the first kernel segment, __HIB. // Kernel image header is located in __TEXT, which follows __HIB. // - ImageAddress += KERNEL_TEXT_VADDR - KERNEL_HIB_VADDR; + ImageAddress += KERNEL_BASE_PADDR; // // Cut higher virtual address bits. diff --git a/Library/OcAppleBootCompatLib/OcAppleBootCompatLib.inf b/Library/OcAppleBootCompatLib/OcAppleBootCompatLib.inf index 3f29ae22..6a6af9af 100644 --- a/Library/OcAppleBootCompatLib/OcAppleBootCompatLib.inf +++ b/Library/OcAppleBootCompatLib/OcAppleBootCompatLib.inf @@ -32,6 +32,7 @@ [Sources] BootCompatInternal.h + CustomSlide.c KernelSupport.c OcAppleBootCompatLib.c ServiceOverrides.c @@ -42,9 +43,10 @@ X64/ContextSwitchSupport.c [Packages] - OcSupportPkg/OcSupportPkg.dec - MdePkg/MdePkg.dec EfiPkg/EfiPkg.dec + MdePkg/MdePkg.dec + OcSupportPkg/OcSupportPkg.dec + UefiCpuPkg/UefiCpuPkg.dec [Guids] gAppleBootVariableGuid ## SOMETIMES_CONSUMES @@ -62,5 +64,8 @@ PrintLib UefiBootServicesTableLib UefiRuntimeServicesTableLib + OcCpuLib + OcDeviceTreeLib OcGuardLib OcMemoryLib + OcRngLib diff --git a/Library/OcAppleBootCompatLib/ServiceOverrides.c b/Library/OcAppleBootCompatLib/ServiceOverrides.c index 4125c4e8..5da02901 100644 --- a/Library/OcAppleBootCompatLib/ServiceOverrides.c +++ b/Library/OcAppleBootCompatLib/ServiceOverrides.c @@ -201,11 +201,10 @@ OcStartImage ( ); if (BootCompat->Settings.EnableAppleSmSlide) { - // FIXME: Implement. - //UnlockSlideSupportForSafeMode ( - // (UINT8 *) AppleLoadedImage->ImageBase, - // AppleLoadedImage->ImageSize - // ); + AppleSlideUnlockForSafeMode ( + (UINT8 *) AppleLoadedImage->ImageBase, + AppleLoadedImage->ImageSize + ); } if (BootCompat->Settings.SetupAppleMap) { @@ -493,18 +492,28 @@ OcGetVariable ( BootCompat = GetBootCompatContext (); - if (BootCompat->Settings.SetupAppleSlide) { - // FIXME: Implement + if (BootCompat->ServiceState.AppleBootNestedCount > 0 + && BootCompat->Settings.SetupAppleSlide) { + Status = AppleSlideGetVariable ( + BootCompat, + BootCompat->ServicePtrs.GetVariable, + BootCompat->ServicePtrs.GetMemoryMap, + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); + } else { + Status = BootCompat->ServicePtrs.GetVariable ( + VariableName, + VendorGuid, + Attributes, + DataSize, + Data + ); } - Status = BootCompat->ServicePtrs.GetVariable ( - VariableName, - VendorGuid, - Attributes, - DataSize, - Data - ); - return Status; } diff --git a/Library/OcAppleBootCompatLib/X64/ContextSwitch.h b/Library/OcAppleBootCompatLib/X64/ContextSwitch.h index a6772e48..55da3664 100644 --- a/Library/OcAppleBootCompatLib/X64/ContextSwitch.h +++ b/Library/OcAppleBootCompatLib/X64/ContextSwitch.h @@ -67,6 +67,11 @@ typedef PACKED struct ASM_KERNEL_JUMP_ { #pragma pack(pop) +/** + Assembly interface to save UEFI environment state in specific way. + + @param[in,out] AsmState Assembly state to update, can be preserved. +**/ VOID EFIAPI AsmAppleMapPlatformSaveState ( diff --git a/Library/OcCpuLib/OcCpuLib.c b/Library/OcCpuLib/OcCpuLib.c index c38075ad..56206ce1 100755 --- a/Library/OcCpuLib/OcCpuLib.c +++ b/Library/OcCpuLib/OcCpuLib.c @@ -34,6 +34,7 @@ #include #include +#include #include #include #include @@ -591,7 +592,7 @@ ScanIntelProcessor ( AppleMajorType = DetectAppleMajorType (Cpu->BrandString); Cpu->AppleProcessorType = DetectAppleProcessorType (Cpu->Model, Cpu->Stepping, AppleMajorType); - DEBUG ((DEBUG_INFO, "Detected Apple Processor Type: %02X -> %04X\n", AppleMajorType, Cpu->AppleProcessorType)); + DEBUG ((DEBUG_INFO, "OCCPU: Detected Apple Processor Type: %02X -> %04X\n", AppleMajorType, Cpu->AppleProcessorType)); if ((Cpu->Family != 0x06 || Cpu->Model < 0x0c) && (Cpu->Family != 0x0f || Cpu->Model < 0x03)) { @@ -637,7 +638,7 @@ ScanIntelProcessor ( DEBUG (( DEBUG_INFO, - "Ratio Min %d Max %d Current %d Turbo %d %d %d %d\n", + "OCCPU: Ratio Min %d Max %d Current %d Turbo %d %d %d %d\n", Cpu->MinBusRatio, Cpu->MaxBusRatio, Cpu->CurBusRatio, @@ -658,7 +659,7 @@ ScanIntelProcessor ( DEBUG (( DEBUG_INFO, - "%a %a %11lld %5dMHz %u * %u / %u = %ld\n", + "OCCPU: %a %a %11lld %5dMHz %u * %u / %u = %ld\n", "ART", "Frequency", Cpu->CPUFrequency, @@ -918,11 +919,11 @@ OcCpuScanProcessor ( } } - DEBUG ((DEBUG_INFO, "%a %a\n", "Found", Cpu->BrandString)); + DEBUG ((DEBUG_INFO, "OCCPU: %a %a\n", "Found", Cpu->BrandString)); DEBUG (( DEBUG_INFO, - "Signature %0X Stepping %0X Model %0X Family %0X Type %0X ExtModel %0X ExtFamily %0X\n", + "OCCPU: Signature %0X Stepping %0X Model %0X Family %0X Type %0X ExtModel %0X ExtFamily %0X\n", Cpu->Signature, Cpu->Stepping, Cpu->Model, @@ -943,7 +944,7 @@ OcCpuScanProcessor ( DEBUG (( DEBUG_INFO, - "%a %a %11lld %5dMHz\n", + "OCCPU: %a %a %11lld %5dMHz\n", "TSC", "Frequency", Cpu->TSCFrequency, @@ -952,7 +953,7 @@ OcCpuScanProcessor ( DEBUG (( DEBUG_INFO, - "%a %a %11lld %5dMHz\n", + "OCCPU: %a %a %11lld %5dMHz\n", "CPU", "Frequency", Cpu->CPUFrequency, @@ -961,7 +962,7 @@ OcCpuScanProcessor ( DEBUG (( DEBUG_INFO, - "%a %a %11lld %5dMHz\n", + "OCCPU: %a %a %11lld %5dMHz\n", "FSB", "Frequency", Cpu->FSBFrequency, @@ -970,7 +971,7 @@ OcCpuScanProcessor ( DEBUG (( DEBUG_INFO, - "Pkg %u Cores %u Threads %u\n", + "OCCPU: Pkg %u Cores %u Threads %u\n", Cpu->PackageCount, Cpu->CoreCount, Cpu->ThreadCount @@ -1056,3 +1057,40 @@ OcCpuModelToAppleFamily ( return CPUFAMILY_UNKNOWN; } } + +BOOLEAN +OcIsSandyOrIvy ( + VOID + ) +{ + CPU_MICROCODE_PROCESSOR_SIGNATURE Sig; + BOOLEAN SandyOrIvy; + UINT32 CpuFamily; + UINT32 CpuModel; + + Sig.Uint32 = 0; + + AsmCpuid (1, &Sig.Uint32, NULL, NULL, NULL); + + CpuFamily = Sig.Bits.Family; + if (CpuFamily == 15) { + CpuFamily += Sig.Bits.ExtendedFamily; + } + + CpuModel = Sig.Bits.Model; + if (CpuFamily == 15 || CpuFamily == 6) { + CpuModel |= Sig.Bits.ExtendedModel << 4; + } + + SandyOrIvy = CpuFamily == 6 && (CpuModel == 0x2A || CpuModel == 0x3A); + + DEBUG (( + DEBUG_VERBOSE, + "OCCPU: Discovered CpuFamily %d CpuModel %d SandyOrIvy %d\n", + CpuFamily, + CpuModel, + SandyOrIvy + )); + + return SandyOrIvy; +} diff --git a/Library/OcMemoryLib/MemoryMap.c b/Library/OcMemoryLib/MemoryMap.c index 17389f6f..e18ca2cf 100644 --- a/Library/OcMemoryLib/MemoryMap.c +++ b/Library/OcMemoryLib/MemoryMap.c @@ -22,12 +22,6 @@ #include #include -/** - * Reverse equivalent of NEXT_MEMORY_DESCRIPTOR. - */ -#define PREV_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ - ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) - EFI_MEMORY_DESCRIPTOR * GetCurrentMemoryMap ( OUT UINTN *MemoryMapSize, @@ -347,3 +341,39 @@ AllocatePagesFromTop ( return Status; } + +UINTN +CountRuntimePages ( + IN UINTN MemoryMapSize, + IN EFI_MEMORY_DESCRIPTOR *MemoryMap, + IN UINTN DescriptorSize, + OUT UINTN *DescriptorCount OPTIONAL + ) +{ + UINTN DescNum; + UINTN PageNum; + UINTN NumEntries; + UINTN Index; + EFI_MEMORY_DESCRIPTOR *Desc; + + DescNum = 0; + PageNum = 0; + NumEntries = MemoryMapSize / DescriptorSize; + Desc = MemoryMap; + + for (Index = 0; Index < NumEntries; ++Index) { + if (Desc->Type != EfiReservedMemoryType + && (Desc->Attribute & EFI_MEMORY_RUNTIME) != 0) { + ++DescNum; + PageNum += Desc->NumberOfPages; + } + + Desc = NEXT_MEMORY_DESCRIPTOR (Desc, DescriptorSize); + } + + if (DescriptorCount != NULL) { + *DescriptorCount = DescNum; + } + + return PageNum; +} -- GitLab