From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id We8KMHaD0mimLA8AWB0awg (envelope-from ) for ; Tue, 23 Sep 2025 07:24:38 -0400 Authentication-Results: simark.ca; dkim=pass (2048-bit key; unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=Vej3TPUu; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id BD3491E0BA; Tue, 23 Sep 2025 07:24:38 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-3.4 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,RCVD_IN_VALIDITY_CERTIFIED_BLOCKED, RCVD_IN_VALIDITY_RPBL_BLOCKED,RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=ham autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 6FFEE1E047 for ; Tue, 23 Sep 2025 07:24:37 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 19B323858C24 for ; Tue, 23 Sep 2025 11:24:37 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 19B323858C24 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=intel.com header.i=@intel.com header.a=rsa-sha256 header.s=Intel header.b=Vej3TPUu Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.16]) by sourceware.org (Postfix) with ESMTPS id F23793858410 for ; Tue, 23 Sep 2025 11:19:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org F23793858410 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=intel.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org F23793858410 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.16 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626389; cv=none; b=Fvic3zwzPhDbfpDmEYPs/Z6lMr/f0rjuWyiG9olKLLe4a0vWEgymuF2Te1oQ25CiDJsGIwtyEoHCekHQ9wvCInwu9qlKFm2+rMZELRWsdURmcNbjf+6eT/ETztCAkSKv/2ZiUjFIuVp+S0uzpv4e2pmT2aUniB2LL/HeiyWRZRY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626389; c=relaxed/simple; bh=M3eHcqwxT5HlphSk5lFRNZUFrKZnztWbah8LTw/WYF8=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=NBNmiK2hY2rr3JquHsc/DHbySN46ncByfSKlcnbWPAOE5EJN+yQxSJw9apLZqaAQBkD9USbZ/ny2u3S3+HU75VhO9vbrer2wzx/ckmmWkSMvOwJ50+TaeU36V+LkcaDS8+Sq0CVXWGMrKD0Be0oeBAbBCqaEeRVFyfywxjvOdjU= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org F23793858410 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1758626389; x=1790162389; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=M3eHcqwxT5HlphSk5lFRNZUFrKZnztWbah8LTw/WYF8=; b=Vej3TPUu7Ag5366jjjqPKhcEWO49iIDHVmmONDp/ukkSEdBkzHje6bWw dvbGT6oDyoZ2jqWj31O1osScJZT3ekrDpl29Pj/2y7XpMs1+rgsf2fDqk 9DoN9V9Sj1LNTVVyh/fuxgY4TU+lXji811oRXwZtp4Uwe9JyWWUFHZONK zVcNwIfLZphvJobtg+O6vnbClNxzp5FHZmjzEQMwZZ05PHJsToFki8hKT bXjOvyz/i57qjOj8qDdJQe2JSrV0CrUG45B6P7aVfXRf592cMLVwrhT/Y +9fL+63z7VmnnXBxxVWfuZ+ZkNKQpvfZ5LkWA41E13mWQ/GNykv29Mp/R w==; X-CSE-ConnectionGUID: hXWrmnQGTd6asRd6kUurAQ== X-CSE-MsgGUID: pTxP1BMHTEO1NWxxPa9BBg== X-IronPort-AV: E=McAfee;i="6800,10657,11561"; a="61071778" X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="61071778" Received: from fmviesa007.fm.intel.com ([10.60.135.147]) by orvoesa108.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:48 -0700 X-CSE-ConnectionGUID: LoF7mFdBRVOuwxv4kfUQbg== X-CSE-MsgGUID: c1lDA9llRkChByz6DW4EuQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="176335273" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by fmviesa007-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:47 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Subject: [PATCH 7/9] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements. Date: Tue, 23 Sep 2025 11:18:40 +0000 Message-Id: <20250923111842.4091694-8-christina.schimpe@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250923111842.4091694-1-christina.schimpe@intel.com> References: <20250923111842.4091694-1-christina.schimpe@intel.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org On x86 with CET there can be elements on the shadow stack which are not return addresses. In this case, we just want to print the element itself in the shadow stack backtrace, but no further information. Provide a gdbarch hook to distinguish between return and non-return addresses and use it to print the shadow stack backtrace as described above. --- gdb/doc/gdb.texinfo | 19 ++++++++++++ gdb/gdbarch-gen.c | 32 ++++++++++++++++++++ gdb/gdbarch-gen.h | 15 +++++++++ gdb/gdbarch.h | 1 + gdb/gdbarch_components.py | 17 +++++++++++ gdb/shadow-stack.c | 64 +++++++++++++++++---------------------- gdb/shadow-stack.h | 37 ++++++++++++++++++++++ 7 files changed, 148 insertions(+), 37 deletions(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index ebda4546b58..a0fde385a8e 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -8887,6 +8887,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. For such shadow stack elements, the +shadow stack frame just contains the level and the address on the shadow +stack, as shown in the following example by frame 1: + +@smallexample +@group +(gdb) bt shadow 4 +#0 0x00007ffff7c54d90 in __restore_rt from /lib64/libc.so.6 +#1 0x80007ffff79fffd8 +#2 0x00007ffff7c54ce6 in __GI_raise at ../sysdeps/posix/raise.c:27 +#3 0x000000000040115d in main at /tmp/amd64-shadow-stack-signal.c:32 +(More shadow stack frames follow...) +@end group +@end smallexample + + @node Selection @section Selecting a Frame diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index 640576f4b85..21def8fd3e7 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -266,6 +266,7 @@ struct gdbarch gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack = nullptr; gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range = 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 @@ -543,6 +544,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of top_addr_empty_shadow_stack, has predicate. */ /* Skip verify of address_in_shadow_stack_memory_range, 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 ()); @@ -1432,6 +1434,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 != NULL) gdbarch->dump_tdep (gdbarch, file); } @@ -5659,3 +5667,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 != NULL); + return gdbarch->is_no_return_shadow_stack_address != NULL; +} + +bool +gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->is_no_return_shadow_stack_address != NULL); + 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, frame); +} + +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; +} diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 34a32c346e8..0b92a448c78 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -1856,3 +1856,18 @@ extern void set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gd extern int gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch); extern 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 'backtrace shadow' and the + shadow stack can contain elements which are not return addresses, this + function has to be provided. + + Return true, if FRAME does not contain a return address in FRAME->VALUE + but another valid value for the architecture's shadow stack. */ + +extern bool gdbarch_is_no_return_shadow_stack_address_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_is_no_return_shadow_stack_address_ftype) (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame); +extern bool gdbarch_is_no_return_shadow_stack_address (struct gdbarch *gdbarch, const shadow_stack_frame_info &frame); +extern 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); diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 20497612811..57b8f021e93 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -31,6 +31,7 @@ #include "gdbsupport/gdb-checked-static-cast.h" #include "registry.h" #include "solib.h" +#include "shadow-stack.h" struct floatformat; struct ui_file; diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index 5223553941f..d925f5a7e87 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -2938,3 +2938,20 @@ this value. predefault="8", invalid=False, ) + +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 'backtrace shadow' and the +shadow stack can contain elements which are not return addresses, this +function has to be provided. + +Return true, if FRAME does not contain a return address in FRAME->VALUE +but another valid value for the architecture's shadow stack. +""", + type="bool", + name="is_no_return_shadow_stack_address", + params=[("const shadow_stack_frame_info &", "frame")], + predicate=True, +) diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index 759ef6989ea..42032f51781 100644 --- a/gdb/shadow-stack.c +++ b/gdb/shadow-stack.c @@ -222,43 +222,6 @@ pc_in_middle_of_statement (CORE_ADDR pc, symtab_and_line sal) return pc > sal.pc && pc <= sal.end; } -enum class ssp_unwind_stop_reason -{ - /* No particular reason; either we haven't tried unwinding yet, or we - didn't fail. */ - no_error = 0, - - /* We could not read the memory of the shadow stack element. */ - memory_read_error -}; - -/* Information of a shadow stack frame belonging to a shadow stack element - at shadow stack pointer SSP. */ - -class shadow_stack_frame_info -{ -public: - /* If possible, unwind the previous shadow stack frame info. RANGE is - the shadow stack memory range [start_address, end_address) belonging - to this frame's shadow stack pointer. If we cannot unwind the - previous frame info, set the unwind_stop_reason attribute. If we - reached the bottom of the shadow stack just don't return a value. */ - std::optional unwind_prev_shadow_stack_frame_info - (gdbarch *gdbarch, std::pair range); - - /* The shadow stack pointer. */ - CORE_ADDR ssp; - /* The value of the shadow stack at SSP. */ - CORE_ADDR value; - /* The level of the element on the shadow stack. */ - unsigned long level; - /* 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. */ - ssp_unwind_stop_reason unwind_stop_reason - = ssp_unwind_stop_reason::no_error; -}; - static gdb::unique_xmalloc_ptr find_pc_funname (CORE_ADDR pc) { @@ -285,6 +248,33 @@ do_print_shadow_stack_frame_info const shadow_stack_print_options &print_options, const shadow_stack_frame_info &frame, print_what print_what) { + if (gdbarch_is_no_return_shadow_stack_address_p (gdbarch) + && gdbarch_is_no_return_shadow_stack_address (gdbarch, frame)) + { + /* It is possible, for the x86 architecture for instance, that an + element on the shadow stack is not a return address. We still + want to print the address in that case but no further + information. */ + ui_out_emit_tuple tuple_emitter (uiout, "shadow-stack-frame"); + uiout->text ("#"); + uiout->field_fmt_signed (2, ui_left, "level", frame.level); + + /* On x86 there can be a shadow stack token at bit 63. For x32, the + address size is only 32 bit. Thus, we still must use + gdbarch_shadow_stack_element_size_aligned (and not + gdbarch_addr_bit) to determine the width of the address to be + printed. */ + const int element_size + = gdbarch_shadow_stack_element_size_aligned (gdbarch); + + uiout->field_string + ("addr", hex_string_custom (frame.value, element_size * 2), + address_style.style ()); + uiout->text ("\n"); + gdb_flush (gdb_stdout); + return; + } + if (print_options.print_frame_info != print_frame_info_auto) { /* Use the specific frame information desired by the user. */ diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index 2d33efafd22..f801d077820 100644 --- a/gdb/shadow-stack.h +++ b/gdb/shadow-stack.h @@ -55,4 +55,41 @@ std::array make_backtrace_shadow_options_def_group (shadow_stack_print_options *print_options); +enum class ssp_unwind_stop_reason +{ + /* No particular reason; either we haven't tried unwinding yet, or we + didn't fail. */ + no_error = 0, + + /* We could not read the memory of the shadow stack element. */ + memory_read_error +}; + +/* Information of a shadow stack frame belonging to a shadow stack element + at shadow stack pointer SSP. */ + +class shadow_stack_frame_info +{ +public: + /* If possible, unwind the previous shadow stack frame info. RANGE is + the shadow stack memory range [start_address, end_address) belonging + to this frame's shadow stack pointer. If we cannot unwind the + previous frame info, set the unwind_stop_reason attribute. If we + reached the bottom of the shadow stack just don't return a value. */ + std::optional unwind_prev_shadow_stack_frame_info + (gdbarch *gdbarch, std::pair range); + + /* The shadow stack pointer. */ + CORE_ADDR ssp; + /* The value of the shadow stack at SSP. */ + CORE_ADDR value; + /* The level of the element on the shadow stack. */ + unsigned long level; + /* 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. */ + ssp_unwind_stop_reason unwind_stop_reason + = ssp_unwind_stop_reason::no_error; +}; + #endif /* GDB_SHADOW_STACK_H */ -- 2.34.1 Intel Deutschland GmbH Registered Address: Am Campeon 10, 85579 Neubiberg, Germany Tel: +49 89 99 8853-0, www.intel.de Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928