From patchwork Tue Mar 30 19:09:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Madhavan T. Venkataraman" X-Patchwork-Id: 12173589 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-16.8 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 73AB8C433E0 for ; Tue, 30 Mar 2021 19:12:28 +0000 (UTC) Received: from desiato.infradead.org (desiato.infradead.org [90.155.92.199]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id D814B61998 for ; Tue, 30 Mar 2021 19:12:27 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org D814B61998 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=linux.microsoft.com Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=desiato.20200630; h=Sender:Content-Transfer-Encoding :Content-Type:List-Subscribe:List-Help:List-Post:List-Archive: List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:Message-Id:Date: Subject:To:From:Reply-To:Cc:Content-ID:Content-Description:Resent-Date: Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:List-Owner; bh=0BO6iJFBMPBaOqVmKVrjLlTv8/s/R1zVsa7on+CbyC0=; b=Kw9gBI4tbCAJHKT46S9Og+1y2 y2oFs1eFC3Ea/wNBgRwtCE3dp/FnkJRZugPFu7YSgz2BYmoIv7kuEAgxtGejtMOhcO9MkLkxU7zLh ssrTIQQqZi12T/f2nDv1255Lldy4ztEgA2m1b4f1TZ2jy/b3q7400XQFkS+GMk8umvWbOOQCVxP/Y C4F5WudcGuyEsV/EwFpauoIT0AbGqowyD9YLqvJE/FwXujbllC0bLFGjFe7FLNXnHwL6tZi1ehaA8 XnfjY3ke6OhNaQkYqYx1vC64RVvRVeKu3GmeOMdA0o3Aoi16pwSrW36LQRCJkCaLUfKjRGmoAkbst Wbg20jm/A==; Received: from localhost ([::1] helo=desiato.infradead.org) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lRJkn-004gbT-Vt; Tue, 30 Mar 2021 19:10:26 +0000 Received: from linux.microsoft.com ([13.77.154.182]) by desiato.infradead.org with esmtp (Exim 4.94 #2 (Red Hat Linux)) id 1lRJke-004gZx-4S for linux-arm-kernel@lists.infradead.org; Tue, 30 Mar 2021 19:10:12 +0000 Received: from x64host.home (unknown [47.187.194.202]) by linux.microsoft.com (Postfix) with ESMTPSA id 3ABA320B5681; Tue, 30 Mar 2021 12:10:04 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 3ABA320B5681 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1617131404; bh=8WqRBqOL5eacZZZ/2y2AI7eJekzqSItDIQKWe5DSzMo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=pR2pY9KKd8J3Bj0Xvj/kLNGgCmwf4ynx7Xk8uUqwRdn2ubbi8J9qCGcMq9VItPQcu PiooUS+0/EnjY76r5o1lNbfiTgIb90FsZYBBIQkHIGBa+NBSFcBTZ5NJ2/yHy/Hv6I BDmIqNEfwOAJKWk+JiAEjVmWvyo/ph5ZgdRyXmwg= From: madvenka@linux.microsoft.com To: mark.rutland@arm.com, broonie@kernel.org, jpoimboe@redhat.com, jthierry@redhat.com, catalin.marinas@arm.com, will@kernel.org, linux-arm-kernel@lists.infradead.org, live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, madvenka@linux.microsoft.com Subject: [RFC PATCH v1 2/4] arm64: Mark a stack trace unreliable if an EL1 exception frame is detected Date: Tue, 30 Mar 2021 14:09:53 -0500 Message-Id: <20210330190955.13707-3-madvenka@linux.microsoft.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210330190955.13707-1-madvenka@linux.microsoft.com> References: <77bd5edeea72d44533c769b1e8c0fea7a9d7eb3a> <20210330190955.13707-1-madvenka@linux.microsoft.com> MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20210330_201008_849431_9C82BE3E X-CRM114-Status: GOOD ( 17.48 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+linux-arm-kernel=archiver.kernel.org@lists.infradead.org From: "Madhavan T. Venkataraman" EL1 exceptions can happen on any instruction including instructions in the frame pointer prolog or epilog. Depending on where exactly they happen, they could render the stack trace unreliable. If an EL1 exception frame is found on the stack, mark the stack trace as unreliable. Now, the EL1 exception frame is not at any well-known offset on the stack. It can be anywhere on the stack. In order to properly detect an EL1 exception frame, the return address must be checked against all of the possible EL1 exception handlers. Preemption ========== Interrupts encountered in kernel code are also EL1 exceptions. At the end of an interrupt, the interrupt handler checks if the current task must be preempted for any reason. If so, it calls the preemption code which takes the task off the CPU. A stack trace taken on the task after the preemption will show the EL1 frame and will be considered unreliable. This is correct behavior as preemption can happen practically at any point in code. Probing ======= Breakpoints encountered in kernel code are also EL1 exceptions. The probing infrastructure uses breakpoints for executing probe code. While in the probe code, the stack trace will show an EL1 frame and will be considered unreliable. This is also correct behavior. Signed-off-by: Madhavan T. Venkataraman Reviewed-by: Mark Brown --- arch/arm64/include/asm/exception.h | 8 +++++++ arch/arm64/kernel/entry.S | 14 +++++------ arch/arm64/kernel/stacktrace.c | 37 ++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h index 6546158d2f2d..4ebd2390ef54 100644 --- a/arch/arm64/include/asm/exception.h +++ b/arch/arm64/include/asm/exception.h @@ -35,6 +35,14 @@ asmlinkage void el1_sync_handler(struct pt_regs *regs); asmlinkage void el0_sync_handler(struct pt_regs *regs); asmlinkage void el0_sync_compat_handler(struct pt_regs *regs); +asmlinkage void el1_sync(void); +asmlinkage void el1_irq(void); +asmlinkage void el1_error(void); +asmlinkage void el1_sync_invalid(void); +asmlinkage void el1_irq_invalid(void); +asmlinkage void el1_fiq_invalid(void); +asmlinkage void el1_error_invalid(void); + asmlinkage void noinstr enter_el1_irq_or_nmi(struct pt_regs *regs); asmlinkage void noinstr exit_el1_irq_or_nmi(struct pt_regs *regs); asmlinkage void enter_from_user_mode(void); diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index a31a0a713c85..9fe3aaeff019 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -630,19 +630,19 @@ SYM_CODE_START_LOCAL(el0_fiq_invalid_compat) SYM_CODE_END(el0_fiq_invalid_compat) #endif -SYM_CODE_START_LOCAL(el1_sync_invalid) +SYM_CODE_START(el1_sync_invalid) inv_entry 1, BAD_SYNC SYM_CODE_END(el1_sync_invalid) -SYM_CODE_START_LOCAL(el1_irq_invalid) +SYM_CODE_START(el1_irq_invalid) inv_entry 1, BAD_IRQ SYM_CODE_END(el1_irq_invalid) -SYM_CODE_START_LOCAL(el1_fiq_invalid) +SYM_CODE_START(el1_fiq_invalid) inv_entry 1, BAD_FIQ SYM_CODE_END(el1_fiq_invalid) -SYM_CODE_START_LOCAL(el1_error_invalid) +SYM_CODE_START(el1_error_invalid) inv_entry 1, BAD_ERROR SYM_CODE_END(el1_error_invalid) @@ -650,7 +650,7 @@ SYM_CODE_END(el1_error_invalid) * EL1 mode handlers. */ .align 6 -SYM_CODE_START_LOCAL_NOALIGN(el1_sync) +SYM_CODE_START_NOALIGN(el1_sync) kernel_entry 1 mov x0, sp bl el1_sync_handler @@ -658,7 +658,7 @@ SYM_CODE_START_LOCAL_NOALIGN(el1_sync) SYM_CODE_END(el1_sync) .align 6 -SYM_CODE_START_LOCAL_NOALIGN(el1_irq) +SYM_CODE_START_NOALIGN(el1_irq) kernel_entry 1 gic_prio_irq_setup pmr=x20, tmp=x1 enable_da_f @@ -737,7 +737,7 @@ el0_irq_naked: b ret_to_user SYM_CODE_END(el0_irq) -SYM_CODE_START_LOCAL(el1_error) +SYM_CODE_START(el1_error) kernel_entry 1 mrs x1, esr_el1 gic_prio_kentry_setup tmp=x2 diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index ff35b3953c39..7662f57d3e88 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -25,8 +26,44 @@ struct function_range { /* * Special functions where the stack trace is unreliable. + * + * EL1 exceptions + * ============== + * + * EL1 exceptions can happen on any instruction including instructions in + * the frame pointer prolog or epilog. Depending on where exactly they happen, + * they could render the stack trace unreliable. + * + * If an EL1 exception frame is found on the stack, mark the stack trace as + * unreliable. Now, the EL1 exception frame is not at any well-known offset + * on the stack. It can be anywhere on the stack. In order to properly detect + * an EL1 exception frame, the return address must be checked against all of + * the possible EL1 exception handlers. + * + * Interrupts encountered in kernel code are also EL1 exceptions. At the end + * of an interrupt, the current task can get preempted. A stack trace taken + * on the task after the preemption will show the EL1 frame and will be + * considered unreliable. This is correct behavior as preemption can happen + * practically at any point in code. + * + * Breakpoints encountered in kernel code are also EL1 exceptions. Breakpoints + * can happen practically on any instruction. Mark the stack trace as + * unreliable. Breakpoints are used for executing probe code. Stack traces + * taken while in the probe code will show an EL1 frame and will be considered + * unreliable. This is correct behavior. */ static struct function_range special_functions[] = { + /* + * EL1 exception handlers. + */ + { (unsigned long) el1_sync, 0 }, + { (unsigned long) el1_irq, 0 }, + { (unsigned long) el1_error, 0 }, + { (unsigned long) el1_sync_invalid, 0 }, + { (unsigned long) el1_irq_invalid, 0 }, + { (unsigned long) el1_fiq_invalid, 0 }, + { (unsigned long) el1_error_invalid, 0 }, + { 0, 0 } };