diff --git a/lldb/include/lldb/Target/ABI.h b/lldb/include/lldb/Target/ABI.h index 1a1f1724222e3..3e2e7808c6c22 100644 --- a/lldb/include/lldb/Target/ABI.h +++ b/lldb/include/lldb/Target/ABI.h @@ -152,6 +152,33 @@ class ABI : public PluginInterface { static lldb::ABISP FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch); + struct MemoryPermissions { + // Both of these are sets of lldb::Permissions values. + // Overlay are the permissions being applied to the original permissions. + uint32_t overlay; + // Effective is the result of applying the overlay to the original + // permissions. Calculating this is done by the plugin because some + // permission overlays are done as positive (add permissions) and some as + // negative (remove permissions). + uint32_t effective; + }; + + /// Get the effective memory permissions that result when the permissions + /// referred to by a protection key are applied to the original permissions. + /// + /// This is intended for architectures that have some sort of permission + /// overlay system. Where the protection key is used to look up a set of + /// permissions that modifies the original permissions. + /// + /// \returns the overlay permissions (that the protection key refers to) and + /// the effective permissions. If the target does not have an overlay + /// system, or it does and the protection key is invalid, returns nullopt. + virtual std::optional + GetMemoryPermissions(lldb_private::RegisterContext ®_ctx, + unsigned protection_key, uint32_t original_permissions) { + return std::nullopt; + } + protected: ABI(lldb::ProcessSP process_sp, std::unique_ptr info_up) : m_process_wp(process_sp), m_mc_register_info_up(std::move(info_up)) { diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 00a254f60ae19..ac91b63e378a8 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -1689,8 +1689,28 @@ class CommandObjectMemoryRegion : public CommandObjectParsed { LazyBool is_shadow_stack = range_info.IsShadowStack(); if (is_shadow_stack == eLazyBoolYes) result.AppendMessage("shadow stack: yes"); - if (std::optional protection_key = range_info.GetProtectionKey()) - result.AppendMessageWithFormatv("protection key: {0}", *protection_key); + if (std::optional protection_key = + range_info.GetProtectionKey()) { + Stream &strm = result.GetOutputStream(); + strm << llvm::formatv("protection key: {0}", *protection_key); + + if (const lldb::ABISP &abi = target.GetProcessSP()->GetABI()) { + if (auto permissions = abi->GetMemoryPermissions( + *m_exe_ctx.GetRegisterContext(), *protection_key, + range_info.GetLLDBPermissions())) { + strm << llvm::formatv( + " ({0}{1}{2}, effective: {3}{4}{5})", + permissions->overlay & lldb::ePermissionsReadable ? 'r' : '-', + permissions->overlay & lldb::ePermissionsWritable ? 'w' : '-', + permissions->overlay & lldb::ePermissionsExecutable ? 'x' : '-', + permissions->effective & lldb::ePermissionsReadable ? 'r' : '-', + permissions->effective & lldb::ePermissionsWritable ? 'w' : '-', + permissions->effective & lldb::ePermissionsExecutable ? 'x' + : '-'); + } + } + strm.PutChar('\n'); + } const std::optional> &dirty_page_list = range_info.GetDirtyPageList(); diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp index aa9c20b6bb2cf..83a777da3237e 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp +++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp @@ -884,3 +884,49 @@ void ABISysV_arm64::Initialize() { void ABISysV_arm64::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } + +std::optional +ABISysV_arm64::GetMemoryPermissions(lldb_private::RegisterContext ®_ctx, + unsigned protection_key, + uint32_t original_permissions) { + // The presence of the POR register means we have the Permission Overlay + // Extension. + // See Arm Architecture Reference manual "POR_EL0, Permission Overlay Register + // 0 (EL0)". + const RegisterInfo *por_info = reg_ctx.GetRegisterInfoByName("por"); + if (!por_info) + return std::nullopt; + + uint64_t por_value = + reg_ctx.ReadRegisterAsUnsigned(por_info, LLDB_INVALID_ADDRESS); + if (por_value == LLDB_INVALID_ADDRESS) + return std::nullopt; + + // POR contains 16, 4-bit permission sets (though Linux limits this to 8 + // useable sets). + if (protection_key >= 16) + return std::nullopt; + + // Bit 3 - reserved, bit 2 - write, bit 1 - execute, bit 0 - read. + const uint64_t por_permissions = (por_value >> (protection_key * 4)) & 0xf; + uint32_t overlay = 0; + if (por_permissions & 4) + overlay |= lldb::ePermissionsWritable; + if (por_permissions & 2) + overlay |= lldb::ePermissionsExecutable; + if (por_permissions & 1) + overlay |= lldb::ePermissionsReadable; + + uint32_t effective = original_permissions; + + // Permission overlays cannot add permissions, they can only keep, or disable, + // what was originally set. + if (!(overlay & lldb::ePermissionsWritable)) + effective &= ~lldb::ePermissionsWritable; + if (!(overlay & lldb::ePermissionsExecutable)) + effective &= ~lldb::ePermissionsExecutable; + if (!(overlay & lldb::ePermissionsReadable)) + effective &= ~lldb::ePermissionsReadable; + + return MemoryPermissions{overlay, effective}; +} diff --git a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h index 213fbf7417b2c..ae09e6dc87bd1 100644 --- a/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h +++ b/lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h @@ -81,6 +81,14 @@ class ABISysV_arm64 : public ABIAArch64 { lldb::addr_t FixCodeAddress(lldb::addr_t pc) override; lldb::addr_t FixDataAddress(lldb::addr_t pc) override; + // If the Permission Overlay Extension is present, use the protection key + // to look up overlay permissions in por_el0 and apply them to the original + // permissions. + virtual std::optional + GetMemoryPermissions(lldb_private::RegisterContext ®_ctx, + unsigned protection_key, + uint32_t original_permissions) override; + protected: lldb::ValueObjectSP GetReturnValueObjectImpl(lldb_private::Thread &thread, diff --git a/lldb/test/API/linux/aarch64/permission_overlay/TestAArch64LinuxPOE.py b/lldb/test/API/linux/aarch64/permission_overlay/TestAArch64LinuxPOE.py index f4e67b2f402e0..affd66514b9bf 100644 --- a/lldb/test/API/linux/aarch64/permission_overlay/TestAArch64LinuxPOE.py +++ b/lldb/test/API/linux/aarch64/permission_overlay/TestAArch64LinuxPOE.py @@ -82,15 +82,25 @@ def test_poe_live(self): # Unmapped region has no key (not even default). self.expect("memory region 0", substrs=["protection key:"], matching=False) - # The region has base permissions rwx, which is what we see here. + # The region has base permissions r-x, and overlay is r--. The result + # is that execution is disabled. self.expect( - "memory region read_only_page", substrs=["rwx", "protection key: 6"] + "memory region read_only_page", + substrs=["rw-", "protection key: 6 (r--, effective: r--)"], + ) + # A region not assigned to a protection key has the default key 0. This + # key is rwx, but overlays cannot add permissions not already in the + # page table. So the execute permission is not enabled. + self.expect( + "memory region key_zero_page", + substrs=["rw-", "protection key: 0 (rwx, effective: rw-)"], ) - # A region not assigned to a protection key has the default key 0. - self.expect("memory region key_zero_page", substrs=["rwx", "protection key: 0"]) - # Protection keys should be on their own line. - self.expect("memory region --all", patterns=["\nprotection key: [0-9]+\n"]) + # Overlay permissions are on their own line. + self.expect( + "memory region --all", + patterns=["\nprotection key: [0-9]+ \([rwx-]{3}, effective: [rwx-]{3}\)\n"], + ) # Not passing this to the application allows us to fix the permissions # using lldb, then continue to a normal exit. diff --git a/lldb/test/API/linux/aarch64/permission_overlay/main.c b/lldb/test/API/linux/aarch64/permission_overlay/main.c index ec2c0088b7084..5eb4782b9a6ca 100644 --- a/lldb/test/API/linux/aarch64/permission_overlay/main.c +++ b/lldb/test/API/linux/aarch64/permission_overlay/main.c @@ -76,9 +76,10 @@ int main(void) { // Which leaves 7 keys available for programs to allocate. const size_t page_size = (size_t)sysconf(_SC_PAGESIZE); - // pkeys can only subtract from the set of permissions in the page table, - // so we set the page table to allow everything. - const int prot = PROT_READ | PROT_WRITE | PROT_EXEC; + // pkeys can only subtract from the set of permissions in the page table. + // So we leave out execute here to check later that an overlay does not + // enable execution. + const int prot = PROT_READ | PROT_WRITE; const int flags = MAP_PRIVATE | MAP_ANONYMOUS; // This page will have the default key 0.