From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id gPhSAsOpX2hqXyMAWB0awg (envelope-from ) for ; Sat, 28 Jun 2025 04:37:23 -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=ktjcuaZE; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 05F711E11E; Sat, 28 Jun 2025 04:37:23 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-10.1 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, RCVD_IN_VALIDITY_RPBL,RCVD_IN_VALIDITY_SAFE 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 337CF1E089 for ; Sat, 28 Jun 2025 04:37:22 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D432A3856DF0 for ; Sat, 28 Jun 2025 08:37:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org D432A3856DF0 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=ktjcuaZE Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.19]) by sourceware.org (Postfix) with ESMTPS id C82B0385781B for ; Sat, 28 Jun 2025 08:29:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C82B0385781B 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 C82B0385781B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=192.198.163.19 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751099359; cv=none; b=J6UZ21S0B3hjhz7L4LZyEk8h6HOcPtzDELulg5EsYWhI0kujHKS7WqdFg51Vs2WIiFDJYJ641q/o3AMuDrGc+tWFLKC/tJ40GIq1UxD7HfgtsA42IMmyp3P/DAt6VmXzP/C4mc4bnRJSq7++BZ98IOI4JV0D3Jiqh+2DJdHwDEY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751099359; c=relaxed/simple; bh=gmIClq/Sedrji9tv4j3E6E0qndJq4FBF59Ci/Ew2mac=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=I0Y6rXXUaSCatIAVd3EqwEShXK/2A2RK2k8wUstyV+oPV6Zfje4POdVvfn+h2g0q3Vo+sOFp6bJMAjiOWuF54G7pERkn1zXSHW3ms7o8O83bQR/0P/Pdjeh57ZDdaLL3CHnZljBvxcdkEaiAIe4CoDB7YF7kRFwHQElTAETkypA= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org C82B0385781B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1751099359; x=1782635359; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=gmIClq/Sedrji9tv4j3E6E0qndJq4FBF59Ci/Ew2mac=; b=ktjcuaZEW7YpmOnJRpDpgK0BcjiNN9F47JCgnVywlRQCVC7CSx/ZAJ32 XVDtQAZT23bbDjfD/t9hD6n6GBNxVSVWSLSrMlppkBnLKXjUBXLfRuZ98 asnkDwpdwNfq1fLYUkUZYutjFt1hW2qjALxE2RGNDKJGjvlqabvLLBAcM zUHzvxqci4oIe8vH8TjTSISIeXrFS5t21VCq8am0VyRMmdz4qyrLucy4u PtBEmjCjvgkCFBa7wsIOHGRiYhXpWQjhwUlJOdXnJ1r07Jtd7i+JaStVp 5mg7Uw8I+SjipB7Ct5kYedPk+CfNDiJThsnBwssuUB+SNv5RUGbr8OKsy w==; X-CSE-ConnectionGUID: hV5hqd5yRJaYqILtvptemw== X-CSE-MsgGUID: ciR7N/mfSa++01c/sbgZXQ== X-IronPort-AV: E=McAfee;i="6800,10657,11477"; a="52518189" X-IronPort-AV: E=Sophos;i="6.16,272,1744095600"; d="scan'208";a="52518189" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa113.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jun 2025 01:29:18 -0700 X-CSE-ConnectionGUID: wh9i9QjDQvCP1OfqsNqMjA== X-CSE-MsgGUID: X5Qu67vURG2iWCuCfAaBhg== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.16,272,1744095600"; d="scan'208";a="157538127" Received: from 0007e934a912.jf.intel.com (HELO localhost) ([10.165.58.208]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jun 2025 01:29:18 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org, luis.machado@arm.com Subject: [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux. Date: Sat, 28 Jun 2025 01:28:10 -0700 Message-ID: <20250628082810.332526-13-christina.schimpe@intel.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20250628082810.332526-1-christina.schimpe@intel.com> References: <20250628082810.332526-1-christina.schimpe@intel.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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 Currently, if displaced stepping is active and the single stepped instruction is a call instruction, the return address atop the stack is the address following the copied instruction. However, to allow normal program execution it has to be the address following the original instruction. Due to that reason, the return address is corrected in amd64_displaced_step_fixup and i386_displaced_step_fixup. For programs that are shadow-stack enabled we see a control-protection exception, as the address on the shadow stack does not match the address atop the stack. Fix this by correcting the shadow stack top address as well. Reviewed-By: Luis Machado Reviewed-By: Eli Zaretskii --- gdb/NEWS | 3 + gdb/amd64-linux-tdep.c | 16 +++- gdb/amd64-tdep.c | 15 +++ gdb/doc/gdb.texinfo | 11 ++- gdb/i386-tdep.c | 15 +++ .../gdb.arch/amd64-shadow-stack-disp-step.exp | 92 +++++++++++++++++++ 6 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp diff --git a/gdb/NEWS b/gdb/NEWS index ba555f0dea1..60510fefea4 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,9 @@ *** Changes since GDB 16 +* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer + size (X32) Shadow Stacks are now supported. + * Support for the shadow stack pointer register on x86-64 or x86-64 with 32-bit pointer size (X32) GNU/Linux. diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 899fe2df02c..782b66f1467 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -1936,8 +1936,10 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch) possible. */ static std::optional -amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache) +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache, + bool &shadow_stack_enabled) { + shadow_stack_enabled = false; const i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (tdep == nullptr || tdep->ssp_regnum < 0) @@ -1955,6 +1957,9 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache) if (ssp == 0x0) return {}; + /* In case there is a shadow stack pointer available which is non-null, + the shadow stack feature is enabled. */ + shadow_stack_enabled = true; return ssp; } @@ -1965,8 +1970,13 @@ static void amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache) { + bool shadow_stack_enabled; std::optional ssp - = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache); + = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache, + shadow_stack_enabled); + + /* For amd64/Linux, if SSP has a value that means shadow stack is + enabled. */ if (!ssp.has_value ()) return; @@ -2122,6 +2132,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, (gdbarch, amd64_linux_remove_non_address_bits_watchpoint); set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push); + set_gdbarch_get_shadow_stack_pointer (gdbarch, + amd64_linux_get_shadow_stack_pointer); dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); } diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index 450dbc38047..8afb3a7abba 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -1917,6 +1917,21 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch, displaced_debug_printf ("relocated return addr at %s to %s", paddress (gdbarch, rsp), paddress (gdbarch, retaddr)); + + /* If shadow stack is enabled, we need to correct the return address + on the shadow stack too. */ + bool shadow_stack_enabled; + std::optional ssp + = gdbarch_get_shadow_stack_pointer (gdbarch, regs, + shadow_stack_enabled); + if (ssp.has_value () && shadow_stack_enabled) + { + write_memory_unsigned_integer (*ssp, retaddr_len, byte_order, + retaddr); + displaced_debug_printf ("relocated shadow stack return addr at %s " + "to %s", paddress (gdbarch, *ssp), + paddress (gdbarch, retaddr)); + } } } diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index b5120b78426..488816d5ca2 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -27059,12 +27059,20 @@ the program stream must be an @code{ENDBR} instruction, otherwise the processor signals a control protection exception. @end itemize -Impact on Call/Print: +Impact on GDB commands: +@itemize @bullet +@item Call/Print: Inferior calls in @value{GDBN} reset the current PC to the beginning of the function that is called. No call instruction is executed, but the @code{RET} instruction actually is. To avoid a control protection exception due to the missing return address on the shadow stack, @value{GDBN} pushes the new return address to the shadow stack and updates the shadow stack pointer. +@item Step: +With displaced stepping, @value{GDBN} may run an out of line copy of a call +instruction. In this case, the wrong return address is pushed to the shadow +stack. @value{GDBN} corrects this value to avoid a control protection +exception. For more details on displaced stepping, see @ref{displaced-stepping}. +@end itemize @node Alpha @subsection Alpha @@ -41741,6 +41749,7 @@ GLOBAL Disassembler_2 (Matches current architecture) @cindex out-of-line single-stepping @item set displaced-stepping @itemx show displaced-stepping +@anchor{displaced-stepping} Control whether or not @value{GDBN} will do @dfn{displaced stepping} if the target supports it. Displaced stepping is a way to single-step over breakpoints without removing them from the inferior, by executing diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index 8eb5b4fac86..3b05ace2142 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -899,6 +899,21 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch, displaced_debug_printf ("relocated return addr at %s to %s", paddress (gdbarch, esp), paddress (gdbarch, retaddr)); + + /* If shadow stack is enabled, we need to correct the return address + on the shadow stack too. */ + bool shadow_stack_enabled; + std::optional ssp + = gdbarch_get_shadow_stack_pointer (gdbarch, regs, + shadow_stack_enabled); + if (ssp.has_value () && shadow_stack_enabled) + { + write_memory_unsigned_integer (*ssp, retaddr_len, byte_order, + retaddr); + displaced_debug_printf ("relocated shadow stack return addr at %s " + "to %s", paddress (gdbarch, *ssp), + paddress (gdbarch, retaddr)); + } } } diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp new file mode 100644 index 00000000000..47bb4df8cfe --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp @@ -0,0 +1,92 @@ +# Copyright 2025 Free Software Foundation, Inc. + +# 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 . + +# Test continue from call instructions with shadow stack and displaced +# stepping being enabled. + +require allow_ssp_tests support_displaced_stepping + +standard_testfile amd64-shadow-stack.c + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + additional_flags="-fcf-protection=return"] } { + return -1 + } + + # Enable displaced stepping. + gdb_test_no_output "set displaced-stepping on" + gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*" + + if { ![runto_main] } { + return -1 + } + + # Get the address of the call1 instruction. + set call1_addr -1 + gdb_test_multiple "disassemble main" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*.*" { + set call1_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call1_addr == -1 } { + return -1 + } + + # Get the address of the call2 instruction. + set call2_addr -1 + gdb_test_multiple "disassemble call1" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*.*" { + set call2_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call2_addr == -1 } { + return -1 + } + + gdb_test "break *$call1_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call1 instruction" + + gdb_test "break *$call2_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call2 instruction" + + # Depending on instruction generation we might end up in the call + # instruction after "runto_main". Only resume until call1 instruction + # in case the first instruction we're stopped at is not yet the call1 + # instruction. + set stop_addr [get_valueof "/x" "\$pc" "" "value of pc after runto_main"] + if {[eval expr "$stop_addr < $call1_addr"]} { + gdb_test "continue" \ + "Breakpoint $decimal, $call1_addr in main ().*" \ + "continue until call1 instruction" + } + gdb_assert {$call1_addr == [get_valueof "/x" "\$pc" ""]} + + # Test continue from breakpoint at call1 and call2 instructions. + gdb_test "continue" \ + "Breakpoint $decimal, $call2_addr in call1 ().*" \ + "continue from call1 instruction" + + gdb_continue_to_end "continue from call2 instruction" +} -- 2.43.0