Skip to content

Commit 4c66205

Browse files
[lldb][Linux] Add overlay and effective permissions to "memory region" (llvm#184115)
In this change I'm extending the "memory region" command to show users the overlay permissions that a protection key refers to, and the result of applying that overlay to the page table permissions. For example, protection key 0 refers to Perm0 in the por register. ``` (lldb) register read por Perm0 = Read, Write, Execute ``` This is the default key, so many regions use it. ``` (lldb) memory region --all <...> [0x000ffffff7db0000-0x000ffffff7f40000) r-x /usr/lib/aarch64-linux-gnu/libc.so.6 PT_LOAD[0] protection key: 0 (rwx, effective: r-x) ``` Protection keys can only change what was already enabled in the page table. So we start with read and execute. Then a read/write/execute overlay is applied. We cannot add write, so the result is read and execute. Here's an example of its use with a real crash (output edited): ``` (lldb) c * thread #1, name = 'test.o', stop reason = signal SIGSEGV: failed protection key checks (fault address=0xffffff7d60000) -> 106 read_only_page[0] = '?'; (lldb) memory region 0xffffff7d60000 [0x000ffffff7d60000-0x000ffffff7d70000) rw- protection key: 6 (r--, effective: r--) (lldb) register read por Perm6 = Read ``` The calculation of permissions is implemented by a new ABI method. It's in ABI for 2 reasons: * These overlays are usually in a register (X86 and AArch64 are) and that register name is architecture specific. * The way the overlay values apply may differ between architecture. AArch64 treats a set bit as adding a permission, but some may treat it as removing. Technically this is dependent on operating system and architecture. However, so are the methods for removing non-address bits, and those are in ABI too. To test this I have changed the allocations in the test program to use read+execute permissions by default. With read+write+execute I could not observe that the overlay only changes enabled permissions.
1 parent a528529 commit 4c66205

6 files changed

Lines changed: 123 additions & 11 deletions

File tree

lldb/include/lldb/Target/ABI.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,33 @@ class ABI : public PluginInterface {
152152

153153
static lldb::ABISP FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch);
154154

155+
struct MemoryPermissions {
156+
// Both of these are sets of lldb::Permissions values.
157+
// Overlay are the permissions being applied to the original permissions.
158+
uint32_t overlay;
159+
// Effective is the result of applying the overlay to the original
160+
// permissions. Calculating this is done by the plugin because some
161+
// permission overlays are done as positive (add permissions) and some as
162+
// negative (remove permissions).
163+
uint32_t effective;
164+
};
165+
166+
/// Get the effective memory permissions that result when the permissions
167+
/// referred to by a protection key are applied to the original permissions.
168+
///
169+
/// This is intended for architectures that have some sort of permission
170+
/// overlay system. Where the protection key is used to look up a set of
171+
/// permissions that modifies the original permissions.
172+
///
173+
/// \returns the overlay permissions (that the protection key refers to) and
174+
/// the effective permissions. If the target does not have an overlay
175+
/// system, or it does and the protection key is invalid, returns nullopt.
176+
virtual std::optional<MemoryPermissions>
177+
GetMemoryPermissions(lldb_private::RegisterContext &reg_ctx,
178+
unsigned protection_key, uint32_t original_permissions) {
179+
return std::nullopt;
180+
}
181+
155182
protected:
156183
ABI(lldb::ProcessSP process_sp, std::unique_ptr<llvm::MCRegisterInfo> info_up)
157184
: m_process_wp(process_sp), m_mc_register_info_up(std::move(info_up)) {

lldb/source/Commands/CommandObjectMemory.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,8 +1689,28 @@ class CommandObjectMemoryRegion : public CommandObjectParsed {
16891689
LazyBool is_shadow_stack = range_info.IsShadowStack();
16901690
if (is_shadow_stack == eLazyBoolYes)
16911691
result.AppendMessage("shadow stack: yes");
1692-
if (std::optional<unsigned> protection_key = range_info.GetProtectionKey())
1693-
result.AppendMessageWithFormatv("protection key: {0}", *protection_key);
1692+
if (std::optional<unsigned> protection_key =
1693+
range_info.GetProtectionKey()) {
1694+
Stream &strm = result.GetOutputStream();
1695+
strm << llvm::formatv("protection key: {0}", *protection_key);
1696+
1697+
if (const lldb::ABISP &abi = target.GetProcessSP()->GetABI()) {
1698+
if (auto permissions = abi->GetMemoryPermissions(
1699+
*m_exe_ctx.GetRegisterContext(), *protection_key,
1700+
range_info.GetLLDBPermissions())) {
1701+
strm << llvm::formatv(
1702+
" ({0}{1}{2}, effective: {3}{4}{5})",
1703+
permissions->overlay & lldb::ePermissionsReadable ? 'r' : '-',
1704+
permissions->overlay & lldb::ePermissionsWritable ? 'w' : '-',
1705+
permissions->overlay & lldb::ePermissionsExecutable ? 'x' : '-',
1706+
permissions->effective & lldb::ePermissionsReadable ? 'r' : '-',
1707+
permissions->effective & lldb::ePermissionsWritable ? 'w' : '-',
1708+
permissions->effective & lldb::ePermissionsExecutable ? 'x'
1709+
: '-');
1710+
}
1711+
}
1712+
strm.PutChar('\n');
1713+
}
16941714

16951715
const std::optional<std::vector<addr_t>> &dirty_page_list =
16961716
range_info.GetDirtyPageList();

lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -884,3 +884,49 @@ void ABISysV_arm64::Initialize() {
884884
void ABISysV_arm64::Terminate() {
885885
PluginManager::UnregisterPlugin(CreateInstance);
886886
}
887+
888+
std::optional<ABISysV_arm64::MemoryPermissions>
889+
ABISysV_arm64::GetMemoryPermissions(lldb_private::RegisterContext &reg_ctx,
890+
unsigned protection_key,
891+
uint32_t original_permissions) {
892+
// The presence of the POR register means we have the Permission Overlay
893+
// Extension.
894+
// See Arm Architecture Reference manual "POR_EL0, Permission Overlay Register
895+
// 0 (EL0)".
896+
const RegisterInfo *por_info = reg_ctx.GetRegisterInfoByName("por");
897+
if (!por_info)
898+
return std::nullopt;
899+
900+
uint64_t por_value =
901+
reg_ctx.ReadRegisterAsUnsigned(por_info, LLDB_INVALID_ADDRESS);
902+
if (por_value == LLDB_INVALID_ADDRESS)
903+
return std::nullopt;
904+
905+
// POR contains 16, 4-bit permission sets (though Linux limits this to 8
906+
// useable sets).
907+
if (protection_key >= 16)
908+
return std::nullopt;
909+
910+
// Bit 3 - reserved, bit 2 - write, bit 1 - execute, bit 0 - read.
911+
const uint64_t por_permissions = (por_value >> (protection_key * 4)) & 0xf;
912+
uint32_t overlay = 0;
913+
if (por_permissions & 4)
914+
overlay |= lldb::ePermissionsWritable;
915+
if (por_permissions & 2)
916+
overlay |= lldb::ePermissionsExecutable;
917+
if (por_permissions & 1)
918+
overlay |= lldb::ePermissionsReadable;
919+
920+
uint32_t effective = original_permissions;
921+
922+
// Permission overlays cannot add permissions, they can only keep, or disable,
923+
// what was originally set.
924+
if (!(overlay & lldb::ePermissionsWritable))
925+
effective &= ~lldb::ePermissionsWritable;
926+
if (!(overlay & lldb::ePermissionsExecutable))
927+
effective &= ~lldb::ePermissionsExecutable;
928+
if (!(overlay & lldb::ePermissionsReadable))
929+
effective &= ~lldb::ePermissionsReadable;
930+
931+
return MemoryPermissions{overlay, effective};
932+
}

lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ class ABISysV_arm64 : public ABIAArch64 {
8181
lldb::addr_t FixCodeAddress(lldb::addr_t pc) override;
8282
lldb::addr_t FixDataAddress(lldb::addr_t pc) override;
8383

84+
// If the Permission Overlay Extension is present, use the protection key
85+
// to look up overlay permissions in por_el0 and apply them to the original
86+
// permissions.
87+
virtual std::optional<MemoryPermissions>
88+
GetMemoryPermissions(lldb_private::RegisterContext &reg_ctx,
89+
unsigned protection_key,
90+
uint32_t original_permissions) override;
91+
8492
protected:
8593
lldb::ValueObjectSP
8694
GetReturnValueObjectImpl(lldb_private::Thread &thread,

lldb/test/API/linux/aarch64/permission_overlay/TestAArch64LinuxPOE.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,25 @@ def test_poe_live(self):
8282
# Unmapped region has no key (not even default).
8383
self.expect("memory region 0", substrs=["protection key:"], matching=False)
8484

85-
# The region has base permissions rwx, which is what we see here.
85+
# The region has base permissions r-x, and overlay is r--. The result
86+
# is that execution is disabled.
8687
self.expect(
87-
"memory region read_only_page", substrs=["rwx", "protection key: 6"]
88+
"memory region read_only_page",
89+
substrs=["rw-", "protection key: 6 (r--, effective: r--)"],
90+
)
91+
# A region not assigned to a protection key has the default key 0. This
92+
# key is rwx, but overlays cannot add permissions not already in the
93+
# page table. So the execute permission is not enabled.
94+
self.expect(
95+
"memory region key_zero_page",
96+
substrs=["rw-", "protection key: 0 (rwx, effective: rw-)"],
8897
)
89-
# A region not assigned to a protection key has the default key 0.
90-
self.expect("memory region key_zero_page", substrs=["rwx", "protection key: 0"])
9198

92-
# Protection keys should be on their own line.
93-
self.expect("memory region --all", patterns=["\nprotection key: [0-9]+\n"])
99+
# Overlay permissions are on their own line.
100+
self.expect(
101+
"memory region --all",
102+
patterns=["\nprotection key: [0-9]+ \([rwx-]{3}, effective: [rwx-]{3}\)\n"],
103+
)
94104

95105
# Not passing this to the application allows us to fix the permissions
96106
# using lldb, then continue to a normal exit.

lldb/test/API/linux/aarch64/permission_overlay/main.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ int main(void) {
7676
// Which leaves 7 keys available for programs to allocate.
7777

7878
const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
79-
// pkeys can only subtract from the set of permissions in the page table,
80-
// so we set the page table to allow everything.
81-
const int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
79+
// pkeys can only subtract from the set of permissions in the page table.
80+
// So we leave out execute here to check later that an overlay does not
81+
// enable execution.
82+
const int prot = PROT_READ | PROT_WRITE;
8283
const int flags = MAP_PRIVATE | MAP_ANONYMOUS;
8384

8485
// This page will have the default key 0.

0 commit comments

Comments
 (0)