From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id 4CXqN+qoX2hkXSMAWB0awg (envelope-from ) for ; Sat, 28 Jun 2025 04:33:46 -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=SBa4bpIH; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id DD3241E11E; Sat, 28 Jun 2025 04:33:46 -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 0C3421E089 for ; Sat, 28 Jun 2025 04:33:46 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id B0D9F385C40E for ; Sat, 28 Jun 2025 08:33:45 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B0D9F385C40E 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=SBa4bpIH Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.14]) by sourceware.org (Postfix) with ESMTPS id B98F738560A1 for ; Sat, 28 Jun 2025 08:29:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org B98F738560A1 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 B98F738560A1 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=192.198.163.14 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751099343; cv=none; b=ThuCnZ5OLGVG13Nfr++DAV6c54ZZyiaVdfCOQ8YkpvI675OyH9sXZJMONJv2sC9NonHqt/yMmNnkUV/iQiEvTdduWtuU97IJ065Q5zXW/OMYBZ5Lv7UxmP81rzUVg09djn8LF83Uh3UffHdkLKdw4c7mNC0HKoheyWjQsWS8eqQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1751099343; c=relaxed/simple; bh=9YXp/uzAmQ6GQY8DxitXEw7yVEcTMacs5IiNP9N3Log=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=S9EOL3L7h1k20QOp+jtrDOQf3BmdKcdUO/GM2xo0FlN7GtuLTV6avwJQ6rcAnGwXKTNuKjH4Lll9ivIJt96Z3t6Xr1mNGvJnIo2CxbjkpDAg1447X90MH1UaEbdwQ2XyvBdgjafMh1Usr9VOsW72DkQ6J0XMrkx1HbBzONJco3g= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org B98F738560A1 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1751099343; x=1782635343; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=9YXp/uzAmQ6GQY8DxitXEw7yVEcTMacs5IiNP9N3Log=; b=SBa4bpIH/lVI2WS+2hrQHnIQLuYpja5YOYk6MWAFFaWLrVb+V6ZmA54+ XZmVCdZOisF838eGgYd73x9to3bOE3SDiskpyORUyJPQ51418ttuIEmHv tq9rZzAar8FGcRsT+57xU+QcQnEsw0fizUIT+mGbmElhcr3+MlYb8hBuL 50kb6iSDFIvjZWi9z0CMWxyS4MxpHtr/BKTaoSy2rtD4sXEf4wpIZRvkp liJsAld1wQ4bjiWXjGrhe9/n7lmViqw6AVa9y5xL0CGeaymRWzyynfIed vvDpTKY4XAacRjCmQyfIcLEEd3FdZg9j/0REa8CA3qpPNN8q0cyGueg3X w==; X-CSE-ConnectionGUID: PCKbuRGzR3S26hbf4e9MjQ== X-CSE-MsgGUID: NoI96bPJTdiSJgNs6ZL9lg== X-IronPort-AV: E=McAfee;i="6800,10657,11477"; a="53491488" X-IronPort-AV: E=Sophos;i="6.16,272,1744095600"; d="scan'208";a="53491488" Received: from orviesa009.jf.intel.com ([10.64.159.149]) by fmvoesa108.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jun 2025 01:29:02 -0700 X-CSE-ConnectionGUID: e8wS1v3sR8qToYHz9/8YOg== X-CSE-MsgGUID: jZuAqpd+Q1aykxueTorweQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.16,272,1744095600"; d="scan'208";a="152734145" Received: from 0007e934a912.jf.intel.com (HELO localhost) ([10.165.58.208]) by orviesa009-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 28 Jun 2025 01:29:02 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Cc: thiago.bauermann@linaro.org, luis.machado@arm.com Subject: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack. Date: Sat, 28 Jun 2025 01:28:05 -0700 Message-ID: <20250628082810.332526-8-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 Intel's Control-Flow Enforcement Technology (CET) provides the shadow stack feature for the x86 architecture. This commit adds support to write and read the shadow-stack node in corefiles. This helps debugging return address violations post-mortem. The format is synced with the linux kernel commit "x86: Add PTRACE interface for shadow stack". As the linux kernel restricts shadow stack support to 64-bit, apply the fix for amd64 only. Co-Authored-By: Christina Schimpe Reviewed-by: Thiago Jung Bauermann --- gdb/amd64-linux-tdep.c | 57 ++++++++- .../gdb.arch/amd64-shadow-stack-corefile.c | 42 +++++++ .../gdb.arch/amd64-shadow-stack-corefile.exp | 110 ++++++++++++++++++ 3 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index edb7d8da6ab..9af7a41ea26 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -47,6 +47,7 @@ #include "expop.h" #include "arch/amd64-linux-tdesc.h" #include "inferior.h" +#include "x86-tdep.h" /* The syscall's XML filename for i386. */ #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml" @@ -1593,6 +1594,14 @@ amd64_linux_record_signal (struct gdbarch *gdbarch, return 0; } +/* Get shadow stack pointer state from core dump. */ + +static bool +amd64_linux_core_read_ssp_state_p (bfd *abfd) +{ + return bfd_get_section_by_name (abfd, ".reg-ssp") != nullptr; +} + /* Get Linux/x86 target description from core dump. */ static const struct target_desc * @@ -1602,11 +1611,14 @@ amd64_linux_core_read_description (struct gdbarch *gdbarch, { /* Linux/x86-64. */ x86_xsave_layout layout; - uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout); - if (xcr0 == 0) - xcr0 = X86_XSTATE_SSE_MASK; + uint64_t xstate_bv_mask = i386_linux_core_read_xsave_info (abfd, layout); + if (xstate_bv_mask == 0) + xstate_bv_mask = X86_XSTATE_SSE_MASK; + + if (amd64_linux_core_read_ssp_state_p (abfd)) + xstate_bv_mask |= X86_XSTATE_CET_U; - return amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK, + return amd64_linux_read_description (xstate_bv_mask & X86_XSTATE_ALL_MASK, gdbarch_ptr_bit (gdbarch) == 32); } @@ -1637,6 +1649,35 @@ static const struct regset amd64_linux_xstateregset = amd64_linux_collect_xstateregset }; +/* Supply shadow stack pointer register from SSP to the register cache + REGCACHE. */ + +static void +amd64_linux_supply_ssp (const regset *regset, + regcache *regcache, int regnum, + const void *ssp, size_t len) +{ + x86_supply_ssp (regcache, *static_cast (ssp)); +} + +/* Collect the shadow stack pointer register from the register cache + REGCACHE and store it in SSP. */ + +static void +amd64_linux_collect_ssp (const regset *regset, + const regcache *regcache, int regnum, + void *ssp, size_t len) +{ + x86_collect_ssp (regcache, *static_cast (ssp)); +} + +/* Shadow stack pointer register. */ + +static const struct regset amd64_linux_ssp_register + { + NULL, amd64_linux_supply_ssp, amd64_linux_collect_ssp + }; + /* Iterate over core file register note sections. */ static void @@ -1653,6 +1694,14 @@ amd64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, cb (".reg-xstate", tdep->xsave_layout.sizeof_xsave, tdep->xsave_layout.sizeof_xsave, &amd64_linux_xstateregset, "XSAVE extended state", cb_data); + + /* SSP can be unavailable. Thus, we need to check the register status + in case we write a core file (regcache != nullptr). */ + if (tdep->ssp_regnum > 0 + && (regcache == nullptr + || REG_VALID == regcache->get_register_status (tdep->ssp_regnum))) + cb (".reg-ssp", 8, 8, &amd64_linux_ssp_register, + "shadow stack pointer", cb_data); } /* The instruction sequences used in x86_64 machines for a diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c new file mode 100644 index 00000000000..f078e33810d --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c @@ -0,0 +1,42 @@ +/* This test program is part of GDB, the GNU debugger. + + 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 . */ + +#include + +/* Call the return instruction before function epilogue to trigger a + control-flow exception. */ +void +function () +{ + unsigned long ssp; + asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp)); + + /* Print ssp to stdout so that the testcase can capture it. */ + printf ("%p\n", (void *) ssp); + fflush (stdout); + + /* Manually cause a control-flow exception by executing a return + instruction before function epilogue, so the address atop the stack + is not the return instruction. */ + __asm__ volatile ("ret\n"); +} + +int +main (void) +{ + function (); /* Break here. */ +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp new file mode 100644 index 00000000000..8784fc3622c --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp @@ -0,0 +1,110 @@ +# Copyright 2021-2024 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 the shadow stack pointer note in core dumps. + +require allow_ssp_tests + +standard_testfile + +proc check_core_file {core_filename saved_pl3_ssp} { + global decimal + + # Load the core file. + if [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault.*" \ + "#0 function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + "$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "load core file"] { + return + } + + # Check the value of ssp in the core file. + gdb_test "print/x \$pl3_ssp" "\\$\[0-9\]+ = $saved_pl3_ssp" \ + "pl3_ssp contents from core file $saved_pl3_ssp" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + {debug additional_flags="-fcf-protection=return"}] } { + return + } + + set linespec ${srcfile}:[gdb_get_line_number "Break here"] + + if ![runto $linespec] { + return + } + + # Continue until a crash. The line with the hex number is optional because + # it's printed by the test program, and doesn't appear in the Expect buffer + # when testing a remote target. + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "($hex\r\n)?" \ + "Program received signal SIGSEGV, Segmentation fault.*" \ + "function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + {.*__asm__ volatile \("ret\\n"\);}] \ + "continue to SIGSEGV" + + set ssp_in_gcore [get_valueof "/x" "\$pl3_ssp" "*unknown*"] + + # Generate the gcore core file. + set gcore_filename [standard_output_file "${testfile}.gcore"] + set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"] + + # Obtain an OS-generated core file. Save test program output to + # ${binfile}.out. + set core_filename [core_find $binfile {} {} "${binfile}.out"] + set core_generated [expr {$core_filename != ""}] + set os_core_name "${binfile}.core" + remote_exec build "mv $core_filename $os_core_name" + set core_filename $os_core_name + + # At this point we have a couple of core files, the gcore one generated by + # GDB and the one generated by the operating system. Make sure GDB can + # read both correctly. + + if {$gcore_generated} { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $ssp_in_gcore + } + } else { + fail "gcore corefile not generated" + } + + if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + # Read ssp value from saved output of the test program. + set out_id [open ${binfile}.out "r"] + set ssp_in_gcore [gets $out_id] + + close $out_id + check_core_file $core_filename $ssp_in_gcore + } + } else { + untested "OS corefile not generated" + } +} -- 2.43.0