From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id yNjSCaZdUWhd8hEAWB0awg (envelope-from ) for ; Tue, 17 Jun 2025 08:20:54 -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=bRAE7Mqt; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 246CF1E11C; Tue, 17 Jun 2025 08:20:54 -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 701B41E089 for ; Tue, 17 Jun 2025 08:20:53 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1FFEB3A4AF76 for ; Tue, 17 Jun 2025 12:20:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1FFEB3A4AF76 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=bRAE7Mqt Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.19]) by sourceware.org (Postfix) with ESMTPS id 9119D3844BA1 for ; Tue, 17 Jun 2025 12:13:29 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9119D3844BA1 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 9119D3844BA1 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=198.175.65.19 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1750162409; cv=none; b=lnoMgNqV2IXwfq/+Jo0E8HkCFNhFRhBWr5/EX1DdYo8f3BfRqZtbnfSQPHJfMm4+zTLRfEOAa1S6x77jVtSg/ztW30GOjWcXkYqVLqya5wFvRAJ2POQnTrp5nz+L7VW1mHlTZLhL9BPpflihVOfiaBWmxuhuvL/AVN2lnCDhBic= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1750162409; c=relaxed/simple; bh=WZnUHV+BenemFW69RzlUKC2sEeahM5JEmG0L/gwJXic=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=HaBsIcUTiOcZ8gZrU3gKgj6aZD4qDLo+zxd+The/xQ1KKCBS8eSPnMH8LYPxsYIARzHH3C59hVzLv0ahDXhodKpEorQO+606s3f0GF1xdo22stuVjH7GkqywBvu/I5tTo55EH6M9GgBb0/zFhvax/Oom6xQy76jsofZ00olTz5E= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 9119D3844BA1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1750162410; x=1781698410; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=WZnUHV+BenemFW69RzlUKC2sEeahM5JEmG0L/gwJXic=; b=bRAE7MqtJqteHQzeuLhfO9aRr/x7w70yC5VL81qTnZlW8vM8lDJzIZbc zjWCom04QihBB3wUen1zpY9gH1E9J8pNhm5hWhLMb2GviPFzvtDxWH14y 3wbPfXQWB/sBuTrVHdkZQrIbdnNfommCZ2KfxBhrKP3N88ZuGRPnam7R3 OlhDnIcFZxbeF5j0bwtnJ7yVDV2HUZnpwLBU7KFExEFln/iwOb2OJNcgK E2Q8Sm67gjTJYohxlou8e53kNylSYHRY454OKKnP8RUiIYRFdWv03h+JK 4ICIoVr+O8PhsFaKA5V2oJg44l0aRBqvY7ZDfmy0hYRPlsq+C5GKUkTo8 A==; X-CSE-ConnectionGUID: e5HkLJwiSNuNWH5/r0b2HA== X-CSE-MsgGUID: FYRHgU0OSaiKJ0UhOEAgyg== X-IronPort-AV: E=McAfee;i="6800,10657,11467"; a="52206075" X-IronPort-AV: E=Sophos;i="6.16,243,1744095600"; d="scan'208";a="52206075" Received: from orviesa003.jf.intel.com ([10.64.159.143]) by orvoesa111.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Jun 2025 05:13:29 -0700 X-CSE-ConnectionGUID: VjewBr4OSUq3Rcr9FhzXNA== X-CSE-MsgGUID: VAO7N9sAQO6R2UP/BMzTKQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.16,243,1744095600"; d="scan'208";a="153527506" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by ORVIESA003-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Jun 2025 05:13:28 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org, eliz@gnu.org Subject: [PATCH v4 09/11] gdb: Implement amd64 linux shadow stack support for inferior calls. Date: Tue, 17 Jun 2025 12:11:45 +0000 Message-Id: <20250617121147.1956686-10-christina.schimpe@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20250617121147.1956686-1-christina.schimpe@intel.com> References: <20250617121147.1956686-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 This patch enables inferior calls to support Intel's Control-Flow Enforcement Technology (CET), which provides the shadow stack feature for the x86 architecture. Following the restriction of the linux kernel, enable inferior calls for amd64 only. Reviewed-by: Thiago Jung Bauermann Reviewed-By: Eli Zaretskii --- gdb/amd64-linux-tdep.c | 64 +++++++++++++++++++ gdb/doc/gdb.texinfo | 29 +++++++++ .../gdb.arch/amd64-shadow-stack-cmds.exp | 55 +++++++++++++++- 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 9436f0b190c..d847248659a 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -1931,6 +1931,68 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch) return (binfo->bits_per_word / binfo->bits_per_byte); } +/* Read the shadow stack pointer register and return its value, if + possible. */ + +static std::optional +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache) +{ + const i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + if (tdep == nullptr || tdep->ssp_regnum < 0) + return {}; + + CORE_ADDR ssp; + if (regcache_raw_read_unsigned (regcache, tdep->ssp_regnum, &ssp) + != REG_VALID) + return {}; + + /* Starting with v6.6., the Linux kernel supports CET shadow stack. + Dependent on the target the ssp register can be invalid or nullptr + when shadow stack is supported by HW and the linux kernel but not + enabled for the current thread. */ + if (ssp == 0x0) + return {}; + + return ssp; +} + +/* If shadow stack is enabled, push the address NEW_ADDR on the shadow + stack and increment the shadow stack pointer accordingly. */ + +static void +amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, + regcache *regcache) +{ + std::optional ssp + = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache); + if (!ssp.has_value ()) + return; + + /* The shadow stack grows downwards. To push addresses on the stack, + we need to decrement SSP. */ + const int element_size + = amd64_linux_shadow_stack_element_size_aligned (gdbarch); + const CORE_ADDR new_ssp = *ssp - element_size; + + /* Starting with v6.6., the Linux kernel supports CET shadow stack. + Using /proc/PID/smaps we can only check if NEW_SSP points to shadow + stack memory. If it doesn't, we assume the stack is full. */ + std::pair memrange; + if (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange)) + error (_("No space left on the shadow stack.")); + + /* On x86 there can be a shadow stack token at bit 63. For x32, the + address size is only 32 bit. Thus, we must use ELEMENT_SIZE (and + not gdbarch_addr_bit) to determine the width of the address to be + written. */ + const bfd_endian byte_order = gdbarch_byte_order (gdbarch); + write_memory_unsigned_integer (new_ssp, element_size, byte_order, + (ULONGEST) new_addr); + + i386_gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp); +} /* Implement shadow stack pointer unwinding. For each new shadow stack pointer check if its address is still in the shadow stack memory range. @@ -2057,6 +2119,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_remove_non_address_bits_watchpoint (gdbarch, amd64_linux_remove_non_address_bits_watchpoint); + + set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push); dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); } diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 0ae09f09c88..cf152bd1e6f 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -27033,6 +27033,35 @@ registers @end itemize +@subsubsection Intel Control-Flow Enforcement Technology. +@cindex Intel Control-Flow Enforcement Technology. + +The @dfn{Intel Control-Flow Enforcement Technology} (@acronym{Intel CET}) +provides two capabilities to defend against ``Return-oriented Programming'' +and ``call/jmp-oriented programming'' style control-flow attacks: + +@itemize @bullet +@item Shadow Stack: +A shadow stack is a second stack for a program. It holds the return +addresses pushed by the call instruction. The @code{RET} instruction pops the +return addresses from both call and shadow stack. If the return addresses from +the two stacks do not match, the processor signals a control protection +exception. +@item Indirect Branch Tracking (IBT): +When IBT is enabled, the CPU implements a state machine that tracks indirect +@code{JMP} and @code{CALL} instructions. The state machine can be either IDLE +or WAIT_FOR_ENDBRANCH. In WAIT_FOR_ENDBRANCH state the next instruction in +the program stream must be an @code{ENDBR} instruction, otherwise the +processor signals a control protection exception. +@end itemize + +Impact on 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. + @node Alpha @subsection Alpha diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp index 17f32ce3964..96f83678f39 100644 --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp @@ -13,12 +13,29 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Test shadow stack enabling for frame level update and the return command. +# Test shadow stack enabling for frame level update, the return and the +# call command. +# As potential CET violations often only occur after resuming normal +# execution, test normal program continuation after each return or call +# commands. require allow_ssp_tests standard_testfile amd64-shadow-stack.c +proc restart_and_run_infcall_call2 {} { + global binfile + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + set inside_infcall_str "The program being debugged stopped while in a function called from GDB" + gdb_breakpoint [ gdb_get_line_number "break call2" ] + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + gdb_test "call (int) call2()" \ + "Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*" +} + save_vars { ::env(GLIBC_TUNABLES) } { append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" @@ -33,6 +50,42 @@ save_vars { ::env(GLIBC_TUNABLES) } { return -1 } + with_test_prefix "test inferior call and continue" { + gdb_breakpoint [ gdb_get_line_number "break call1" ] + gdb_continue_to_breakpoint "break call1" ".*break call1.*" + + gdb_test "call (int) call2()" "= 42" + + gdb_continue_to_end + } + + with_test_prefix "test return inside an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "return" "\#0.*call2.*" \ + "Test shadow stack return inside an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + with_test_prefix "test return 'above' an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call" + + gdb_test "return" "\#0.*call1.*" \ + "Test shadow stack return 'above' an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + set call1_line [ gdb_get_line_number "break call1" ] set call2_line [ gdb_get_line_number "break call2" ] -- 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