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
2 changes: 2 additions & 0 deletions lldb/docs/resources/lldbgdbremote.md
Original file line number Diff line number Diff line change
Expand Up @@ -1443,6 +1443,8 @@ tuples to return are:
listed (`dirty-pages:;`) indicates no dirty pages in
this memory region. The *absence* of this key means
that this stub cannot determine dirty pages.
* `protection-key:<key>` - where `<key>` is an unsigned integer memory
protection key.

If the address requested is not in a mapped region (e.g. we've jumped through
a NULL pointer and are at 0x0) currently lldb expects to get back the size
Expand Down
11 changes: 10 additions & 1 deletion lldb/include/lldb/Target/MemoryRegionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ class MemoryRegionInfo {

LazyBool IsShadowStack() const { return m_is_shadow_stack; }

std::optional<unsigned> GetProtectionKey() const { return m_protection_key; }

void SetReadable(LazyBool val) { m_read = val; }

void SetWritable(LazyBool val) { m_write = val; }
Expand Down Expand Up @@ -81,6 +83,11 @@ class MemoryRegionInfo {
return *this;
}

MemoryRegionInfo &SetProtectionKey(std::optional<unsigned> key) {
m_protection_key = key;
return *this;
}

// Get permissions as a uint32_t that is a mask of one or more bits from the
// lldb::Permissions
uint32_t GetLLDBPermissions() const {
Expand Down Expand Up @@ -114,7 +121,8 @@ class MemoryRegionInfo {
m_memory_tagged == rhs.m_memory_tagged &&
m_pagesize == rhs.m_pagesize &&
m_is_stack_memory == rhs.m_is_stack_memory &&
m_is_shadow_stack == rhs.m_is_shadow_stack;
m_is_shadow_stack == rhs.m_is_shadow_stack &&
m_protection_key == rhs.m_protection_key;
}

bool operator!=(const MemoryRegionInfo &rhs) const { return !(*this == rhs); }
Expand Down Expand Up @@ -157,6 +165,7 @@ class MemoryRegionInfo {
LazyBool m_memory_tagged = eLazyBoolDontKnow;
LazyBool m_is_stack_memory = eLazyBoolDontKnow;
LazyBool m_is_shadow_stack = eLazyBoolDontKnow;
std::optional<unsigned> m_protection_key = std::nullopt;
int m_pagesize = 0;
std::optional<std::vector<lldb::addr_t>> m_dirty_pages;
};
Expand Down
2 changes: 2 additions & 0 deletions lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1694,6 +1694,8 @@ 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);

const std::optional<std::vector<addr_t>> &dirty_page_list =
range_info.GetDirtyPageList();
Expand Down
4 changes: 4 additions & 0 deletions lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
region->SetMemoryTagged(eLazyBoolYes);
else if (flag == "ss")
region->SetIsShadowStack(eLazyBoolYes);
} else if (name == "ProtectionKey") {
unsigned key = 0;
if (!value.ltrim().getAsInteger(10, key))
region->SetProtectionKey(key);
}
} else {
// Orphaned settings line
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,10 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
dirty_page_list.push_back(page);
}
region_info.SetDirtyPageList(dirty_page_list);
} else if (name == "protection-key") {
unsigned protection_key = 0;
if (!value.getAsInteger(10, protection_key))
region_info.SetProtectionKey(protection_key);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2895,6 +2895,9 @@ GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo(
response.PutStringAsRawHex8(name.GetStringRef());
response.PutChar(';');
}

if (std::optional<unsigned> protection_key = region_info.GetProtectionKey())
response.Printf("protection-key:%" PRIu32 ";", *protection_key);
}

return SendPacketNoLock(response.GetString());
Expand Down
16 changes: 8 additions & 8 deletions lldb/source/Target/MemoryRegionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ using namespace lldb_private;

llvm::raw_ostream &lldb_private::operator<<(llvm::raw_ostream &OS,
const MemoryRegionInfo &Info) {
return OS << llvm::formatv("MemoryRegionInfo([{0}, {1}), {2:r}{3:w}{4:x}, "
"{5}, `{6}`, {7}, {8}, {9}, {10}, {11})",
Info.GetRange().GetRangeBase(),
Info.GetRange().GetRangeEnd(), Info.GetReadable(),
Info.GetWritable(), Info.GetExecutable(),
Info.GetMapped(), Info.GetName(), Info.GetFlash(),
Info.GetBlocksize(), Info.GetMemoryTagged(),
Info.IsStackMemory(), Info.IsShadowStack());
return OS << llvm::formatv(
"MemoryRegionInfo([{0}, {1}), {2:r}{3:w}{4:x}, "
"{5}, `{6}`, {7}, {8}, {9}, {10}, {11}, {12})",
Info.GetRange().GetRangeBase(), Info.GetRange().GetRangeEnd(),
Info.GetReadable(), Info.GetWritable(), Info.GetExecutable(),
Info.GetMapped(), Info.GetName(), Info.GetFlash(),
Info.GetBlocksize(), Info.GetMemoryTagged(), Info.IsStackMemory(),
Info.IsShadowStack(), Info.GetProtectionKey());
}

void llvm::format_provider<LazyBool>::format(const LazyBool &B, raw_ostream &OS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,19 @@ def test_poe_live(self):
self.expect("expression expr_function()", substrs=["$0 = 1"])
self.expect("register read por", substrs=[self.EXPECTED_POR])

# 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.
self.expect(
"memory region read_only_page", substrs=["rwx", "protection key: 6"]
)
# 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"])

# Not passing this to the application allows us to fix the permissions
# using lldb, then continue to a normal exit.
self.runCmd("process handle SIGSEGV --pass false")
Expand Down Expand Up @@ -127,3 +140,7 @@ def test_poe_core(self):
"register read por",
substrs=[f" {self.EXPECTED_POR}\n" + self.EXPECTED_POR_FIELDS],
)

# Protection keys are listed in /proc/<pid>/smaps, which is not included
# in core files.
self.expect("memory region --all", substrs=["protection key:"], matching=False)
5 changes: 5 additions & 0 deletions lldb/test/API/linux/aarch64/permission_overlay/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ int main(void) {
const int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
const int flags = MAP_PRIVATE | MAP_ANONYMOUS;

// This page will have the default key 0.
char *key_zero_page = mmap(NULL, page_size, prot, flags, -1, 0);
if (key_zero_page == MAP_FAILED)
exit(2);

// Later we will use this to cause a protection key fault.
char *read_only_page = NULL;

Expand Down
40 changes: 40 additions & 0 deletions lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,46 @@ INSTANTIATE_TEST_SUITE_P(
.SetIsShadowStack(eLazyBoolYes)
.SetMemoryTagged(eLazyBoolNo),
},
""),
// 0 is the default protection key.
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"ProtectionKey: 0",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetProtectionKey(0),
},
""),
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"ProtectionKey: 99",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr))
.SetProtectionKey(99),
},
""),
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"ProtectionKey: not_an_integer",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr)),
},
""),
// Should be unsigned.
std::make_tuple("0-0 rw-p 00000000 00:00 0\n"
"ProtectionKey: -24",
MemoryRegionInfos{
MemoryRegionInfo(make_range(0, 0), eLazyBoolYes,
eLazyBoolYes, eLazyBoolNo,
eLazyBoolNo, eLazyBoolYes,
ConstString(nullptr)),
},
"")));

TEST_P(LinuxProcSMapsTestFixture, ParseSMapRegions) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
EXPECT_EQ(lldb_private::eLazyBoolDontKnow, region_info.GetMemoryTagged());
EXPECT_EQ(lldb_private::eLazyBoolDontKnow, region_info.IsStackMemory());
EXPECT_EQ(lldb_private::eLazyBoolDontKnow, region_info.IsShadowStack());
EXPECT_EQ(std::nullopt, region_info.GetProtectionKey());

result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
Expand Down Expand Up @@ -429,6 +430,25 @@ TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfo) {
"start:a000;size:2000;type:heap;");
EXPECT_TRUE(result.get().Success());
EXPECT_EQ(lldb_private::eLazyBoolNo, region_info.IsStackMemory());

result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});

HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;protection-key:42;");
EXPECT_TRUE(result.get().Success());
ASSERT_THAT(region_info.GetProtectionKey(),
::testing::Optional(::testing::Eq(42)));

result = std::async(std::launch::async, [&] {
return client.GetMemoryRegionInfo(addr, region_info);
});

HandlePacket(server, "qMemoryRegionInfo:a000",
"start:a000;size:2000;protection-key:not_a_number;");
EXPECT_TRUE(result.get().Success());
ASSERT_THAT(region_info.GetProtectionKey(), std::nullopt);
}

TEST_F(GDBRemoteCommunicationClientTest, GetMemoryRegionInfoInvalidResponse) {
Expand Down