Skip to content

Commit 290899d

Browse files
committed
Check what happens if we fail to setup the gcs
1 parent 56d1fef commit 290899d

File tree

2 files changed

+37
-13
lines changed

2 files changed

+37
-13
lines changed

lldb/source/Plugins/ABI/AArch64/ABISysV_arm64.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -101,14 +101,21 @@ static Status PushToLinuxGuardedControlStack(addr_t return_addr,
101101
Status error;
102102
size_t wrote = thread.GetProcess()->WriteMemory(gcspr_el0, &return_addr,
103103
sizeof(return_addr), error);
104-
if ((wrote != sizeof(return_addr) || error.Fail()))
104+
if ((wrote != sizeof(return_addr) || error.Fail())) {
105+
// When PrepareTrivialCall fails, the register context is not restored,
106+
// unlike when an expression fails to execute. This is arguably a bug,
107+
// see https://github.com/llvm/llvm-project/issues/124269.
108+
// For now we are handling this here specifically. We can assume this
109+
// write will work as the one to decrement the register did.
110+
reg_ctx->WriteRegisterFromUnsigned(gcspr_el0_info, gcspr_el0 + 8);
105111
return Status("Failed to write new Guarded Control Stack entry.");
112+
}
106113

107114
Log *log = GetLog(LLDBLog::Expressions);
108115
LLDB_LOGF(log,
109-
"Pushed return address 0x%" PRIx64 "to Guarded Control Stack. "
116+
"Pushed return address 0x%" PRIx64 " to Guarded Control Stack. "
110117
"gcspr_el0 was 0%" PRIx64 ", is now 0x%" PRIx64 ".",
111-
return_addr, gcspr_el0 + 8, gcspr_el0);
118+
return_addr, gcspr_el0 - 8, gcspr_el0);
112119

113120
// gcspr_el0 will be restored to the original value by lldb-server after
114121
// the call has finished, which serves as the "pop".
@@ -143,6 +150,18 @@ bool ABISysV_arm64::PrepareTrivialCall(Thread &thread, addr_t sp,
143150
if (args.size() > 8)
144151
return false;
145152

153+
// Do this first, as it's got the most chance of failing (though still very
154+
// low).
155+
if (GetProcessSP()->GetTarget().GetArchitecture().GetTriple().isOSLinux()) {
156+
Status err = PushToLinuxGuardedControlStack(return_addr, reg_ctx, thread);
157+
// If we could not manage the GCS, the expression will certainly fail,
158+
// and if we just carried on, that failure would be a lot more cryptic.
159+
if (err.Fail()) {
160+
LLDB_LOGF(log, "Failed to setup Guarded Call Stack: %s", err.AsCString());
161+
return false;
162+
}
163+
}
164+
146165
for (size_t i = 0; i < args.size(); ++i) {
147166
const RegisterInfo *reg_info = reg_ctx->GetRegisterInfo(
148167
eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1 + i);
@@ -159,16 +178,6 @@ bool ABISysV_arm64::PrepareTrivialCall(Thread &thread, addr_t sp,
159178
return_addr))
160179
return false;
161180

162-
if (GetProcessSP()->GetTarget().GetArchitecture().GetTriple().isOSLinux()) {
163-
Status err = PushToLinuxGuardedControlStack(return_addr, reg_ctx, thread);
164-
// If we could not manage the GCS, the expression will certainly fail,
165-
// and if we just carried on, that failure would be a lot more cryptic.
166-
if (err.Fail()) {
167-
LLDB_LOGF(log, "Failed to setup Guarded Call Stack: %s", err.AsCString());
168-
return false;
169-
}
170-
}
171-
172181
// Set "sp" to the requested value
173182
if (!reg_ctx->WriteRegisterFromUnsigned(
174183
reg_ctx->GetRegisterInfo(eRegisterKindGeneric,

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,12 +263,27 @@ def test_gcs_expression_simple(self):
263263
substrs=["stopped", "stop reason = breakpoint"],
264264
)
265265

266+
# If we fail to setup the GCS entry, we should not leave any of the GCS registers
267+
# changed. The last thing we do is write a new GCS entry to memory and
268+
# to simulate the failure of that, temporarily point the GCS to the zero page.
269+
#
270+
# We use the value 8 here because LLDB will decrement it by 8 so it points to
271+
# what we think will be an empty entry on the guarded control stack.
272+
_, _, original_gcspr = self.check_gcs_registers()
273+
self.runCmd("register write gcspr_el0 8")
274+
before = self.check_gcs_registers()
275+
self.expect(expr_cmd, error=True)
276+
self.check_gcs_registers(*before)
277+
# Point to the valid shadow stack region again.
278+
self.runCmd(f"register write gcspr_el0 {original_gcspr}")
279+
266280
# This time we do need to push to the GCS and having done so, we can
267281
# return from this expression without causing a fault.
268282
before = self.check_gcs_registers()
269283
self.expect(expr_cmd, substrs=["(unsigned long) 1"])
270284
self.check_gcs_registers(*before)
271285

286+
272287
@skipUnlessPlatform(["linux"])
273288
def test_gcs_expression_disable_gcs(self):
274289
if not self.isAArch64GCS():

0 commit comments

Comments
 (0)