From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id efpxOQPhtGrS4DsAWB0awg (envelope-from ) for ; Thu, 24 Sep 2026 04:36:19 -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=QXbTL+UD; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id E94CE1E06B; Thu, 24 Sep 2026 04:36:19 -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 vm01.sourceware.org (vm01.sourceware.org [38.145.34.32]) (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 A75301E04E for ; Thu, 24 Sep 2026 04:36:18 -0400 (EDT) Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 465F14BB3BFD for ; Thu, 24 Sep 2026 08:36:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 465F14BB3BFD 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=QXbTL+UD Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.14]) by sourceware.org (Postfix) with ESMTPS id 99B724BB3BF9 for ; Thu, 24 Sep 2026 08:34:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 99B724BB3BF9 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 99B724BB3BF9 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=192.198.163.14 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1790238889; cv=none; b=KBDt3mvJfCzXW+her7B15pNEPUVGpccfrk9AAoYcfPaYcYIEDcBGyvrNFGAMTTWDxp/rrtJnEV+1IQGJWDN2kXdPG5Z+CnPneoQVDoZ4HYIK26cg0U4CVpdsiKrGzHCWNIjmjvfCZOprPplzVXBoFZ8eeAWVTNpFBo9/q8OtRRY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1790238889; c=relaxed/simple; bh=7H3nBewiDkg0IJL/gU4lkRk68YxYXmoZR/uw98huoMA=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=XAIVIKg4FoHpw2N0lHuk6ivM2hQ6kJqtGuAQdh0u3zpb7C9djsY33lGG57fXMaySYExpx6Q+rVblhxPLZv1K2FzY5+Sgl6Xcjnpj4+MuG2WYIocEbJIepfxnTJOPEc5l7yk8IrHZX1Rt3cP2eqeIn45uWKqAmH1/CxrS8Xdxh9w= ARC-Authentication-Results: i=1; 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=QXbTL+UD DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 99B724BB3BF9 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1790238889; x=1821774889; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=7H3nBewiDkg0IJL/gU4lkRk68YxYXmoZR/uw98huoMA=; b=QXbTL+UDNgp8C7f2Agl5LESAZy5/4FxiLKxCF6EuocqWg46u2JY6yufM LZvwdchEgW4cRZSyxmhKDuqqJi5GfMa2lUxuQVwHwSDvVIPA9o+iC2MkI cJw3mRrU1uCtxibSdaP3uOSK00UKls6TBiVfcaX9KfLxgvzgWqvKsqaNR ++wDiST+xhyftPWW2ONHSo3CqGGvIdoKYyt/nNEBjzaVUQvB8rJCqHlT4 qA8OD+41uixRws56/43p1zy9JbP9WmkzoEEPq0hxpztVA+6scE98RyBEJ Zedtx9YhaMnslUXB+5avLbaTRxwTgFZbnIZ1nI0lC65Hgda1X1J8qEWO6 w==; X-CSE-ConnectionGUID: 7EwBuPi7QMKDq2WPv2duMQ== X-CSE-MsgGUID: /VPCpBBtSLqU1tB6Djq3kA== X-IronPort-AV: E=McAfee;i="6800,10657,11914"; a="91021372" X-IronPort-AV: E=Sophos;i="6.27,120,1787036400"; d="scan'208";a="91021372" Received: from fmviesa003.fm.intel.com ([10.60.135.143]) by fmvoesa108.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Sep 2026 01:34:48 -0700 X-CSE-ConnectionGUID: MXGEWTDITc6GFHXZgyHy9A== X-CSE-MsgGUID: FrYbUm7wSuGF5dKa3zbDWQ== X-ExtLoop1: 1 Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by fmviesa003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 24 Sep 2026 01:34:47 -0700 From: Christina Joos To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org, tom@tromey.com, luis.machado.foss@gmail.com Subject: [PATCH v5 09/13] gdb: Provide gdbarch hook to distinguish shadow stack backtrace elements. Date: Thu, 24 Sep 2026 10:33:06 +0200 Message-ID: <20260924083311.1961530-10-christina.joos@intel.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260924083311.1961530-1-christina.joos@intel.com> References: <20260924083311.1961530-1-christina.joos@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 or on ARM with GCS, there can be elements on the shadow stack which are not return addresses. In this case, we don't want to print the shadow stack element, but a string instead which describes the frame similar to the normal backtrace command for dummy frames or signals. Provide a gdbarch hook to distinguish between return and non-return addresses and to configure a string which is printed instead of the shadow stack element. Reviewed-by: Thiago Jung Bauermann --- gdb/doc/gdb.texinfo | 19 +++++++++++++++++++ gdb/gdbarch-gen.c | 32 ++++++++++++++++++++++++++++++++ gdb/gdbarch-gen.h | 19 +++++++++++++++++++ gdb/gdbarch_components.py | 25 +++++++++++++++++++++++++ gdb/shadow-stack.c | 36 +++++++++++++++++++++++++++++++++++- gdb/shadow-stack.h | 26 ++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 1 deletion(-) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 418c83224e8..c2f9474b4de 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -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 +#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. diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index f5f85ba1f63..894522a7243 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -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 +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; +} diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 75a867ee8f5..08358b18fcf 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -1795,3 +1795,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 (struct gdbarch *gdbarch, const CORE_ADDR ssp, const CORE_ADDR value, const unsigned long level); +std::optional 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); diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index 26d104e841f..e8677d641bb 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -2847,3 +2847,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", + name="is_no_return_shadow_stack_address", + params=[ + ("const CORE_ADDR", "ssp"), + ("const CORE_ADDR", "value"), + ("const unsigned long", "level"), + ], + predicate=True, +) diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c index 0f6bba148ac..75d9e0597ac 100644 --- a/gdb/shadow-stack.c +++ b/gdb/shadow-stack.c @@ -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 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 ({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::unwind_prev_shadow_stack_frame_info (std::pair 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(). */ diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h index 5c89aee70bb..4278620c70c 100644 --- a/gdb/shadow-stack.h +++ b/gdb/shadow-stack.h @@ -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 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 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. */ -- 2.53.0 ________________________________________ Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 (89) 99143-0 www.intel.de Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman Chairperson of the Supervisory Board: Sonja Pierer Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928 This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.