Skip to content

Commit 75aa5a3

Browse files
[lldb][AArch64] Add Guarded Control Stack support for Linux core files (#124293)
This allows you to read the same registers as you would for a live process. As the content of proc/pid/smaps is not included in the core file, we don't get the "ss" marker that tell us that it is shadow stack. The GCS region is still in the list though.
1 parent 65f81df commit 75aa5a3

File tree

5 files changed

+54
-0
lines changed

5 files changed

+54
-0
lines changed

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ RegisterContextCorePOSIX_arm64::Create(Thread &thread, const ArchSpec &arch,
6969
if (fpmr_data.GetByteSize() >= sizeof(uint64_t))
7070
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskFPMR);
7171

72+
DataExtractor gcs_data = getRegset(notes, arch.GetTriple(), AARCH64_GCS_Desc);
73+
struct __attribute__((packed)) gcs_regs {
74+
uint64_t features_enabled;
75+
uint64_t features_locked;
76+
uint64_t gcspr_e0;
77+
};
78+
if (gcs_data.GetByteSize() >= sizeof(gcs_regs))
79+
opt_regsets.Set(RegisterInfoPOSIX_arm64::eRegsetMaskGCS);
80+
7281
auto register_info_up =
7382
std::make_unique<RegisterInfoPOSIX_arm64>(arch, opt_regsets);
7483
return std::unique_ptr<RegisterContextCorePOSIX_arm64>(
@@ -136,6 +145,9 @@ RegisterContextCorePOSIX_arm64::RegisterContextCorePOSIX_arm64(
136145
if (m_register_info_up->IsFPMRPresent())
137146
m_fpmr_data = getRegset(notes, target_triple, AARCH64_FPMR_Desc);
138147

148+
if (m_register_info_up->IsGCSPresent())
149+
m_gcs_data = getRegset(notes, target_triple, AARCH64_GCS_Desc);
150+
139151
ConfigureRegisterContext();
140152
}
141153

@@ -330,6 +342,11 @@ bool RegisterContextCorePOSIX_arm64::ReadRegister(const RegisterInfo *reg_info,
330342
assert(offset < m_mte_data.GetByteSize());
331343
value.SetFromMemoryData(*reg_info, m_mte_data.GetDataStart() + offset,
332344
reg_info->byte_size, lldb::eByteOrderLittle, error);
345+
} else if (IsGCS(reg)) {
346+
offset = reg_info->byte_offset - m_register_info_up->GetGCSOffset();
347+
assert(offset < m_gcs_data.GetByteSize());
348+
value.SetFromMemoryData(*reg_info, m_gcs_data.GetDataStart() + offset,
349+
reg_info->byte_size, lldb::eByteOrderLittle, error);
333350
} else if (IsSME(reg)) {
334351
// If you had SME in the process, active or otherwise, there will at least
335352
// be a ZA header. No header, no SME at all.

lldb/source/Plugins/Process/elf-core/RegisterContextPOSIXCore_arm64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class RegisterContextCorePOSIX_arm64 : public RegisterContextPOSIX_arm64 {
6363
lldb_private::DataExtractor m_mte_data;
6464
lldb_private::DataExtractor m_zt_data;
6565
lldb_private::DataExtractor m_fpmr_data;
66+
lldb_private::DataExtractor m_gcs_data;
6667

6768
SVEState m_sve_state = SVEState::Unknown;
6869
uint16_t m_sve_vector_length = 0;

lldb/source/Plugins/Process/elf-core/RegisterUtilities.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ constexpr RegsetDesc AARCH64_FPMR_Desc[] = {
148148
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_FPMR},
149149
};
150150

151+
constexpr RegsetDesc AARCH64_GCS_Desc[] = {
152+
{llvm::Triple::Linux, llvm::Triple::aarch64, llvm::ELF::NT_ARM_GCS},
153+
};
154+
151155
constexpr RegsetDesc PPC_VMX_Desc[] = {
152156
{llvm::Triple::FreeBSD, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},
153157
{llvm::Triple::Linux, llvm::Triple::UnknownArch, llvm::ELF::NT_PPC_VMX},

lldb/test/API/linux/aarch64/gcs/TestAArch64LinuxGCS.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,3 +374,35 @@ def test_gcs_expression_enable_gcs(self):
374374
# consistent with the disabled -> enabled behaviour.
375375
enabled |= 1
376376
self.check_gcs_registers(enabled, locked, spr_el0)
377+
378+
@skipIfLLVMTargetMissing("AArch64")
379+
def test_gcs_core_file(self):
380+
# To re-generate the core file, build the test file and run it on a
381+
# machine with GCS enabled. Note that because the kernel decides where
382+
# the GCS is stored, the value of gcspr_el0 and which memory region it
383+
# points to may change between runs.
384+
385+
self.runCmd("target create --core corefile")
386+
387+
self.expect(
388+
"bt",
389+
substrs=["stop reason = SIGSEGV: control protection fault"],
390+
)
391+
392+
self.expect(
393+
"register read --all",
394+
substrs=[
395+
"Guarded Control Stack Registers:",
396+
"gcs_features_enabled = 0x0000000000000001",
397+
"gcs_features_locked = 0x0000000000000000",
398+
"gcspr_el0 = 0x0000ffffa83ffff0",
399+
],
400+
)
401+
402+
# Core files do not include /proc/pid/smaps, so we cannot see the
403+
# shadow stack "ss" flag. gcspr_el0 should at least point to some mapped
404+
# region.
405+
self.expect(
406+
"memory region $gcspr_el0",
407+
substrs=["[0x0000ffffa8000000-0x0000ffffa8400000) rw-"],
408+
)
24 KB
Binary file not shown.

0 commit comments

Comments
 (0)