mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 04:24:43 +00:00
This patch adds the user mode register PL3_SSP which is part of the
Intel(R) Control-Flow Enforcement Technology (CET) feature for support
of shadow stack.
For now, only native and remote debugging support for shadow stack
userspace on amd64 linux are covered by this patch including 64 bit and
x32 support. 32 bit support is not covered due to missing Linux kernel
support.
This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
which is failing in case the shadow stack pointer is unavailable.
Such a state is possible if shadow stack is disabled for the current thread
but supported by HW.
This test uses the Python unwinder inline-frame-cycle-unwind.py which fakes
the cyclic stack cycle by reading the pending frame's registers and adding
them to the unwinder:
~~~
for reg in pending_frame.architecture().registers("general"):
val = pending_frame.read_register(reg)
unwinder.add_saved_register(reg, val)
return unwinder
~~~
However, in case the python unwinder is used we add a register (pl3_ssp) that is
unavailable. This leads to a NOT_AVAILABLE_ERROR caught in
gdb/frame-unwind.c:frame_unwind_try_unwinder and it is continued with standard
unwinders. This destroys the faked cyclic behavior and the stack is
further unwinded after frame 5.
In the working scenario an error should be triggered:
~~~
bt
0 inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
1 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
2 0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
3 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
4 0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
5 normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5: backtrace when the unwind is broken at frame 5
~~~
To fix the Python unwinder, we simply skip the unavailable registers.
Also it makes the test gdb.dap/scopes.exp fail. The shadow stack feature is
disabled by default, so the pl3_ssp register which is added with my CET
shadow stack series will be shown as unavailable and we see a TCL error:
~~
>>> {"seq": 12, "type": "request", "command": "variables", "arguments": {"variablesReference": 2, "count": 85}}
Content-Length: 129^M
^M
{"request_seq": 12, "type": "response", "command": "variables", "success": false, "message": "value is not available", "seq": 25}FAIL: gdb.dap/scopes.exp: fetch all registers success
ERROR: tcl error sourcing /tmp/gdb/testsuite/gdb.dap/scopes.exp.
ERROR: tcl error code TCL LOOKUP DICT body
ERROR: key "body" not known in dictionary
while executing
"dict get $val body variables"
(file "/tmp/gdb/testsuite/gdb.dap/scopes.exp" line 152)
invoked from within
"source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
("uplevel" body line 1)
invoked from within
"uplevel #0 source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
invoked from within
"catch "uplevel #0 source $test_file_name" msg"
UNRESOLVED: gdb.dap/scopes.exp: testcase '/tmp/gdb/testsuite/gdb.dap/scopes.exp' aborted due to Tcl error
~~
I am fixing this by enabling the test for CET shadow stack, in case we
detect that the HW supports it:
~~~
# If x86 shadow stack is supported we need to configure GLIBC_TUNABLES
# such that the feature is enabled and the register pl3_ssp is
# available. Otherwise the reqeust to fetch all registers will fail
# with "message": "value is not available".
if { [allow_ssp_tests] } {
append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
}
~~~
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Approved-By: Luis Machado <luis.machado@arm.com>
Approved-By: Andrew Burgess <aburgess@redhat.com>
107 lines
3.7 KiB
C++
107 lines
3.7 KiB
C++
/* Native-dependent code for GNU/Linux x86 (i386 and x86-64).
|
||
|
||
Copyright (C) 1999-2025 Free Software Foundation, Inc.
|
||
|
||
This file is part of GDB.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#ifndef GDB_X86_LINUX_NAT_H
|
||
#define GDB_X86_LINUX_NAT_H
|
||
|
||
#include "gdb_proc_service.h"
|
||
#include "linux-nat.h"
|
||
#include "gdbsupport/x86-xstate.h"
|
||
#include "x86-nat.h"
|
||
#include "nat/x86-linux.h"
|
||
|
||
struct x86_linux_nat_target : public x86_nat_target<linux_nat_target>
|
||
{
|
||
virtual ~x86_linux_nat_target () override = 0;
|
||
|
||
/* Add the description reader. */
|
||
const struct target_desc *read_description () override;
|
||
|
||
struct btrace_target_info *enable_btrace (thread_info *tp,
|
||
const struct btrace_config *conf) override;
|
||
void disable_btrace (struct btrace_target_info *tinfo) override;
|
||
void teardown_btrace (struct btrace_target_info *tinfo) override;
|
||
enum btrace_error read_btrace (struct btrace_data *data,
|
||
struct btrace_target_info *btinfo,
|
||
enum btrace_read_type type) override;
|
||
const struct btrace_config *btrace_conf (const struct btrace_target_info *) override;
|
||
|
||
x86_xsave_layout fetch_x86_xsave_layout () override
|
||
{ return m_xsave_layout; }
|
||
|
||
/* These two are rewired to low_ versions. linux-nat.c queries
|
||
stopped-by-watchpoint info as soon as an lwp stops (via the low_
|
||
methods) and caches the result, to be returned via the normal
|
||
non-low methods. */
|
||
bool stopped_by_watchpoint () override
|
||
{ return linux_nat_target::stopped_by_watchpoint (); }
|
||
|
||
bool stopped_data_address (CORE_ADDR *addr_p) override
|
||
{ return linux_nat_target::stopped_data_address (addr_p); }
|
||
|
||
bool low_stopped_by_watchpoint () override
|
||
{ return x86_nat_target::stopped_by_watchpoint (); }
|
||
|
||
bool low_stopped_data_address (CORE_ADDR *addr_p) override
|
||
{ return x86_nat_target::stopped_data_address (addr_p); }
|
||
|
||
void low_new_fork (struct lwp_info *parent, pid_t child_pid) override;
|
||
|
||
void low_forget_process (pid_t pid) override
|
||
{ x86_forget_process (pid); }
|
||
|
||
void low_prepare_to_resume (struct lwp_info *lwp) override
|
||
{ x86_linux_prepare_to_resume (lwp); }
|
||
|
||
void low_new_thread (struct lwp_info *lwp) override
|
||
{ x86_linux_new_thread (lwp); }
|
||
|
||
void low_delete_thread (struct arch_lwp_info *lwp) override
|
||
{ x86_linux_delete_thread (lwp); }
|
||
|
||
protected:
|
||
/* Override the GNU/Linux inferior startup hook. */
|
||
void post_startup_inferior (ptid_t) override;
|
||
|
||
private:
|
||
x86_xsave_layout m_xsave_layout;
|
||
};
|
||
|
||
|
||
|
||
/* Helper for ps_get_thread_area. Sets BASE_ADDR to a pointer to
|
||
the thread local storage (or its descriptor) and returns PS_OK
|
||
on success. Returns PS_ERR on failure. */
|
||
|
||
extern ps_err_e x86_linux_get_thread_area (pid_t pid, void *addr,
|
||
unsigned int *base_addr);
|
||
|
||
/* Fetch the value of the shadow stack pointer register from process/thread
|
||
TID and store it to GDB's register cache. */
|
||
|
||
extern void x86_linux_fetch_ssp (regcache *regcache, const int tid);
|
||
|
||
/* Read the value of the shadow stack pointer from GDB's register cache
|
||
and store it in the shadow stack pointer register of process/thread TID.
|
||
Throw an error in case of failure. */
|
||
|
||
extern void x86_linux_store_ssp (const regcache *regcache, const int tid);
|
||
|
||
#endif /* GDB_X86_LINUX_NAT_H */
|