From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id kbSDI4qC0mimLA8AWB0awg (envelope-from ) for ; Tue, 23 Sep 2025 07:20:42 -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=MLsI8IX7; dkim-atps=neutral Received: by simark.ca (Postfix, from userid 112) id 8D6A21E0BA; Tue, 23 Sep 2025 07:20:42 -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 747521E047 for ; Tue, 23 Sep 2025 07:20:39 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 249C93858D3C for ; Tue, 23 Sep 2025 11:20:37 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 249C93858D3C 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=MLsI8IX7 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.17]) by sourceware.org (Postfix) with ESMTPS id 1E19F3858C62 for ; Tue, 23 Sep 2025 11:19:05 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1E19F3858C62 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 1E19F3858C62 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=192.198.163.17 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626345; cv=none; b=sura0dwuSLTG7vEK4+5JO9FV2G1czpp4oCH4t6icc6JYSDorwaQUOOvZ6BhpZWoYkBhGWp8dCOplkZw+9m1JnUg+uw4rxsxdmTi2J6G4R5Z8NNrfnQhqmLnlEjPtUYhVmCqSwRFVs5JIaAqVEVEGAzfCD8SDFpXC4jGSzfHxSJY= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1758626345; c=relaxed/simple; bh=lwb7ElWyR6ktaFyVb91e+NLa/bto6k95d9qs74r3UOE=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=PrMuQH+5idtAr0+gkZRfMuS8on4xw8ix1N1VXn/IrxbGU1fFtq0S0+n6QxfQckjEIKGNR1YdUiS9GFYlg4vpju8kuHMeDaQ1nCRarfTE8cSE436pe9DNmt6VuvS30YZy/rOGceHJIbFPBIRS7t5hxlmsU3lhKJGSD8k7x0Vvwls= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 1E19F3858C62 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1758626345; x=1790162345; h=from:to:subject:date:message-id:in-reply-to:references: mime-version:content-transfer-encoding; bh=lwb7ElWyR6ktaFyVb91e+NLa/bto6k95d9qs74r3UOE=; b=MLsI8IX7P3cY9uB9eWkxjBxha+XSPZc6s3cayKanD4t/7TFKS5CdFay4 yTte1hhw8YH70J6NCg+BEUWtRy9FvkG+Si/uVNxSQcUoeX5e6WIARRcqQ gZnTKs16iqD+MvxlfyI8HptMxOTUtpSxlSmIZOcdFiH/XEzQwHWEdApgl v8IoY8yFUa9igsIgscAg5uRVjVE62iq6MDj5Yb5OvwKxdx0tjKG2YSqDl Y4hzrhZoibRq62V0DCvQ1cebb9QUdCY6nneHkREp1Bpm+rJ2CnhinzRaT NVgrQoHeC1lNbHlkv/9wwhz36z9Zdl5ESQnvlQ/TGsCxjybAqOOmtwzqj g==; X-CSE-ConnectionGUID: Vo0DV492QYm0E2VyBgj9Pg== X-CSE-MsgGUID: e+XhgrP6RJ+kb3/Q3DwY5A== X-IronPort-AV: E=McAfee;i="6800,10657,11561"; a="60827507" X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="60827507" Received: from fmviesa008.fm.intel.com ([10.60.135.148]) by fmvoesa111.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:05 -0700 X-CSE-ConnectionGUID: w5vDb83HQGSyTkKs3EDYhQ== X-CSE-MsgGUID: xsTn/n2CSSOAPF6Ka1E2jQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.18,287,1751266800"; d="scan'208";a="177118630" Received: from gkldtt-dev-004.igk.intel.com (HELO localhost) ([10.123.221.202]) by fmviesa008-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 23 Sep 2025 04:19:04 -0700 From: Christina Schimpe To: gdb-patches@sourceware.org Subject: [PATCH 1/9] gdb: Generalize handling of the shadow stack pointer. Date: Tue, 23 Sep 2025 11:18:34 +0000 Message-Id: <20250923111842.4091694-2-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 Until now, handling of the shadow stack pointer has been done in the target dependent implementations of the gdbarch hook 'gdbarch_shadow_stack_push'. Also amd64 and aarch64 linux specific unwinders for the shadow stack pointer are implemented. In a following commit a subcommmand "backtrace shadow" of the ordinary backtrace command will be added to print the shadow stack backtrace. This requires more target-independent logic to handle the shadow stack pointer. To avoid that we duplicate the logic, add new source and header files "shadow-stack" for the implementation of shadow_stack_push and shadow stack pointer unwinding in a target-independent way. --- gdb/Makefile.in | 2 + gdb/aarch64-linux-tdep.c | 51 +----------- gdb/aarch64-tdep.c | 42 ++-------- gdb/amd64-linux-tdep.c | 129 ----------------------------- gdb/amd64-tdep.c | 20 +++++ gdb/gdbarch-gen.c | 126 ++++++++++++++++++++++------ gdb/gdbarch-gen.h | 65 ++++++++++++--- gdb/gdbarch_components.py | 83 ++++++++++++++++--- gdb/infcall.c | 4 +- gdb/linux-tdep.c | 2 + gdb/shadow-stack.c | 167 ++++++++++++++++++++++++++++++++++++++ gdb/shadow-stack.h | 39 +++++++++ 12 files changed, 462 insertions(+), 268 deletions(-) create mode 100644 gdb/shadow-stack.c create mode 100644 gdb/shadow-stack.h diff --git a/gdb/Makefile.in b/gdb/Makefile.in index beacefcfdd5..9cb782fe88c 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1191,6 +1191,7 @@ COMMON_SFILES = \ sentinel-frame.c \ ser-event.c \ serial.c \ + shadow-stack.c \ skip.c \ solib.c \ solib-target.c \ @@ -1653,6 +1654,7 @@ HFILES_NO_SRCDIR = \ serial.h \ ser-tcp.h \ ser-unix.h \ + shadow-stack.h \ sh-tdep.h \ sim-regno.h \ skip.h \ diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 048be4f3532..2928b723e0b 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -64,6 +64,7 @@ #include "elf/common.h" #include "elf/aarch64.h" #include "arch/aarch64-insn.h" +#include "shadow-stack.h" /* For std::pow */ #include @@ -2562,54 +2563,6 @@ aarch64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache, return gcspr; } -/* Implement Guarded Control Stack Pointer Register unwinding. For each - previous GCS pointer check if its address is still in the GCS memory - range. If it's outside the range set the returned value to unavailable, - otherwise return a value containing the new GCS pointer. */ - -static value * -aarch64_linux_dwarf2_prev_gcspr (const frame_info_ptr &this_frame, - void **this_cache, int regnum) -{ - value *v = frame_unwind_got_register (this_frame, regnum, regnum); - gdb_assert (v != nullptr); - - gdbarch *gdbarch = get_frame_arch (this_frame); - - if (v->entirely_available () && !v->optimized_out ()) - { - int size = register_size (gdbarch, regnum); - bfd_endian byte_order = gdbarch_byte_order (gdbarch); - CORE_ADDR gcspr = extract_unsigned_integer (v->contents_all ().data (), - size, byte_order); - - /* Starting with v6.13, the Linux kernel supports Guarded Control - Stack. Using /proc/PID/smaps we can only check if the current - GCSPR points to GCS memory. Only if this is the case a valid - previous GCS pointer can be calculated. */ - std::pair range; - if (linux_address_in_shadow_stack_mem_range (gcspr, &range)) - { - /* The GCS grows downwards. To compute the previous GCS pointer, - we need to increment the GCSPR. */ - CORE_ADDR new_gcspr = gcspr + 8; - - /* If NEW_GCSPR still points within the current GCS memory range - we consider it to be valid. */ - if (new_gcspr < range.second) - return frame_unwind_got_address (this_frame, regnum, new_gcspr); - } - } - - /* Return a value which is marked as unavailable in case we could not - calculate a valid previous GCS pointer. */ - value *retval - = value::allocate_register (get_next_frame_sentinel_okay (this_frame), - regnum, register_type (gdbarch, regnum)); - retval->mark_bytes_unavailable (0, retval->type ()->length ()); - return retval; -} - /* AArch64 Linux implementation of the report_signal_info gdbarch hook. Displays information about possible memory tag violations. */ @@ -3188,7 +3141,7 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { set_gdbarch_get_shadow_stack_pointer (gdbarch, aarch64_linux_get_shadow_stack_pointer); - tdep->fn_prev_gcspr = aarch64_linux_dwarf2_prev_gcspr; + tdep->fn_prev_gcspr = dwarf2_prev_ssp; } } diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 500ac77d75a..95af82c2632 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -58,6 +58,9 @@ /* For inferior_ptid and current_inferior (). */ #include "inferior.h" + +#include "shadow-stack.h" + /* For std::sqrt and std::pow. */ #include @@ -1893,29 +1896,6 @@ pass_in_v_vfp_candidate (struct gdbarch *gdbarch, struct regcache *regcache, } } -/* Push LR_VALUE to the Guarded Control Stack. */ - -static void -aarch64_push_gcs_entry (regcache *regs, CORE_ADDR lr_value) -{ - gdbarch *arch = regs->arch (); - aarch64_gdbarch_tdep *tdep = gdbarch_tdep (arch); - CORE_ADDR gcs_addr; - - register_status status = regs->cooked_read (tdep->gcs_reg_base, &gcs_addr); - if (status != REG_VALID) - error (_("Can't read $gcspr.")); - - gcs_addr -= 8; - gdb_byte buf[8]; - store_integer (buf, gdbarch_byte_order (arch), lr_value); - if (target_write_memory (gcs_addr, buf, sizeof (buf)) != 0) - error (_("Can't write to Guarded Control Stack.")); - - /* Update GCSPR. */ - regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr); -} - /* Remove the newest entry from the Guarded Control Stack. */ static void @@ -1933,15 +1913,6 @@ aarch64_pop_gcs_entry (regcache *regs) regcache_cooked_write_unsigned (regs, tdep->gcs_reg_base, gcs_addr + 8); } -/* Implement the "shadow_stack_push" gdbarch method. */ - -static void -aarch64_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr, - regcache *regcache) -{ - aarch64_push_gcs_entry (regcache, new_addr); -} - /* Implement the "push_dummy_call" gdbarch method. */ static CORE_ADDR @@ -3683,7 +3654,7 @@ aarch64_displaced_step_b (const int is_bl, const int32_t offset, gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs, gcs_is_enabled); if (gcs_is_enabled) - aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4); + shadow_stack_push (dsd->regs->arch (), dsd->regs, data->insn_addr + 4); } } @@ -3847,7 +3818,7 @@ aarch64_displaced_step_others (const uint32_t insn, gdbarch_get_shadow_stack_pointer (dsd->regs->arch (), dsd->regs, gcs_is_enabled); if (gcs_is_enabled) - aarch64_push_gcs_entry (dsd->regs, data->insn_addr + 4); + shadow_stack_push (dsd->regs->arch (), dsd->regs, data->insn_addr + 4); } else aarch64_emit_insn (dsd->insn_buf, insn); @@ -4871,9 +4842,6 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_get_pc_address_flags (gdbarch, aarch64_get_pc_address_flags); - if (tdep->has_gcs ()) - set_gdbarch_shadow_stack_push (gdbarch, aarch64_shadow_stack_push); - tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data)); /* Fetch the updated number of registers after we're done adding all diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index a21f8a9a5ce..f0db3b7a1b4 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -48,8 +48,6 @@ #include "arch/amd64-linux-tdesc.h" #include "inferior.h" #include "x86-tdep.h" -#include "dwarf2/frame.h" -#include "frame-unwind.h" /* The syscall's XML filename for i386. */ #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml" @@ -1923,18 +1921,6 @@ amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid, return dtv_addr; } -/* Return the number of bytes required to update the shadow stack pointer - by one element. For x32 the shadow stack elements are still 64-bit - aligned. Thus, gdbarch_addr_bit cannot be used to compute the new - stack pointer. */ - -static inline int -amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch) -{ - const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch); - return (binfo->bits_per_word / binfo->bits_per_byte); -} - /* Read the shadow stack pointer register and return its value, if possible. */ @@ -1966,119 +1952,6 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache, return ssp; } -/* If shadow stack is enabled, push the address NEW_ADDR to 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) -{ - bool shadow_stack_enabled; - std::optional ssp - = 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; - else - gdb_assert (shadow_stack_enabled); - - /* The shadow stack grows downwards. To push addresses to 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; - - /* 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. Always write back the full 8 bytes to - include the shadow stack token. */ - 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); - gdb_assert (tdep->ssp_regnum > -1); - - 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. - If it's outside the range set the returned value to unavailable, - otherwise return a value containing the new shadow stack pointer. */ - -static value * -amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame, - void **this_cache, int regnum) -{ - value *v = frame_unwind_got_register (this_frame, regnum, regnum); - gdb_assert (v != nullptr); - - gdbarch *gdbarch = get_frame_arch (this_frame); - - if (v->entirely_available () && !v->optimized_out ()) - { - int size = register_size (gdbarch, regnum); - bfd_endian byte_order = gdbarch_byte_order (gdbarch); - CORE_ADDR ssp = extract_unsigned_integer (v->contents_all ().data (), - size, byte_order); - - /* Using /proc/PID/smaps we can only check if the current shadow - stack pointer SSP points to shadow stack memory. Only if this is - the case a valid previous shadow stack pointer can be - calculated. */ - std::pair range; - if (linux_address_in_shadow_stack_mem_range (ssp, &range)) - { - /* The shadow stack grows downwards. To compute the previous - shadow stack pointer, we need to increment SSP. */ - CORE_ADDR new_ssp - = ssp + amd64_linux_shadow_stack_element_size_aligned (gdbarch); - - /* There can be scenarios where we have a shadow stack pointer - but the shadow stack is empty, as no call instruction has - been executed yet. If NEW_SSP points to the end of or before - (<=) the current shadow stack memory range we consider - NEW_SSP as valid (but empty). */ - if (new_ssp <= range.second) - return frame_unwind_got_address (this_frame, regnum, new_ssp); - } - } - - /* Return a value which is marked as unavailable in case we could not - calculate a valid previous shadow stack pointer. */ - value *retval - = value::allocate_register (get_next_frame_sentinel_okay (this_frame), - regnum, register_type (gdbarch, regnum)); - retval->mark_bytes_unavailable (0, retval->type ()->length ()); - return retval; -} - -/* Implement the "init_reg" dwarf2_frame_ops method. */ - -static void -amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg, - const frame_info_ptr &this_frame) -{ - if (regnum == gdbarch_pc_regnum (gdbarch)) - reg->how = DWARF2_FRAME_REG_RA; - else if (regnum == gdbarch_sp_regnum (gdbarch)) - reg->how = DWARF2_FRAME_REG_CFA; - else if (regnum == AMD64_PL3_SSP_REGNUM) - { - reg->how = DWARF2_FRAME_REG_FN; - reg->loc.fn = amd64_linux_dwarf2_prev_ssp; - } -} - static void amd64_linux_init_abi_common (struct gdbarch_info info, struct gdbarch *gdbarch, int num_disp_step_buffers) @@ -2137,10 +2010,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); set_gdbarch_get_shadow_stack_pointer (gdbarch, amd64_linux_get_shadow_stack_pointer); - dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); } static void diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index 9245889210b..5406275e199 100755 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -51,6 +51,8 @@ #include "x86-tdep.h" #include "amd64-ravenscar-thread.h" #include "gdbsupport/selftest.h" +#include "shadow-stack.h" +#include "dwarf2/frame.h" /* Note that the AMD64 architecture was previously known as x86-64. The latter is (forever) engraved into the canonical system name as @@ -3493,6 +3495,21 @@ amd64_in_indirect_branch_thunk (struct gdbarch *gdbarch, CORE_ADDR pc) AMD64_RIP_REGNUM); } +static void +amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg, + const frame_info_ptr &this_frame) +{ + if (regnum == gdbarch_pc_regnum (gdbarch)) + reg->how = DWARF2_FRAME_REG_RA; + else if (regnum == gdbarch_sp_regnum (gdbarch)) + reg->how = DWARF2_FRAME_REG_CFA; + else if (regnum == AMD64_PL3_SSP_REGNUM) + { + reg->how = DWARF2_FRAME_REG_FN; + reg->loc.fn = dwarf2_prev_ssp; + } +} + void amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, const target_desc *default_tdesc) @@ -3660,6 +3677,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_in_indirect_branch_thunk (gdbarch, amd64_in_indirect_branch_thunk); + set_gdbarch_ssp_regnum (gdbarch, tdep->ssp_regnum); + dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg); + register_amd64_ravenscar_ops (gdbarch); } diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index 7887b1386c6..640576f4b85 100644 --- a/gdb/gdbarch-gen.c +++ b/gdb/gdbarch-gen.c @@ -85,6 +85,7 @@ struct gdbarch int pc_regnum = -1; int ps_regnum = -1; int fp0_regnum = -1; + int ssp_regnum = -1; gdbarch_stab_reg_to_regnum_ftype *stab_reg_to_regnum = no_op_reg_to_regnum; gdbarch_ecoff_reg_to_regnum_ftype *ecoff_reg_to_regnum = no_op_reg_to_regnum; gdbarch_sdb_reg_to_regnum_ftype *sdb_reg_to_regnum = no_op_reg_to_regnum; @@ -261,8 +262,10 @@ struct gdbarch gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings; gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes; gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context; - gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr; gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer; + 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; }; /* Create a new ``struct gdbarch'' based on information provided by @@ -348,6 +351,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of pc_regnum, invalid_p == 0. */ /* Skip verify of ps_regnum, invalid_p == 0. */ /* Skip verify of fp0_regnum, invalid_p == 0. */ + /* Skip verify of ssp_regnum, invalid_p == 0. */ /* Skip verify of stab_reg_to_regnum, invalid_p == 0. */ /* Skip verify of ecoff_reg_to_regnum, invalid_p == 0. */ /* Skip verify of sdb_reg_to_regnum, invalid_p == 0. */ @@ -535,8 +539,10 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of read_core_file_mappings, invalid_p == 0. */ /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0. */ /* Skip verify of core_parse_exec_context, invalid_p == 0. */ - /* Skip verify of shadow_stack_push, has predicate. */ /* Skip verify of get_shadow_stack_pointer, invalid_p == 0. */ + /* 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. */ if (!log.empty ()) internal_error (_("verify_gdbarch: the following are invalid ...%s"), log.c_str ()); @@ -709,6 +715,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: fp0_regnum = %s\n", plongest (gdbarch->fp0_regnum)); + gdb_printf (file, + "gdbarch_dump: ssp_regnum = %s\n", + plongest (gdbarch->ssp_regnum)); gdb_printf (file, "gdbarch_dump: stab_reg_to_regnum = <%s>\n", host_address_to_string (gdbarch->stab_reg_to_regnum)); @@ -1405,15 +1414,24 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) gdb_printf (file, "gdbarch_dump: core_parse_exec_context = <%s>\n", host_address_to_string (gdbarch->core_parse_exec_context)); - gdb_printf (file, - "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n", - gdbarch_shadow_stack_push_p (gdbarch)); - gdb_printf (file, - "gdbarch_dump: shadow_stack_push = <%s>\n", - host_address_to_string (gdbarch->shadow_stack_push)); gdb_printf (file, "gdbarch_dump: get_shadow_stack_pointer = <%s>\n", host_address_to_string (gdbarch->get_shadow_stack_pointer)); + gdb_printf (file, + "gdbarch_dump: gdbarch_top_addr_empty_shadow_stack_p() = %d\n", + gdbarch_top_addr_empty_shadow_stack_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: top_addr_empty_shadow_stack = <%s>\n", + host_address_to_string (gdbarch->top_addr_empty_shadow_stack)); + gdb_printf (file, + "gdbarch_dump: gdbarch_address_in_shadow_stack_memory_range_p() = %d\n", + gdbarch_address_in_shadow_stack_memory_range_p (gdbarch)); + gdb_printf (file, + "gdbarch_dump: address_in_shadow_stack_memory_range = <%s>\n", + host_address_to_string (gdbarch->address_in_shadow_stack_memory_range)); + gdb_printf (file, + "gdbarch_dump: shadow_stack_element_size_aligned = %s\n", + plongest (gdbarch->shadow_stack_element_size_aligned)); if (gdbarch->dump_tdep != NULL) gdbarch->dump_tdep (gdbarch, file); } @@ -2161,6 +2179,23 @@ set_gdbarch_fp0_regnum (struct gdbarch *gdbarch, gdbarch->fp0_regnum = fp0_regnum; } +int +gdbarch_ssp_regnum (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + /* Skip verify of ssp_regnum, invalid_p == 0. */ + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_ssp_regnum called\n"); + return gdbarch->ssp_regnum; +} + +void +set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, + int ssp_regnum) +{ + gdbarch->ssp_regnum = ssp_regnum; +} + int gdbarch_stab_reg_to_regnum (struct gdbarch *gdbarch, int stab_regnr) { @@ -5543,43 +5578,84 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch->core_parse_exec_context = core_parse_exec_context; } +std::optional +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->get_shadow_stack_pointer != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n"); + return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled); +} + +void +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, + gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer) +{ + gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; +} + bool -gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch) +gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - return gdbarch->shadow_stack_push != NULL; + return gdbarch->top_addr_empty_shadow_stack != NULL; +} + +bool +gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair range) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->top_addr_empty_shadow_stack != NULL); + if (gdbarch_debug >= 2) + gdb_printf (gdb_stdlog, "gdbarch_top_addr_empty_shadow_stack called\n"); + return gdbarch->top_addr_empty_shadow_stack (gdbarch, ADDR, range); } void -gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache) +set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, + gdbarch_top_addr_empty_shadow_stack_ftype top_addr_empty_shadow_stack) +{ + gdbarch->top_addr_empty_shadow_stack = top_addr_empty_shadow_stack; +} + +bool +gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->shadow_stack_push != NULL); + return gdbarch->address_in_shadow_stack_memory_range != NULL; +} + +bool +gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ssp, std::pair *range) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->address_in_shadow_stack_memory_range != NULL); if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n"); - gdbarch->shadow_stack_push (gdbarch, new_addr, regcache); + gdb_printf (gdb_stdlog, "gdbarch_address_in_shadow_stack_memory_range called\n"); + return gdbarch->address_in_shadow_stack_memory_range (ssp, range); } void -set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, - gdbarch_shadow_stack_push_ftype shadow_stack_push) +set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, + gdbarch_address_in_shadow_stack_memory_range_ftype address_in_shadow_stack_memory_range) { - gdbarch->shadow_stack_push = shadow_stack_push; + gdbarch->address_in_shadow_stack_memory_range = address_in_shadow_stack_memory_range; } -std::optional -gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled) +int +gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->get_shadow_stack_pointer != NULL); + /* Skip verify of shadow_stack_element_size_aligned, invalid_p == 0. */ if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n"); - return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled); + gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_element_size_aligned called\n"); + return gdbarch->shadow_stack_element_size_aligned; } void -set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, - gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer) +set_gdbarch_shadow_stack_element_size_aligned (struct gdbarch *gdbarch, + int shadow_stack_element_size_aligned) { - gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; + gdbarch->shadow_stack_element_size_aligned = shadow_stack_element_size_aligned; } diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index 24fa8bab2db..34a32c346e8 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -283,6 +283,12 @@ extern void set_gdbarch_ps_regnum (struct gdbarch *gdbarch, int ps_regnum); extern int gdbarch_fp0_regnum (struct gdbarch *gdbarch); extern void set_gdbarch_fp0_regnum (struct gdbarch *gdbarch, int fp0_regnum); +/* Register number for the shadow stack pointer. For inferior calls, the + gdbarch value ssp_regnum has to be provided. */ + +extern int gdbarch_ssp_regnum (struct gdbarch *gdbarch); +extern void set_gdbarch_ssp_regnum (struct gdbarch *gdbarch, int ssp_regnum); + /* Convert stab register number (from `r' declaration) to a gdb REGNUM. */ typedef int (gdbarch_stab_reg_to_regnum_ftype) (struct gdbarch *gdbarch, int stab_regnr); @@ -1793,19 +1799,21 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc /* Some targets support special hardware-assisted control-flow protection technologies. For example, the Intel Control-Flow Enforcement Technology (Intel CET) on x86 provides a shadow stack and indirect branch tracking. - To enable shadow stack support for inferior calls the shadow_stack_push - gdbarch hook has to be provided. The get_shadow_stack_pointer gdbarch - hook has to be provided to enable displaced stepping. - - Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */ - -extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch); - -typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache); -extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache); -extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push); - -/* If possible, return the shadow stack pointer. If the shadow stack + For GDB shadow stack support the following methods or values must be + provided: + - get_shadow_stack_pointer: required for displaced stepping + - address_in_shadow_stack_memory_range and top_addr_empty_shadow_stack: + required for shadow stack pointer unwinding + - address_in_shadow_stack_memory_range and the value ssp_regnum: required + for inferior function calls. + + If the shadow stack alignment is not the predefault of 8 bytes, configure + the gdbarch value shadow_stack_element_size_aligned. + In case the shadow stack can be empty even though the shadow stack pointer + points to shadow stack memory range, implement the gdbarch hook + top_addr_empty_shadow_stack. + + If possible, return the shadow stack pointer. If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED to false. This hook has to be provided to enable displaced stepping for shadow stack enabled programs. @@ -1817,3 +1825,34 @@ extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shad typedef std::optional (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled); extern std::optional gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled); extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer); + +/* Return true if ADDR points to the top of an empty shadow stack, defined by + RANGE [start_address, end_address). This hook has to be provided to enable + inferior calls for shadow stack enabled programs and to enable unwinding + of the shadow stack pointer. */ + +extern bool gdbarch_top_addr_empty_shadow_stack_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_top_addr_empty_shadow_stack_ftype) (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair range); +extern bool gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, const CORE_ADDR ADDR, const std::pair range); +extern void set_gdbarch_top_addr_empty_shadow_stack (struct gdbarch *gdbarch, gdbarch_top_addr_empty_shadow_stack_ftype *top_addr_empty_shadow_stack); + +/* Returns true if ADDR belongs to a shadow stack memory range. If this is + the case, assign the shadow stack memory range to RANGE + [start_address, end_address). This hook has to be provided in case the + shadow stack can be empty even though SSP points to shadow stack memory + defined by RANGE [start_address, end_address). This is possible for the + Guarded Control Stack on ARM. */ + +extern bool gdbarch_address_in_shadow_stack_memory_range_p (struct gdbarch *gdbarch); + +typedef bool (gdbarch_address_in_shadow_stack_memory_range_ftype) (CORE_ADDR ssp, std::pair *range); +extern bool gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, CORE_ADDR ssp, std::pair *range); +extern void set_gdbarch_address_in_shadow_stack_memory_range (struct gdbarch *gdbarch, gdbarch_address_in_shadow_stack_memory_range_ftype *address_in_shadow_stack_memory_range); + +/* The number of bytes required to update the shadow stack pointer by one + element. In case the alignment is not the predefault (8 bytes), configure + this value. */ + +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); diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py index 3820ae3c94c..5223553941f 100644 --- a/gdb/gdbarch_components.py +++ b/gdb/gdbarch_components.py @@ -544,6 +544,17 @@ Value( invalid=False, ) +Value( + comment=""" +Register number for the shadow stack pointer. For inferior calls, the +gdbarch value ssp_regnum has to be provided. +""", + type="int", + name="ssp_regnum", + predefault="-1", + invalid=False, +) + Method( comment=""" Convert stab register number (from `r' declaration) to a gdb REGNUM. @@ -2852,20 +2863,20 @@ Method( Some targets support special hardware-assisted control-flow protection technologies. For example, the Intel Control-Flow Enforcement Technology (Intel CET) on x86 provides a shadow stack and indirect branch tracking. -To enable shadow stack support for inferior calls the shadow_stack_push -gdbarch hook has to be provided. The get_shadow_stack_pointer gdbarch -hook has to be provided to enable displaced stepping. - -Push NEW_ADDR to the shadow stack and update the shadow stack pointer. -""", - type="void", - name="shadow_stack_push", - params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")], - predicate=True, -) +For GDB shadow stack support the following methods or values must be +provided: +- get_shadow_stack_pointer: required for displaced stepping +- address_in_shadow_stack_memory_range and top_addr_empty_shadow_stack: + required for shadow stack pointer unwinding +- address_in_shadow_stack_memory_range and the value ssp_regnum: required + for inferior function calls. + +If the shadow stack alignment is not the predefault of 8 bytes, configure +the gdbarch value shadow_stack_element_size_aligned. +In case the shadow stack can be empty even though the shadow stack pointer +points to shadow stack memory range, implement the gdbarch hook +top_addr_empty_shadow_stack. -Method( - comment=""" If possible, return the shadow stack pointer. If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED to false. This hook has to be provided to enable @@ -2881,3 +2892,49 @@ SHADOW_STACK_ENABLED to false. predefault="default_get_shadow_stack_pointer", invalid=False, ) + +Method( + comment=""" +Return true if ADDR points to the top of an empty shadow stack, defined by +RANGE [start_address, end_address). This hook has to be provided to enable +inferior calls for shadow stack enabled programs and to enable unwinding +of the shadow stack pointer. +""", + type="bool", + name="top_addr_empty_shadow_stack", + params=[ + ("const CORE_ADDR", "ADDR"), + ("const std::pair", "range") + ], + predicate=True, +) + +Function( + comment=""" +Returns true if ADDR belongs to a shadow stack memory range. If this is +the case, assign the shadow stack memory range to RANGE +[start_address, end_address). This hook has to be provided in case the +shadow stack can be empty even though SSP points to shadow stack memory +defined by RANGE [start_address, end_address). This is possible for the +Guarded Control Stack on ARM. +""", + type="bool", + name="address_in_shadow_stack_memory_range", + params=[ + ("CORE_ADDR", "ssp"), + ("std::pair *", "range") + ], + predicate=True, +) + +Value( + comment=""" +The number of bytes required to update the shadow stack pointer by one +element. In case the alignment is not the predefault (8 bytes), configure +this value. +""", + type="int", + name="shadow_stack_element_size_aligned", + predefault="8", + invalid=False, +) diff --git a/gdb/infcall.c b/gdb/infcall.c index c4b4c8f0bea..46f9fd9342e 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -42,6 +42,7 @@ #include "thread-fsm.h" #include #include "gdbsupport/scope-exit.h" +#include "shadow-stack.h" #include /* True if we are debugging inferior calls. */ @@ -1462,8 +1463,7 @@ call_function_by_hand_dummy (struct value *function, /* Push the return address of the inferior (bp_addr) to the shadow stack and update the shadow stack pointer. As we don't execute a call instruction to call the function we need to handle this manually. */ - if (gdbarch_shadow_stack_push_p (gdbarch)) - gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache); + shadow_stack_push (gdbarch, regcache, bp_addr); /* Set up a frame ID for the dummy frame so we can pass it to set_momentary_breakpoint. We need to give the breakpoint a frame diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 4b679c8759e..1c1ebd5a99f 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -3118,6 +3118,8 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch, set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); set_gdbarch_core_parse_exec_context (gdbarch, linux_corefile_parse_exec_context); + set_gdbarch_address_in_shadow_stack_memory_range + (gdbarch, linux_address_in_shadow_stack_mem_range); } INIT_GDB_FILE (linux_tdep) diff --git a/gdb/shadow-stack.c b/gdb/shadow-stack.c new file mode 100644 index 00000000000..d153d5fc846 --- /dev/null +++ b/gdb/shadow-stack.c @@ -0,0 +1,167 @@ +/* Manage a shadow stack pointer for GDB, the GNU debugger. + + Copyright (C) 2024-2025 Free Software Foundation, Inc. + This file is part of GDB. + + 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 "defs.h" +#include "arch-utils.h" +#include "gdbcore.h" +#include "extract-store-integer.h" +#include "frame.h" +#include "frame-unwind.h" +#include "shadow-stack.h" + +enum class ssp_update_direction +{ + /* Update ssp towards the bottom of the shadow stack. */ + bottom = 0, + + /* Update ssp towards the top of the shadow stack. */ + top +}; + +/* Return a new shadow stack pointer which is incremented or decremented + by COUNT elements dependent on DIRECTION. */ + +static CORE_ADDR +update_shadow_stack_pointer (gdbarch *gdbarch, CORE_ADDR ssp, + const ssp_update_direction direction) +{ + bool increment = (gdbarch_stack_grows_down (gdbarch)) + ? (direction == ssp_update_direction::bottom) + : (direction == ssp_update_direction::top); + + CORE_ADDR new_ssp; + if (increment) + return ssp + gdbarch_shadow_stack_element_size_aligned (gdbarch); + else + return ssp - gdbarch_shadow_stack_element_size_aligned (gdbarch); +} + +/* See shadow-stack.h. */ + +void shadow_stack_push (gdbarch *gdbarch, regcache *regcache, + const CORE_ADDR new_addr) +{ + if (!gdbarch_address_in_shadow_stack_memory_range_p (gdbarch) + || gdbarch_ssp_regnum (gdbarch) == -1) + return; + + bool shadow_stack_enabled; + std::optional ssp + = gdbarch_get_shadow_stack_pointer (gdbarch, regcache, + shadow_stack_enabled); + if (!ssp.has_value () || !shadow_stack_enabled) + return; + + const CORE_ADDR new_ssp + = update_shadow_stack_pointer (gdbarch, *ssp, + ssp_update_direction::top); + + /* If NEW_SSP does not point to shadow stack memory, we assume the stack + is full. */ + std::pair range; + if (!gdbarch_address_in_shadow_stack_memory_range (gdbarch, + new_ssp, + &range)) + 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. Always write back the full 8 bytes to + include the shadow stack token. */ + const int element_size + = gdbarch_shadow_stack_element_size_aligned (gdbarch); + + const bfd_endian byte_order = gdbarch_byte_order (gdbarch); + + write_memory_unsigned_integer (new_ssp, element_size, byte_order, + (ULONGEST) new_addr); + + regcache_raw_write_unsigned (regcache, + gdbarch_ssp_regnum (gdbarch), + new_ssp); +} + +/* See shadow-stack.h. */ + +value * +dwarf2_prev_ssp (const frame_info_ptr &this_frame, void **this_cache, + int regnum) +{ + value *v = frame_unwind_got_register (this_frame, regnum, regnum); + gdb_assert (v != nullptr); + + gdbarch *gdbarch = get_frame_arch (this_frame); + + if (gdbarch_address_in_shadow_stack_memory_range_p (gdbarch) + && v->entirely_available () && !v->optimized_out ()) + { + const int size = register_size (gdbarch, regnum); + bfd_endian byte_order = gdbarch_byte_order (gdbarch); + CORE_ADDR ssp = extract_unsigned_integer + (v->contents_all ().data (), size, byte_order); + + /* Only if the current shadow stack pointer SSP points to shadow + stack memory a valid previous shadow stack pointer can be + calculated. */ + std::pair range; + if (gdbarch_address_in_shadow_stack_memory_range (gdbarch, ssp, + &range)) + { + /* Note that a shadow stack memory range can change, due to + shadow stack switches for instance on x86 for an inter- + privilege far call or when calling an interrupt/exception + handler at a higher privilege level. Shadow stack for + userspace is supported for amd64 linux starting with + Linux kernel v6.6. However, shadow stack switches are not + supported due to missing kernel space support. We therefore + implement this unwinder without support for shadow stack + switches for now. */ + const CORE_ADDR new_ssp + = update_shadow_stack_pointer (gdbarch, ssp, + ssp_update_direction::bottom); + + /* On x86, if NEW_SSP points to the end of RANGE, it indicates + that NEW_SSP is valid, but the shadow stack is empty. In + contrast, on ARM's Guarded Control Stack, if NEW_SSP points + to the end of RANGE, it means that the shadow stack pointer + is invalid. */ + bool is_top_addr_empty_shadow_stack + = gdbarch_top_addr_empty_shadow_stack_p (gdbarch) + && gdbarch_top_addr_empty_shadow_stack (gdbarch, new_ssp, range); + + /* Check whether the new SSP is valid. Depending on the + architecture, this may rely on both + IS_TOP_ADDR_EMPTY_SHADOW_STACK and the return value of + gdbarch_address_in_shadow_stack_memory_range, or on the + latter only. */ + if (is_top_addr_empty_shadow_stack + || gdbarch_address_in_shadow_stack_memory_range (gdbarch, + ssp, + &range)) + return frame_unwind_got_address (this_frame, regnum, new_ssp); + } + } + + /* Return a value which is marked as unavailable, in case we could not + calculate a valid previous shadow stack pointer. */ + value *retval + = value::allocate_register (get_next_frame_sentinel_okay (this_frame), + regnum, register_type (gdbarch, regnum)); + retval->mark_bytes_unavailable (0, retval->type ()->length ()); + return retval; + +} diff --git a/gdb/shadow-stack.h b/gdb/shadow-stack.h new file mode 100644 index 00000000000..5c3ba80974e --- /dev/null +++ b/gdb/shadow-stack.h @@ -0,0 +1,39 @@ +/* Definitions to manage a shadow stack pointer for GDB, the GNU debugger. + + Copyright (C) 2024-2025 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 . */ + +#ifndef GDB_SHADOW_STACK_H +#define GDB_SHADOW_STACK_H + +/* If shadow stack is enabled, push the address NEW_ADDR on the shadow + stack and update the shadow stack pointer accordingly. */ + +void shadow_stack_push (gdbarch *gdbarch, regcache *regcache, + const CORE_ADDR new_addr); + +/* Unwind the previous shadow stack pointer of THIS_FRAME's shadow stack + pointer. REGNUM is the register number of the shadow stack pointer. + Return a value that is unavailable in case we cannot unwind the + previous shadow stack pointer. Otherwise, return a value containing + the previous shadow stack pointer. */ + +value * dwarf2_prev_ssp (const frame_info_ptr &this_frame, + void **this_cache, int regnum); + +#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