Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions lldb/include/lldb/Target/ABI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MemoryPermissions>
GetMemoryPermissions(lldb_private::RegisterContext &reg_ctx,
unsigned protection_key, uint32_t original_permissions) {
return std::nullopt;
}

protected:
ABI(lldb::ProcessSP process_sp, std::unique_ptr<llvm::MCRegisterInfo> info_up)
: m_process_wp(process_sp), m_mc_register_info_up(std::move(info_up)) {
Expand Down
24 changes: 22 additions & 2 deletions lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned> protection_key = range_info.GetProtectionKey())
result.AppendMessageWithFormatv("protection key: {0}", *protection_key);
if (std::optional<unsigned> 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<std::vector<addr_t>> &dirty_page_list =
range_info.GetDirtyPageList();
Expand Down
46 changes: 46 additions & 0 deletions lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -884,3 +884,49 @@ void ABISysV_arm64::Initialize() {
void ABISysV_arm64::Terminate() {
PluginManager::UnregisterPlugin(CreateInstance);
}

std::optional<ABISysV_arm64::MemoryPermissions>
ABISysV_arm64::GetMemoryPermissions(lldb_private::RegisterContext &reg_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};
}
8 changes: 8 additions & 0 deletions lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MemoryPermissions>
GetMemoryPermissions(lldb_private::RegisterContext &reg_ctx,
unsigned protection_key,
uint32_t original_permissions) override;

protected:
lldb::ValueObjectSP
GetReturnValueObjectImpl(lldb_private::Thread &thread,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions lldb/test/API/linux/aarch64/permission_overlay/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down