From patchwork Thu Oct 31 13:23:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yury Khrustalev X-Patchwork-Id: 2004664 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) 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 (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XfPtg5dd4z1xwF for ; Fri, 1 Nov 2024 00:27:39 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 44BAF385772A for ; Thu, 31 Oct 2024 13:27:37 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by sourceware.org (Postfix) with ESMTP id D3756385734F; Thu, 31 Oct 2024 13:25:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D3756385734F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=arm.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D3756385734F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730381134; cv=none; b=tPebhvWKUzGpo7pH8ir70IDTCaWaH7G689YTWz0GhcsPinc/82LA59NLYZhYKtiyneacmnoYU0OqTzfJsx5anmPmUbBa2igGu2KuddWsDH9U018H/drJ+f03f0p1yIQW9Wlfcgxz9g1EpCe47gJjcW6luhUm4bXdZC4VX5eOSyI= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1730381134; c=relaxed/simple; bh=sSmHfseW0hxNU1nXHojKguTe/pnvn2lvS3n6SHsRtCo=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=kNn6QUxCmLwfjBLeBRnbpi7x/TI7OVfz7ibeSLihWHGlv3V1jG+7AQxJ+izjA7NCXe4FrYeo9I89BCQ+cee2vGME/9VhdEy2Z2HYZOGA2dAfpixAau+vsn0065y9Fwyb5J3hApFqOyBVCzri3vLOeR13+j7QTnbUnEdX+1MSTkY= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 6883B113E; Thu, 31 Oct 2024 06:26:00 -0700 (PDT) Received: from udebian.localdomain (unknown [10.1.31.34]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D71583F528; Thu, 31 Oct 2024 06:25:29 -0700 (PDT) From: Yury Khrustalev To: gcc-patches@gcc.gnu.org Cc: nsz@gcc.gnu.org, richard.earnshaw@arm.com, matthieu.longo@arm.com, richard.ball@arm.com, richard.sandiford@arm.com Subject: [PATCH v2 14/21] aarch64: Add GCS support to the unwinder Date: Thu, 31 Oct 2024 13:23:16 +0000 Message-Id: <20241031132323.948159-15-yury.khrustalev@arm.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241031132323.948159-1-yury.khrustalev@arm.com> References: <20241031132323.948159-1-yury.khrustalev@arm.com> MIME-Version: 1.0 X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org From: Szabolcs Nagy Follows the current linux ABI that uses single signal entry token and shared shadow stack between thread and alt stack. Could be behind __ARM_FEATURE_GCS_DEFAULT ifdef (only do anything special with gcs compat codegen) but there is a runtime check anyway. Change affected tests to be compatible with -mbranch-protection=standard gcc/testsuite/ChangeLog: * g++.target/aarch64/pr94515-1.C (f1_no_pac_ret): Update. (main): Update. * gcc.target/aarch64/pr104689.c (unwind): Update. libgcc/ChangeLog: * config/aarch64/aarch64-unwind.h (_Unwind_Frames_Extra): Update. (_Unwind_Frames_Increment): Define. Co-authored-by: Matthieu Longo --- gcc/testsuite/g++.target/aarch64/pr94515-1.C | 6 +- gcc/testsuite/gcc.target/aarch64/pr104689.c | 3 +- libgcc/config/aarch64/aarch64-unwind.h | 59 +++++++++++++++++++- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/gcc/testsuite/g++.target/aarch64/pr94515-1.C b/gcc/testsuite/g++.target/aarch64/pr94515-1.C index 359039e1753..8175ea50c32 100644 --- a/gcc/testsuite/g++.target/aarch64/pr94515-1.C +++ b/gcc/testsuite/g++.target/aarch64/pr94515-1.C @@ -5,7 +5,7 @@ volatile int zero = 0; -__attribute__((noinline, target("branch-protection=none"))) +__attribute__((noinline, target("branch-protection=bti"))) void unwind (void) { if (zero == 0) @@ -22,7 +22,7 @@ int test (int z) // autiasp -> cfi_negate_ra_state: RA_signing_SP -> RA_no_signing return 1; } else { - // 2nd cfi_negate_ra_state because the CFI directives are processed linearily. + // 2nd cfi_negate_ra_state because the CFI directives are processed linearly. // At this point, the unwinder would believe that the address is not signed // due to the previous return. That's why the compiler has to emit second // cfi_negate_ra_state to mean that the return address is still signed. @@ -33,7 +33,7 @@ int test (int z) } } -__attribute__((target("branch-protection=none"))) +__attribute__((target("branch-protection=bti"))) int main () { try { diff --git a/gcc/testsuite/gcc.target/aarch64/pr104689.c b/gcc/testsuite/gcc.target/aarch64/pr104689.c index 3b7adbdfe7d..9688ecc85f9 100644 --- a/gcc/testsuite/gcc.target/aarch64/pr104689.c +++ b/gcc/testsuite/gcc.target/aarch64/pr104689.c @@ -98,6 +98,7 @@ asm("" "unusual_no_pac_ret:\n" " .cfi_startproc\n" " " SET_RA_STATE_0 "\n" +" bti c\n" " stp x29, x30, [sp, -16]!\n" " .cfi_def_cfa_offset 16\n" " .cfi_offset 29, -16\n" @@ -121,7 +122,7 @@ static void f2_pac_ret (void) die (); } -__attribute__((target("branch-protection=none"))) +__attribute__((target("branch-protection=bti"))) static void f1_no_pac_ret (void) { unusual_pac_ret (f2_pac_ret); diff --git a/libgcc/config/aarch64/aarch64-unwind.h b/libgcc/config/aarch64/aarch64-unwind.h index 4d36f0b26f7..cf4ec749c05 100644 --- a/libgcc/config/aarch64/aarch64-unwind.h +++ b/libgcc/config/aarch64/aarch64-unwind.h @@ -178,6 +178,9 @@ aarch64_demangle_return_addr (struct _Unwind_Context *context, return addr; } +/* GCS enable flag for chkfeat instruction. */ +#define CHKFEAT_GCS 1 + /* SME runtime function local to libgcc, streaming compatible and preserves more registers than the base PCS requires, but we don't rely on that here. */ @@ -185,12 +188,66 @@ __attribute__ ((visibility ("hidden"))) void __libgcc_arm_za_disable (void); /* Disable the SME ZA state in case an unwound frame used the ZA - lazy saving scheme. */ + lazy saving scheme. And unwind the GCS for EH. */ #undef _Unwind_Frames_Extra #define _Unwind_Frames_Extra(x) \ do \ { \ __libgcc_arm_za_disable (); \ + if (__builtin_aarch64_chkfeat (CHKFEAT_GCS) == 0) \ + { \ + for (_Unwind_Word n = (x); n != 0; n--) \ + __builtin_aarch64_gcspopm (); \ + } \ + } \ + while (0) + +/* On signal entry the OS places a token on the GCS that can be used to + verify the integrity of the GCS pointer on signal return. It also + places the signal handler return address (the restorer that calls the + signal return syscall) on the GCS so the handler can return. + Because of this token, each stack frame visited during unwinding has + exactly one corresponding entry on the GCS, so the frame count is + the number of entries that will have to be popped at EH return time. + + Note: This depends on the GCS signal ABI of the OS. + + When unwinding across a stack frame for each frame the corresponding + entry is checked on the GCS against the computed return address from + the normal stack. If they don't match then _URC_FATAL_PHASE2_ERROR + is returned. This check is omitted if + + 1. GCS is disabled. Note: asynchronous GCS disable is supported here + if GCSPR and the GCS remains readable. + 2. Non-catchable exception where exception_class == 0. Note: the + pthread cancellation implementation in glibc sets exception_class + to 0 when the unwinder is used for cancellation cleanup handling, + so this allows the GCS to get out of sync during cancellation. + This weakens security but avoids an ABI break in glibc. + 3. Zero return address which marks the outermost stack frame. + 4. Signal stack frame, the GCS entry is an OS specific token then + with the top bit set. + */ +#undef _Unwind_Frames_Increment +#define _Unwind_Frames_Increment(exc, context, frames) \ + do \ + { \ + frames++; \ + if (__builtin_aarch64_chkfeat (CHKFEAT_GCS) != 0 \ + || exc->exception_class == 0 \ + || _Unwind_GetIP (context) == 0) \ + break; \ + const _Unwind_Word *gcs = __builtin_aarch64_gcspr (); \ + if (_Unwind_IsSignalFrame (context)) \ + { \ + if (gcs[frames] >> 63 == 0) \ + return _URC_FATAL_PHASE2_ERROR; \ + } \ + else \ + { \ + if (gcs[frames] != _Unwind_GetIP (context)) \ + return _URC_FATAL_PHASE2_ERROR; \ + } \ } \ while (0)