-
Notifications
You must be signed in to change notification settings - Fork 12.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb][Linux] Mark memory regions used for shadow stacks #117861
base: main
Are you sure you want to change the base?
Conversation
This is intended for use with Arm's Guarded Control Stack extension (GCS). Which reuses some existing shadow stack support in Linux. It should also work with the x86 equivalent. A "ss" flag is added to the "VmFlags" line of shadow stack memory regions in /proc/<pid>/smaps. To keep the naming generic I've called it shadow stack instead of guarded control stack. Also the wording is "shadow stack: yes" because the shadow stack region is just where it's stored. It's enabled for the whole process or it isn't. As opposed to memory tagging which can be enabled per region, so "memory tagging: enabled" fits better for that. I've added a test case that is also intended to be the start of a set of tests for GCS. This should help me avoid duplicating the inline assembly needed. Note that no special compiler support is needed for the test. However, for the intial enabling of GCS (assuming the libc isn't doing it) we do need to use an inline assembly version of prctl. This is because as soon as you enable GCS, all returns are checked against the GCS. If the GCS is empty, the program will fault. In other words, you can never return from the function that enabled GCS, unless you push values onto it (which is possible but not needed here). So you cannot use the libc's prctl wrapper for this reason. You can use that wrapper for anything else, as we do to check if GCS is enabled.
@llvm/pr-subscribers-lldb Author: David Spickett (DavidSpickett) ChangesThis is intended for use with Arm's Guarded Control Stack extension (GCS). Which reuses some existing shadow stack support in Linux. It should also work with the x86 equivalent. A "ss" flag is added to the "VmFlags" line of shadow stack memory regions in /proc/<pid>/smaps. To keep the naming generic I've called it shadow stack instead of guarded control stack. Also the wording is "shadow stack: yes" because the shadow stack region is just where it's stored. It's enabled for the whole process or it isn't. As opposed to memory tagging which can be enabled per region, so "memory tagging: enabled" fits better for that. I've added a test case that is also intended to be the start of a set of tests for GCS. This should help me avoid duplicating the inline assembly needed. Note that no special compiler support is needed for the test. However, for the intial enabling of GCS (assuming the libc isn't doing it) we do need to use an inline assembly version of prctl. This is because as soon as you enable GCS, all returns are checked against the GCS. If the GCS is empty, the program will fault. In other words, you can never return from the function that enabled GCS, unless you push values onto it (which is possible but not needed here). So you cannot use the libc's prctl wrapper for this reason. You can use that wrapper for anything else, as we do to check if GCS is enabled. Patch is 39.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/117861.diff 14 Files Affected:
diff --git a/lldb/include/lldb/Target/MemoryRegionInfo.h b/lldb/include/lldb/Target/MemoryRegionInfo.h
index 66a4b3ed1b42d5..3e1272c2bba214 100644
--- a/lldb/include/lldb/Target/MemoryRegionInfo.h
+++ b/lldb/include/lldb/Target/MemoryRegionInfo.h
@@ -27,13 +27,13 @@ class MemoryRegionInfo {
MemoryRegionInfo() = default;
MemoryRegionInfo(RangeType range, OptionalBool read, OptionalBool write,
OptionalBool execute, OptionalBool shared,
- OptionalBool mapped, ConstString name,
- OptionalBool flash, lldb::offset_t blocksize,
- OptionalBool memory_tagged, OptionalBool stack_memory)
+ OptionalBool mapped, ConstString name, OptionalBool flash,
+ lldb::offset_t blocksize, OptionalBool memory_tagged,
+ 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; }
@@ -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; }
@@ -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 {
@@ -101,12 +105,13 @@ class MemoryRegionInfo {
bool operator==(const MemoryRegionInfo &rhs) const {
return m_range == rhs.m_range && m_read == rhs.m_read &&
m_write == rhs.m_write && m_execute == rhs.m_execute &&
- m_shared == rhs.m_shared &&
- m_mapped == rhs.m_mapped && m_name == rhs.m_name &&
- m_flash == rhs.m_flash && m_blocksize == rhs.m_blocksize &&
+ m_shared == rhs.m_shared && m_mapped == rhs.m_mapped &&
+ m_name == rhs.m_name && m_flash == rhs.m_flash &&
+ 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); }
@@ -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;
};
diff --git a/lldb/packages/Python/lldbsuite/test/lldbtest.py b/lldb/packages/Python/lldbsuite/test/lldbtest.py
index 8884ef5933ada8..cee2abafdac83f 100644
--- a/lldb/packages/Python/lldbsuite/test/lldbtest.py
+++ b/lldb/packages/Python/lldbsuite/test/lldbtest.py
@@ -1363,6 +1363,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
diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp
index b5612f21f11563..6ca4056c960ee5 100644
--- a/lldb/source/Commands/CommandObjectMemory.cpp
+++ b/lldb/source/Commands/CommandObjectMemory.cpp
@@ -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();
diff --git a/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp b/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
index fd803c8cabafe3..2ed896327a2f82 100644
--- a/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
+++ b/lldb/source/Plugins/Process/Utility/LinuxProcMaps.cpp
@@ -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(
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index e42526c8fd7266..b3f1c6f052955b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -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;
@@ -1629,10 +1630,10 @@ Status GDBRemoteCommunicationClient::GetMemoryRegionInfo(
std::tie(flag, flags) = flags.split(' ');
// 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") {
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 35fa93e53bc66f..becc79eb8a07c6 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -2796,11 +2796,17 @@ 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) {
+ if (memory_tagged == MemoryRegionInfo::eYes)
response.PutCString("mt");
- }
+ if (is_shadow_stack == MemoryRegionInfo::eYes)
+ response.PutCString("ss");
+
response.PutChar(';');
}
diff --git a/lldb/source/Target/MemoryRegionInfo.cpp b/lldb/source/Target/MemoryRegionInfo.cpp
index 0d5ebbdbe23800..979e45ad023af9 100644
--- a/lldb/source/Target/MemoryRegionInfo.cpp
+++ b/lldb/source/Target/MemoryRegionInfo.cpp
@@ -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(
diff --git a/lldb/test/API/linux/aarch64/gcs/Makefile b/lldb/test/API/linux/aarch64/gcs/Makefile
new file mode 100644
index 00000000000000..10495940055b63
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/gcs/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py b/lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py
new file mode 100644
index 00000000000000..b425c9e548ee51
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py
@@ -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.
diff --git a/lldb/test/API/linux/aarch64/gcs/main.c b/lldb/test/API/linux/aarch64/gcs/main.c
new file mode 100644
index 00000000000000..9633ed2838f9e8
--- /dev/null
+++ b/lldb/test/API/linux/aarch64/gcs/main.c
@@ -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.
+}
diff --git a/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp b/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
index ac1c6132630b0b..0f90520411c932 100644
--- a/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
+++ b/lldb/unittests/Process/Utility/LinuxProcMapsTest.cpp
@@ -87,12 +87,13 @@ INSTANTIATE_TEST_SUITE_P(
"0-0 rwzp 00000000 00:00 0\n"
"2-3 r-xp 00000000 00:00 0 [def]\n",
MemoryRegionInfos{
- MemoryRegionInfo(make_range(0, 1), MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
- MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes, ConstString("[abc]"),
- MemoryRegionInfo::eDontKnow, 0,
- MemoryRegionInfo::eDontKnow,
- MemoryRegionInfo::eDontKnow),
+ MemoryRegionInfo(
+ make_range(0, 1), MemoryRegionInfo::eYes,
+ MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
+ ConstString("[abc]"), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow,
+ MemoryRegionInfo::eDontKnow),
},
"unexpected /proc/{pid}/maps exec permission char"),
// Single entry
@@ -101,10 +102,10 @@ INSTANTIATE_TEST_SUITE_P(
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x55a4512f7000, 0x55a451b68000),
- MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
- MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes,
- ConstString("[heap]"), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eYes, MemoryRegionInfo::eYes,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eYes, ConstString("[heap]"),
+ MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
""),
@@ -117,24 +118,24 @@ INSTANTIATE_TEST_SUITE_P(
MemoryRegionInfos{
MemoryRegionInfo(
make_range(0x7fc090021000, 0x7fc094000000),
- MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
- MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes,
- ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eYes, ConstString(nullptr),
+ MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(
make_range(0x7fc094000000, 0x7fc094a00000),
- MemoryRegionInfo::eNo, MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes,
- MemoryRegionInfo::eYes,
- ConstString(nullptr), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
+ MemoryRegionInfo::eYes, ConstString(nullptr),
+ MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
MemoryRegionInfo(
make_range(0xffffffffff600000, 0xffffffffff601000),
- MemoryRegionInfo::eYes, MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
- MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes,
- ConstString("[vsyscall]"), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eYes, ConstString("[vsyscall]"),
+ MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow),
},
"")));
@@ -157,11 +158,11 @@ INSTANTIATE_TEST_SUITE_P(
"0/0 rw-p 00000000 00:00 0",
MemoryRegionInfos{
MemoryRegionInfo(
- make_range(0x1111, 0x2222),
- MemoryRegionInfo::eYes, MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
- MemoryRegionInfo::eNo,
- MemoryRegionInfo::eYes, ConstString("[foo]"),
- MemoryRegionInfo::eDontKnow, 0, MemoryRegionInfo::eDontKnow,
+ make_range(0x1111, 0x2222), MemoryRegionInfo::eYes,
+ MemoryRegionInfo::eYes, MemoryRegionInfo::eNo,
+ MemoryRegionInfo::eNo, MemoryRegionInfo::eYes,
+ ConstString("[foo]"), MemoryRegionInfo::eDontKnow, 0,
+ MemoryRegionInfo::eDontKnow, MemoryRegionInfo::eDontKnow,
MemoryRegionInfo::eDontKnow),
},
"malformed /proc/{pid}/smaps entry, missing dash between ad...
[truncated]
|
RFC here: https://discourse.llvm.org/t/rfc-aarch64-guarded-control-stack-support-for-lldb/83364 Should help you understand the test case. |
Though now I read this I think maybe this is meaningless. You can have multiple shadow stacks using GCS, just like many regions could be memory tagged. So I'll go with whatever makes sense to others, I'm too close to the details to decide really. |
This is intended for use with Arm's Guarded Control Stack extension (GCS). Which reuses some existing shadow stack support in Linux. It should also work with the x86 equivalent.
A "ss" flag is added to the "VmFlags" line of shadow stack memory regions in /proc//smaps. To keep the naming generic I've called it shadow stack instead of guarded control stack.
Also the wording is "shadow stack: yes" because the shadow stack region is just where it's stored. It's enabled for the whole process or it isn't. As opposed to memory tagging which can be enabled per region, so "memory tagging: enabled" fits better for that.
I've added a test case that is also intended to be the start of a set of tests for GCS. This should help me avoid duplicating the inline assembly needed.
Note that no special compiler support is needed for the test. However, for the intial enabling of GCS (assuming the libc isn't doing it) we do need to use an inline assembly version of prctl.
This is because as soon as you enable GCS, all returns are checked against the GCS. If the GCS is empty, the program will fault. In other words, you can never return from the function that enabled GCS, unless you push values onto it (which is possible but not needed here).
So you cannot use the libc's prctl wrapper for this reason. You can use that wrapper for anything else, as we do to check if GCS is enabled.