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
12 changes: 9 additions & 3 deletions lldb/include/lldb/Target/MemoryRegionInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ class MemoryRegionInfo {
OptionalBool execute, OptionalBool shared,
OptionalBool mapped, ConstString name, OptionalBool flash,
lldb::offset_t blocksize, OptionalBool memory_tagged,
OptionalBool stack_memory)
OptionalBool stack_memory, OptionalBool shadow_stack)
: m_range(range), m_read(read), m_write(write), m_execute(execute),
m_shared(shared), m_mapped(mapped), m_name(name), m_flash(flash),
m_blocksize(blocksize), m_memory_tagged(memory_tagged),
m_is_stack_memory(stack_memory) {}
m_is_stack_memory(stack_memory), m_is_shadow_stack(shadow_stack) {}

RangeType &GetRange() { return m_range; }

Expand All @@ -55,6 +55,8 @@ class MemoryRegionInfo {

OptionalBool GetMemoryTagged() const { return m_memory_tagged; }

OptionalBool IsShadowStack() const { return m_is_shadow_stack; }

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

void SetWritable(OptionalBool val) { m_write = val; }
Expand All @@ -77,6 +79,8 @@ class MemoryRegionInfo {

void SetMemoryTagged(OptionalBool val) { m_memory_tagged = val; }

void SetIsShadowStack(OptionalBool val) { m_is_shadow_stack = val; }

// 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 @@ -106,7 +110,8 @@ class MemoryRegionInfo {
m_blocksize == rhs.m_blocksize &&
m_memory_tagged == rhs.m_memory_tagged &&
m_pagesize == rhs.m_pagesize &&
m_is_stack_memory == rhs.m_is_stack_memory;
m_is_stack_memory == rhs.m_is_stack_memory &&
m_is_shadow_stack == rhs.m_is_shadow_stack;
}

bool operator!=(const MemoryRegionInfo &rhs) const { return !(*this == rhs); }
Expand Down Expand Up @@ -148,6 +153,7 @@ class MemoryRegionInfo {
lldb::offset_t m_blocksize = 0;
OptionalBool m_memory_tagged = eDontKnow;
OptionalBool m_is_stack_memory = eDontKnow;
OptionalBool m_is_shadow_stack = eDontKnow;
int m_pagesize = 0;
std::optional<std::vector<lldb::addr_t>> m_dirty_pages;
};
Expand Down
3 changes: 3 additions & 0 deletions lldb/packages/Python/lldbsuite/test/lldbtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,9 @@ def isAArch64SMEFA64(self):
def isAArch64MTE(self):
return self.isAArch64() and "mte" in self.getCPUInfo()

def isAArch64GCS(self):
return self.isAArch64() and "gcs" in self.getCPUInfo()

def isAArch64PAuth(self):
if self.getArchitecture() == "arm64e":
return True
Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1664,6 +1664,9 @@ class CommandObjectMemoryRegion : public CommandObjectParsed {
MemoryRegionInfo::OptionalBool memory_tagged = range_info.GetMemoryTagged();
if (memory_tagged == MemoryRegionInfo::OptionalBool::eYes)
result.AppendMessage("memory tagging: enabled");
MemoryRegionInfo::OptionalBool is_shadow_stack = range_info.IsShadowStack();
if (is_shadow_stack == MemoryRegionInfo::OptionalBool::eYes)
result.AppendMessage("shadow stack: yes");

const std::optional<std::vector<addr_t>> &dirty_page_list =
range_info.GetDirtyPageList();
Expand Down
15 changes: 10 additions & 5 deletions lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,17 @@ void lldb_private::ParseLinuxSMapRegions(llvm::StringRef linux_smap,
if (!name.contains(' ')) {
if (region) {
if (name == "VmFlags") {
if (value.contains("mt"))
region->SetMemoryTagged(MemoryRegionInfo::eYes);
else
region->SetMemoryTagged(MemoryRegionInfo::eNo);
region->SetMemoryTagged(MemoryRegionInfo::eNo);
region->SetIsShadowStack(MemoryRegionInfo::eNo);

llvm::SmallVector<llvm::StringRef> flags;
value.split(flags, ' ', /*MaxSplit=*/-1, /*KeepEmpty=*/false);
for (llvm::StringRef flag : flags)
if (flag == "mt")
region->SetMemoryTagged(MemoryRegionInfo::eYes);
else if (flag == "ss")
region->SetIsShadowStack(MemoryRegionInfo::eYes);
}
// Ignore anything else
} else {
// Orphaned settings line
callback(ProcMapError(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,7 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
region_info.SetName(name.c_str());
} else if (name == "flags") {
region_info.SetMemoryTagged(MemoryRegionInfo::eNo);
region_info.SetIsShadowStack(MemoryRegionInfo::eNo);

llvm::StringRef flags = value;
llvm::StringRef flag;
Expand All @@ -1629,10 +1630,10 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
std::tie(flag, flags) = flags.split(' ');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of tokenizing and checking cant we just use
`if (value.contains("mt"))
region_info.SetMemoryTagged(MemoryRegionInfo::eYes);

if (value.contains("ss"))
region_info.SetIsShadowStack(MemoryRegionInfo::eYes);`

// To account for trailing whitespace
if (flag.size()) {
if (flag == "mt") {
if (flag == "mt")
region_info.SetMemoryTagged(MemoryRegionInfo::eYes);
break;
}
else if (flag == "ss")
region_info.SetIsShadowStack(MemoryRegionInfo::eYes);
}
}
} else if (name == "type") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2796,11 +2796,18 @@ GDBRemoteCommunicationServerLLGS::Handle_qMemoryRegionInfo(
// Flags
MemoryRegionInfo::OptionalBool memory_tagged =
region_info.GetMemoryTagged();
if (memory_tagged != MemoryRegionInfo::eDontKnow) {
MemoryRegionInfo::OptionalBool is_shadow_stack =
region_info.IsShadowStack();

if (memory_tagged != MemoryRegionInfo::eDontKnow ||
is_shadow_stack != MemoryRegionInfo::eDontKnow) {
response.PutCString("flags:");
if (memory_tagged == MemoryRegionInfo::eYes) {
response.PutCString("mt");
}
// Space is the separator.
if (memory_tagged == MemoryRegionInfo::eYes)
response.PutCString("mt ");
if (is_shadow_stack == MemoryRegionInfo::eYes)
response.PutCString("ss ");

response.PutChar(';');
}

Expand Down
5 changes: 3 additions & 2 deletions lldb/source/Target/MemoryRegionInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@ 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})",
"{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.GetBlocksize(), Info.GetMemoryTagged(),
Info.IsStackMemory(), Info.IsShadowStack());
}

void llvm::format_provider<MemoryRegionInfo::OptionalBool>::format(
Expand Down
3 changes: 3 additions & 0 deletions lldb/test/API/linux/aarch64/gcs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
C_SOURCES := main.c

include Makefile.rules
63 changes: 63 additions & 0 deletions lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Check that lldb features work when the AArch64 Guarded Control Stack (GCS)
extension is enabled.
"""


import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class AArch64LinuxGCSTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True

@skipUnlessArch("aarch64")
@skipUnlessPlatform(["linux"])
def test_gcs_region(self):
if not self.isAArch64GCS():
self.skipTest("Target must support GCS.")

# This test assumes that we have /proc/<PID>/smaps files
# that include "VmFlags:" lines.
# AArch64 kernel config defaults to enabling smaps with
# PROC_PAGE_MONITOR and "VmFlags" was added in kernel 3.8,
# before GCS was supported at all.

self.build()
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)

lldbutil.run_break_set_by_file_and_line(
self,
"main.c",
line_number("main.c", "// Set break point at this line."),
num_expected_locations=1,
)

self.runCmd("run", RUN_SUCCEEDED)

if self.process().GetState() == lldb.eStateExited:
self.fail("Test program failed to run.")

self.expect(
"thread list",
STOPPED_DUE_TO_BREAKPOINT,
substrs=["stopped", "stop reason = breakpoint"],
)

# By now either the program or the system C library enabled GCS and there
# should be one region marked for use by it (we cannot predict exactly
# where it will be).
self.runCmd("memory region --all")
found_ss = False
for line in self.res.GetOutput().splitlines():
if line.strip() == "shadow stack: yes":
if found_ss:
self.fail("Found more than one shadow stack region.")
found_ss = True

self.assertTrue(found_ss, "Failed to find a shadow stack region.")

# Note that we must let the debugee get killed here as it cannot exit
# cleanly if GCS was manually enabled.
54 changes: 54 additions & 0 deletions lldb/test/API/linux/aarch64/gcs/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include <asm/hwcap.h>
#include <sys/auxv.h>
#include <sys/prctl.h>

#ifndef HWCAP2_GCS
#define HWCAP2_GCS (1UL << 63)
#endif

#define PR_GET_SHADOW_STACK_STATUS 74
#define PR_SET_SHADOW_STACK_STATUS 75
#define PR_SHADOW_STACK_ENABLE (1UL)
#define PRCTL_SYSCALL_NO 167

// Once we enable GCS, we cannot return from the function that made the syscall
// to enable it. This is because the control stack is empty, there is no valid
// address for us to return to. So for the initial enable we must use inline asm
// instead of the libc's prctl wrapper function.
#define my_prctl(option, arg2, arg3, arg4, arg5) \
({ \
register unsigned long x0 __asm__("x0") = option; \
register unsigned long x1 __asm__("x1") = arg2; \
register unsigned long x2 __asm__("x2") = arg3; \
register unsigned long x3 __asm__("x3") = arg4; \
register unsigned long x4 __asm__("x4") = arg5; \
register unsigned long x8 __asm__("x8") = PRCTL_SYSCALL_NO; \
__asm__ __volatile__("svc #0\n" \
: "=r"(x0) \
: "r"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), \
"r"(x8) \
: "cc", "memory"); \
})

unsigned long get_gcs_status() {
unsigned long mode = 0;
prctl(PR_GET_SHADOW_STACK_STATUS, &mode, 0, 0, 0);
return mode;
}

int main() {
if (!(getauxval(AT_HWCAP2) & HWCAP2_GCS))
return 1;

unsigned long mode = get_gcs_status();
if ((mode & 1) == 0) {
// If GCS wasn't already enabled by the C library, enable it.
my_prctl(PR_SET_SHADOW_STACK_STATUS, PR_SHADOW_STACK_ENABLE, 0, 0, 0);
// From this point on, we cannot return from main without faulting because
// the return address from main, and every function before that, is not on
// the guarded control stack.
}

// By now we should have one memory region where the GCS is stored.
return 0; // Set break point at this line.
}
Loading
Loading