From mboxrd@z Thu Jan 1 00:00:00 1970 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=-22.4 required=3.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,MENTIONS_GIT_HOSTING,SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS, USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham 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 0DF07C43603 for ; Mon, 9 Dec 2019 14:31:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C83A5207FF for ; Mon, 9 Dec 2019 14:31:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="V93v4RsD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727653AbfLIObw (ORCPT ); Mon, 9 Dec 2019 09:31:52 -0500 Received: from mail-yw1-f73.google.com ([209.85.161.73]:49769 "EHLO mail-yw1-f73.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727038AbfLIObw (ORCPT ); Mon, 9 Dec 2019 09:31:52 -0500 Received: by mail-yw1-f73.google.com with SMTP id q124so3016598ywb.16 for ; Mon, 09 Dec 2019 06:31:51 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=6dJeiKQWAfFPvcJSNpo7bPzgTA1iBxJQ7E6uNYT5cFk=; b=V93v4RsD7PkZduWaTBtsIbjC8Qu0QuHUBWPrdZzYDiwIkcz33Y5G0wjx4+1NYDYxJh qUTGU6O525L2ph0mRx7Rqko6x/IOJc/5eUMomV272P/h+ItLB/vOK8mlUatwRaYogep+ 7BvgsO5WLI8GZ6Ln0P1IrO6UhtKXfJ7aOlidT/KTHBfUstr1zaASpASf2+XGpcLMYj26 8EOHkPetyw0kUKLeUIoGJCcTMGC6m+x3cQkTi48jI7tJdeymgabpw6SfXzAzQ7CSVMY9 I92kc+2ate0/kS+Ry11zlU+8RjcYmpZW5lMMw6iURB1mga/kRaDvoCquCbzrR4hc7pN3 Q4pQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=6dJeiKQWAfFPvcJSNpo7bPzgTA1iBxJQ7E6uNYT5cFk=; b=CdkuRpOa345ThJtd8TSwzCqQzs/fUkPZBuSMIr2fXQwd5QRzz22iHqCVdHB17PBvnT l5OOeOFEG/iaD4F/n2wuAT+HZvfTvwaxLTKH6kjzYPgrKYZybbGYR76ZB6dc01tCWcNt zZwNKyGn3q6PS8FoAWHgJbw+QMkzfau8KHfLBH76loHWDUy8pBGeLIqXXQJ4t3pp4QJq WZgbjAdIxy2IdkvHXGSWJFIHPryalCDgRhRtdeI7Qtd6Hq7hlEsGCy7lMypQhzdff7HW 5HFQShKP19YOTegYLvJeYqfu0KXIlgSH6OLfe0feXW2XAQpLHzdA9nqYeSeHN1fNv6vC 2jzA== X-Gm-Message-State: APjAAAUwWQyp3u4kn5h7ssHj9OydTnc2BlVE/7NpJn1tsRqDG2xTwmI6 L7B5wpzOSWaoRaKf4m59sOXvN2S9mA== X-Google-Smtp-Source: APXvYqxrTqUCB5Cav8Xav9DCo/+cBcZA/aqzAuJ0wgyQ3Oe74pB51iuVTgNRGTiKZtG088VI8Ouli/GPlA== X-Received: by 2002:a0d:c187:: with SMTP id c129mr5509408ywd.389.1575901910795; Mon, 09 Dec 2019 06:31:50 -0800 (PST) Date: Mon, 9 Dec 2019 15:31:18 +0100 In-Reply-To: <20191209143120.60100-1-jannh@google.com> Message-Id: <20191209143120.60100-2-jannh@google.com> Mime-Version: 1.0 References: <20191209143120.60100-1-jannh@google.com> X-Mailer: git-send-email 2.24.0.393.g34dc348eaf-goog Subject: [PATCH v6 2/4] x86/traps: Print address on #GP From: Jann Horn To: Thomas Gleixner , Ingo Molnar , Borislav Petkov , "H. Peter Anvin" , x86@kernel.org, Andrey Ryabinin , Alexander Potapenko , Dmitry Vyukov , kasan-dev@googlegroups.com, jannh@google.com Cc: linux-kernel@vger.kernel.org, Andrey Konovalov , Andy Lutomirski , Sean Christopherson Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org A frequent cause of #GP exceptions are memory accesses to non-canonical addresses. Unlike #PF, #GP doesn't come with a fault address in CR2, so the kernel doesn't currently print the fault address for #GP. Luckily, we already have the necessary infrastructure for decoding X86 instructions and computing the memory address that is being accessed; hook it up to the #GP handler so that we can figure out the address operand of the faulting instruction and print it. Distinguish two cases: a) (Part of) the memory range being accessed lies in the non-canonical address range; in this case, is is likely that the address we decoded is actually the one that caused the #GP. b) The entire memory range of the operand we decoded lies in canonical address space; the #GP may or may not be related in some way to the address we computed. We'll still print it, but with hedging language in the message. While it is already possible to compute the faulting address manually by disassembling the opcode dump and evaluating the instruction against the register dump, this should make it slightly easier to identify crashes at a glance. Note that the operand length, which we get from the instruction decoder and use to determine whether the access straddles into non-canonical address space, is currently somewhat unreliable; but it should be good enough, considering that Linux on x86-64 never maps the page directly before the start of the non-canonical range anyway, and therefore the case where a memory range begins in that page and potentially straddles into the non-canonical range should be fairly uncommon. And if we do get this wrong, it only influences whether the error message claims that the access is canonical. Reviewed-and-tested-by: Sean Christopherson Signed-off-by: Jann Horn --- Notes: v2: - print different message for segment-related GP (Borislav) - rewrite check for non-canonical address (Sean) - make it clear we don't know for sure why the GP happened (Andy) v3: - change message format to one line (Borislav) v4: - rename insn_bytes to insn_buf (Ingo) - add space after GPFSTR (Ingo) - make sizeof(desc) clearer (Ingo, Borislav) - also print the address (with a different message) if it's canonical (Ingo) v5: - reword comment on get_kernel_gp_address() (Sean) - make get_kernel_gp_address() also work on 32-bit (Sean) - minor nits (Sean) - more hedging for canonical GP (Sean) - let get_kernel_gp_address() return an enum (Sean) - rewrite commit message v6: - add comma after GPFSTR (Sean) - reorder variable declarations (Sean) I have already sent a patch to syzkaller that relaxes their parsing of GPF messages (https://github.com/google/syzkaller/commit/432c7650) such that changes like the one in this patch don't break it. That patch has already made its way into syzbot's syzkaller instances according to . arch/x86/kernel/traps.c | 70 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index f19de6f45d48..c8b4ae6aed5b 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -56,6 +56,8 @@ #include #include #include +#include +#include #ifdef CONFIG_X86_64 #include @@ -518,10 +520,55 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code) do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, 0, NULL); } +enum kernel_gp_hint { + GP_NO_HINT, + GP_NON_CANONICAL, + GP_CANONICAL +}; + +/* + * When an uncaught #GP occurs, try to determine a memory address accessed by + * the instruction and return that address to the caller. + * Also try to figure out whether any part of the access to that address was + * non-canonical. + */ +static enum kernel_gp_hint get_kernel_gp_address(struct pt_regs *regs, + unsigned long *addr) +{ + u8 insn_buf[MAX_INSN_SIZE]; + struct insn insn; + + if (probe_kernel_read(insn_buf, (void *)regs->ip, MAX_INSN_SIZE)) + return GP_NO_HINT; + + kernel_insn_init(&insn, insn_buf, MAX_INSN_SIZE); + insn_get_modrm(&insn); + insn_get_sib(&insn); + *addr = (unsigned long)insn_get_addr_ref(&insn, regs); + + if (*addr == -1UL) + return GP_NO_HINT; + +#ifdef CONFIG_X86_64 + /* + * Check that: + * - the operand is not in the kernel half + * - the last byte of the operand is not in the user canonical half + */ + if (*addr < ~__VIRTUAL_MASK && + *addr + insn.opnd_bytes - 1 > __VIRTUAL_MASK) + return GP_NON_CANONICAL; +#endif + + return GP_CANONICAL; +} + +#define GPFSTR "general protection fault" + dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code) { - const char *desc = "general protection fault"; + char desc[sizeof(GPFSTR) + 50 + 2*sizeof(unsigned long) + 1] = GPFSTR; struct task_struct *tsk; RCU_LOCKDEP_WARN(!rcu_is_watching(), "entry code didn't wake RCU"); @@ -540,6 +587,9 @@ do_general_protection(struct pt_regs *regs, long error_code) tsk = current; if (!user_mode(regs)) { + enum kernel_gp_hint hint = GP_NO_HINT; + unsigned long gp_addr; + if (fixup_exception(regs, X86_TRAP_GP, error_code, 0)) return; @@ -556,8 +606,22 @@ do_general_protection(struct pt_regs *regs, long error_code) return; if (notify_die(DIE_GPF, desc, regs, error_code, - X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP) - die(desc, regs, error_code); + X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP) + return; + + if (error_code) + snprintf(desc, sizeof(desc), "segment-related " GPFSTR); + else + hint = get_kernel_gp_address(regs, &gp_addr); + + if (hint != GP_NO_HINT) + snprintf(desc, sizeof(desc), GPFSTR ", %s 0x%lx", + (hint == GP_NON_CANONICAL) ? + "probably for non-canonical address" : + "maybe for address", + gp_addr); + + die(desc, regs, error_code); return; } -- 2.24.0.393.g34dc348eaf-goog