@@ -8811,6 +8811,25 @@ This is how a shadow stack backtrace looks like on amd64:
@end group
@end smallexample
+There can be elements on the shadow stack which are not return addresses,
+for example on x86 with the Intel Control-Flow Enforcement Technology
+(@xref{CET}). In case of signals, the old shadow stack pointer is pushed
+in a special format with bit 63 set. See @url{https://docs.kernel.org/arch/x86/shstk.html}
+for more details. For such shadow stack elements, the shadow stack
+frame just contains the level and a string describing the shadow stack
+element:
+
+@smallexample
+@group
+(gdb) bt -shadow 4
+#0 0x00007ffff7c45330 in __restore_rt from /lib/x86_64-linux-gnu/libc.so.6
+#1 <sigframe token>
+#2 0x00007ffff7c4527e in __GI_raise at ../sysdeps/posix/raise.c:26
+#3 0x000055555555519d in main at tmp/amd64-shadow-stack-signal.c:29
+(More shadow stack frames follow...)
+@end group
+@end smallexample
+
@end table
The optional @var{qualifier} is maintained for backward compatibility.
@@ -256,6 +256,7 @@ struct gdbarch
gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = nullptr;
gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr;
int shadow_stack_element_size_aligned = 8;
+ gdbarch_is_no_return_shadow_stack_address_ftype *is_no_return_shadow_stack_address = nullptr;
};
/* Create a new ``struct gdbarch'' based on information provided by
@@ -519,6 +520,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
/* Skip verify of address_in_shadow_stack_memory_range, has predicate. */
/* Skip verify of top_addr_empty_shadow_stack, has predicate. */
/* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0. */
+ /* Skip verify of is_no_return_shadow_stack_address, has predicate. */
if (!log.empty ())
internal_error (_("verify_gdbarch: the following are invalid ...%s"),
log.c_str ());
@@ -1357,6 +1359,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
gdb_printf (file,
"gdbarch_dump: shadow_stack_element_size_aligned = %s\n",
plongest (gdbarch->shadow_stack_element_size_aligned));
+ gdb_printf (file,
+ "gdbarch_dump: gdbarch_is_no_return_shadow_stack_address_p() = %d\n",
+ gdbarch_is_no_return_shadow_stack_address_p (gdbarch));
+ gdb_printf (file,
+ "gdbarch_dump: is_no_return_shadow_stack_address = <%s>\n",
+ host_address_to_string (gdbarch->is_no_return_shadow_stack_address));
if (gdbarch->dump_tdep != nullptr)
gdbarch->dump_tdep (gdbarch, file);
}
@@ -5362,3 +5370,27 @@ set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch,
{
gdbarch->shadow_stack_element_size_aligned = shadow_stack_element_size_aligned;
}
+
+bool
+gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch *gdbarch)
+{
+ gdb_assert (gdbarch != nullptr);
+ return gdbarch->is_no_return_shadow_stack_address != nullptr;
+}
+
+std::optional<shadow_stack_frame_info>
+gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (gdbarch->is_no_return_shadow_stack_address != nullptr);
+ if (gdbarch_debug >= 2)
+ gdb_printf (gdb_stdlog, "gdbarch_is_no_return_shadow_stack_address called\n");
+ return gdbarch->is_no_return_shadow_stack_address (gdbarch, ssp, value, level);
+}
+
+void
+set_gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch,
+ gdbarch_is_no_return_shadow_stack_address_ftype is_no_return_shadow_stack_address)
+{
+ gdbarch->is_no_return_shadow_stack_address = is_no_return_shadow_stack_address;
+}
@@ -1789,3 +1789,22 @@ void set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, gdbarch_t
int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch);
void set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch, int shadow_stack_element_size_aligned);
+
+/* There can be elements on the shadow stack which are not return addresses.
+ This happens for example on x86 with CET in case of signals.
+ If an architecture implements the command option 'backtrace -shadow' and
+ the shadow stack can contain elements which are not return addresses, this
+ function has to be provided.
+ Return a shadow stack frame info with frame type
+ ssp_frame_type::non_return_frame, if the shadow stack pointer SSP belongs
+ to a valid shadow stack frame while the element on the shadow stack
+ VALUE does not refer to a return address. In that case, also the frame's
+ attribute non_return_description has to be set to a string which is
+ displayed instead of the element on the shadow stack in the shadow stack
+ backtrace. Otherwise, return an empty optional. */
+
+bool gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch *gdbarch);
+
+using gdbarch_is_no_return_shadow_stack_address_ftype = std::optional<shadow_stack_frame_info> (struct gdbarch *gdbarch, const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level);
+std::optional<shadow_stack_frame_info> gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level);
+void set_gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, gdbarch_is_no_return_shadow_stack_address_ftype *is_no_return_shadow_stack_address);
@@ -2839,3 +2839,28 @@ this value.
# linux starting with Linux kernel v6.6.
unused=True,
)
+
+Method(
+ comment="""
+There can be elements on the shadow stack which are not return addresses.
+This happens for example on x86 with CET in case of signals.
+If an architecture implements the command option 'backtrace -shadow' and
+the shadow stack can contain elements which are not return addresses, this
+function has to be provided.
+Return a shadow stack frame info with frame type
+ssp_frame_type::non_return_frame, if the shadow stack pointer SSP belongs
+to a valid shadow stack frame while the element on the shadow stack
+VALUE does not refer to a return address. In that case, also the frame's
+attribute non_return_description has to be set to a string which is
+displayed instead of the element on the shadow stack in the shadow stack
+backtrace. Otherwise, return an empty optional.
+""",
+ type="std::optional<shadow_stack_frame_info>",
+ name="is_no_return_shadow_stack_address",
+ params=[
+ ("const CORE_ADDR", "ssp"),
+ ("const CORE_ADDR", "value"),
+ ("const unsigned long", "level"),
+ ],
+ predicate=True,
+)
@@ -233,6 +233,29 @@ do_print_shadow_stack_frame_info
const int element_size
= gdbarch_shadow_stack_element_size_aligned (frame.arch);
+ if (frame.type != ssp_frame_type::normal_frame)
+ {
+ std::string str;
+ if (frame.type == ssp_frame_type::non_return_frame)
+ {
+ /* For non-return frames, the string must have a value. */
+ gdb_assert (frame.non_return_description.has_value ());
+ str = frame.non_return_description.value ();
+ }
+ else
+ gdb_assert_not_reached ("Invalid shadow stack frame type.");
+
+ ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame");
+ uiout->text ("#");
+ uiout->field_fmt_signed (2, ui_left, "level", frame.level);
+
+ uiout->field_string ("func", str, metadata_style.style ());
+
+ uiout->text ("\n");
+ gdb_flush (gdb_stdout);
+ return;
+ }
+
if (fp_opts.print_frame_info != print_frame_info_auto)
{
/* Use the specific frame information desired by the user. */
@@ -398,6 +421,15 @@ get_shadow_stack_frame_info
if (!read_shadow_stack_memory (fallback_arch, ssp, value))
return {};
+ if (gdbarch_is_no_return_shadow_stack_address_p (fallback_arch))
+ {
+ std::optional<shadow_stack_frame_info> no_return_frame
+ = gdbarch_is_no_return_shadow_stack_address (fallback_arch, ssp,
+ value, level);
+ if (no_return_frame.has_value ())
+ return *no_return_frame;
+ }
+
/* At this point, we know that SSP points to VALUE which is a return
address. In contrast to find_frame_sal which is used for the normal
backtrace command, VALUE always points at the return instruction
@@ -414,6 +446,7 @@ get_shadow_stack_frame_info
return std::optional<shadow_stack_frame_info>
({ssp, value, level, sal_arch, sal,
+ ssp_frame_type::normal_frame, {},
ssp_unwind_stop_reason::no_error});
}
@@ -421,7 +454,8 @@ std::optional<shadow_stack_frame_info>
shadow_stack_frame_info::unwind_prev_shadow_stack_frame_info
(std::pair<CORE_ADDR, CORE_ADDR> range)
{
- if (!user_set_backtrace_options.backtrace_past_main
+ if (this->type == ssp_frame_type::normal_frame
+ && !user_set_backtrace_options.backtrace_past_main
&& this->inside_main_func ())
{
/* Don't unwind past main(). */
@@ -63,6 +63,21 @@ enum class ssp_unwind_stop_reason
memory_read_error,
};
+enum class ssp_frame_type
+{
+ /* A normal shadow stack frame which belongs to a return address of the
+ program execution flow. */
+ normal_frame,
+
+ /* A shadow stack frame which does not belong to a return address.
+ It is possible, on x86 for instance, that an element on the shadow
+ stack is not a return address. We don't want to print the address
+ in that case but only a string describing that specific frame type.
+ This frame type is configured in the target specific implementation
+ of is_no_return_shadow_stack_address. */
+ non_return_frame,
+};
+
/* Information of a shadow stack frame belonging to a shadow stack element
at shadow stack pointer SSP. */
@@ -92,6 +107,17 @@ class shadow_stack_frame_info
/* Optional SAL object of the current shadow stack frame. */
std::optional<symtab_and_line> sal;
+ /* The shadow stack frame type. */
+ ssp_frame_type type;
+
+ /* If this is not a normal shadow stack frame belonging to a return
+ address of the program execution flow then the frame will have the
+ type ssp_frame_type::non_return_frame. In this case, this string
+ is configured to describe this specific frame type and it will
+ be printed in the shadow stack backtrace instead of the element on
+ the shadow stack. */
+ std::optional<std::string> non_return_description;
+
/* If unwinding of the previous frame info fails assign this value to a
matching condition ssp_unwind_stop_reason
> ssp_unwind_stop_reason::no_error. */