From patchwork Thu Aug 12 13:24:34 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: 12433579 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=-17.4 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 9AC54C4338F for ; Thu, 12 Aug 2021 13:27:40 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (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 5375D604D7 for ; Thu, 12 Aug 2021 13:27:40 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.1 mail.kernel.org 5375D604D7 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=lists.infradead.org DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; 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=2QLqFiOOkMjE8JRY9sRKJgflvkxPmoZ6Vdqds32oX1g=; b=XXNWZyxttWYPn+ pZNLbJDhjNZEjCZpaSCDSP37qahd0XR3ucByabzAFYREovm4TS3fSF5X2pkuXZDvF58+yVJMpHQD8 oCX2Z+Q7BFfgthLytjFMfUnv4F+swMtzlyr4z8KFquZmBbC9jj6RAzOCucuEcXLXFYTYg6SNPuzhL +zrqQzRld0F8rqdhsLzEt08576nWOZORD8QceUsr/zUDxzZFK3OdvCYY8m4d7HpvpQTLOCMKNIVWt YC85yREREOmQhmVoYfteiNGBS5Telg8e0hybMB4/a0SzyG2a1colcuEvPTDV+sjW9SsgX+983AD6K 57+sGoT84TkD9gEyAmBA==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mEAiX-00ACEr-60; Thu, 12 Aug 2021 13:25:53 +0000 Received: from linux.microsoft.com ([13.77.154.182]) by bombadil.infradead.org with esmtp (Exim 4.94.2 #2 (Red Hat Linux)) id 1mEAhe-00ABnc-HV for linux-arm-kernel@lists.infradead.org; Thu, 12 Aug 2021 13:25:02 +0000 Received: from x64host.home (unknown [47.187.212.181]) by linux.microsoft.com (Postfix) with ESMTPSA id 7077E20C155D; Thu, 12 Aug 2021 06:24:55 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 7077E20C155D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1628774696; bh=9UDTW5f/wu5lTfTc51GK4zB/v63pXMsEhlhyVfcdK1w=; h=From:To:Subject:Date:In-Reply-To:References:From; b=tB5A41fYkQJsB8wu6jnXzWo40wjbaOwEIMjwuPL/jJbWjNG2O6Nzwoo+6dkqaD0Zt qicWZMZQd7UUntiWiGewtf//26D2nGi34qd2f/P6SrRyQ6Fifcvn5K8267e62h5g76 4LkWxPGPLTfcMLVh6M6H7imNmmpYpoo83ZKEloVg= From: madvenka@linux.microsoft.com To: mark.rutland@arm.com, broonie@kernel.org, jpoimboe@redhat.com, ardb@kernel.org, nobuta.keiya@fujitsu.com, sjitindarsingh@gmail.com, catalin.marinas@arm.com, will@kernel.org, jmorris@namei.org, pasha.tatashin@soleen.com, jthierry@redhat.com, linux-arm-kernel@lists.infradead.org, live-patching@vger.kernel.org, linux-kernel@vger.kernel.org, madvenka@linux.microsoft.com Subject: [RFC PATCH v7 3/4] arm64: Introduce stack trace reliability checks in the unwinder Date: Thu, 12 Aug 2021 08:24:34 -0500 Message-Id: <20210812132435.6143-4-madvenka@linux.microsoft.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20210812132435.6143-1-madvenka@linux.microsoft.com> References: <3f2aab69a35c243c5e97f47c4ad84046355f5b90> <20210812132435.6143-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-20210812_062458_743342_7D09E23B X-CRM114-Status: GOOD ( 22.06 ) 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" There are some kernel features and conditions that make a stack trace unreliable. Callers may require the unwinder to detect these cases. E.g., livepatch. Introduce a new function called unwind_is_reliable() that will detect these cases and return a boolean. Introduce a new argument to unwind() called "need_reliable" so a caller can tell unwind() that it requires a reliable stack trace. For such a caller, any unreliability in the stack trace must be treated as a fatal error and the unwind must be aborted. Call unwind_is_reliable() from unwind_consume() like this: if (frame->need_reliable && !unwind_is_reliable(frame)) { frame->failed = true; return false; } In other words, if the return PC in the stackframe falls in unreliable code, then it cannot be unwound reliably. arch_stack_walk() will pass "false" for need_reliable because its callers don't care about reliability. arch_stack_walk() is used for debug and test purposes. Introduce arch_stack_walk_reliable() for ARM64. This works like arch_stack_walk() except for two things: - It passes "true" for need_reliable. - It returns -EINVAL if unwind() says that the stack trace is unreliable. Introduce the first reliability check in unwind_is_reliable() - If a return PC is not a valid kernel text address, consider the stack trace unreliable. It could be some generated code. Other reliability checks will be added in the future. Until all of the checks are in place, arch_stack_walk_reliable() may not be used by livepatch. But it may be used by debug and test code. Signed-off-by: Madhavan T. Venkataraman --- arch/arm64/include/asm/stacktrace.h | 4 ++ arch/arm64/kernel/stacktrace.c | 63 +++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h index 407007376e97..65ea151da5da 100644 --- a/arch/arm64/include/asm/stacktrace.h +++ b/arch/arm64/include/asm/stacktrace.h @@ -53,6 +53,9 @@ struct stack_info { * replacement lr value in the ftrace graph stack. * * @failed: Unwind failed. + * + * @need_reliable The caller needs a reliable stack trace. Treat any + * unreliability as a fatal error. */ struct stackframe { struct task_struct *task; @@ -65,6 +68,7 @@ struct stackframe { int graph; #endif bool failed; + bool need_reliable; }; extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index ec8f5163c4d0..b60f8a20ba64 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -34,7 +34,8 @@ static void notrace unwind_start(struct stackframe *frame, struct task_struct *task, - unsigned long fp, unsigned long pc) + unsigned long fp, unsigned long pc, + bool need_reliable) { frame->task = task; frame->fp = fp; @@ -56,6 +57,7 @@ static void notrace unwind_start(struct stackframe *frame, frame->prev_fp = 0; frame->prev_type = STACK_TYPE_UNKNOWN; frame->failed = false; + frame->need_reliable = need_reliable; } NOKPROBE_SYMBOL(unwind_start); @@ -178,6 +180,23 @@ void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) barrier(); } +/* + * Check the stack frame for conditions that make further unwinding unreliable. + */ +static bool notrace unwind_is_reliable(struct stackframe *frame) +{ + /* + * If the PC is not a known kernel text address, then we cannot + * be sure that a subsequent unwind will be reliable, as we + * don't know that the code follows our unwind requirements. + */ + if (!__kernel_text_address(frame->pc)) + return false; + return true; +} + +NOKPROBE_SYMBOL(unwind_is_reliable); + static bool notrace unwind_consume(struct stackframe *frame, stack_trace_consume_fn consume_entry, void *cookie) @@ -197,6 +216,12 @@ static bool notrace unwind_consume(struct stackframe *frame, /* Final frame; nothing to unwind */ return false; } + + if (frame->need_reliable && !unwind_is_reliable(frame)) { + /* Cannot unwind to the next frame reliably. */ + frame->failed = true; + return false; + } return true; } @@ -210,11 +235,12 @@ static inline bool unwind_failed(struct stackframe *frame) /* Core unwind function */ static bool notrace unwind(stack_trace_consume_fn consume_entry, void *cookie, struct task_struct *task, - unsigned long fp, unsigned long pc) + unsigned long fp, unsigned long pc, + bool need_reliable) { struct stackframe frame; - unwind_start(&frame, task, fp, pc); + unwind_start(&frame, task, fp, pc, need_reliable); while (unwind_consume(&frame, consume_entry, cookie)) unwind_next(&frame); return !unwind_failed(&frame); @@ -245,7 +271,36 @@ noinline notrace void arch_stack_walk(stack_trace_consume_fn consume_entry, fp = thread_saved_fp(task); pc = thread_saved_pc(task); } - unwind(consume_entry, cookie, task, fp, pc); + unwind(consume_entry, cookie, task, fp, pc, false); +} + +/* + * arch_stack_walk_reliable() may not be used for livepatch until all of + * the reliability checks are in place in unwind_consume(). However, + * debug and test code can choose to use it even if all the checks are not + * in place. + */ +noinline int notrace arch_stack_walk_reliable(stack_trace_consume_fn consume_fn, + void *cookie, + struct task_struct *task) +{ + unsigned long fp, pc; + + if (!task) + task = current; + + if (task == current) { + /* Skip arch_stack_walk_reliable() in the stack trace. */ + fp = (unsigned long)__builtin_frame_address(1); + pc = (unsigned long)__builtin_return_address(0); + } else { + /* Caller guarantees that the task is not running. */ + fp = thread_saved_fp(task); + pc = thread_saved_pc(task); + } + if (unwind(consume_fn, cookie, task, fp, pc, true)) + return 0; + return -EINVAL; } #endif