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=-9.1 required=3.0 tests=DKIMWL_WL_HIGH,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH, MAILING_LIST_MULTI,SIGNED_OFF_BY,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT 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 79073C169C4 for ; Thu, 31 Jan 2019 19:29:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4026020B1F for ; Thu, 31 Jan 2019 19:29:19 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=chromium.org header.i=@chromium.org header.b="Wd3zlTqi" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729436AbfAaT3R (ORCPT ); Thu, 31 Jan 2019 14:29:17 -0500 Received: from mail-pf1-f193.google.com ([209.85.210.193]:35707 "EHLO mail-pf1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729421AbfAaT3Q (ORCPT ); Thu, 31 Jan 2019 14:29:16 -0500 Received: by mail-pf1-f193.google.com with SMTP id z9so1944320pfi.2 for ; Thu, 31 Jan 2019 11:29:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0clwIEKwgT6hx+XqokLmAuCxSYnGRtU7PPE22rHWz3M=; b=Wd3zlTqih2Xg/XfIIlxFOyP0FtbYZFoFzRO8Boil76TykAgYQtCYaDtBIQFU7Wfxx+ ELlowkisP3VeG0cSs4eYHQVHQN0uY8Okt1jyEmszf4WujW1Zm0G9krAsUX/uIrULtqfm NCODULOFqyLRK/4QkVj/Z7dGd5CwIu7OVo2LM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0clwIEKwgT6hx+XqokLmAuCxSYnGRtU7PPE22rHWz3M=; b=QCE0d80aThsFSnfKQ/mTRrYTiXnEQzZJelnWI7Z0IjKGR4Ii+ig8fWo0kPcoh+7ExY 9yYh16zTkCrEFpY6rYbbwilegszEy9EVVO3dnTZ7+IvI5t4Xf+fisNdB/U0W7hnOB4O7 bLcmg1H9yuoT55eDhjd7xpLG3FvXxrETZ4McGphktZ1YmOa1TKsb//4BFLk3nQzkZH2l Xo/xqd05caEv32EuVeH6G/FQReZtdxNN3fm9iPSpaGkKC71fGdLI99j6s+9dupemXo+Z jPQq8ZBN7ztf8qj7hQw+Iy8EX8GG04ewKFupBmib2DhqxGdxJ3aj+BZlsnq44NmH46dW w0BQ== X-Gm-Message-State: AJcUukfVnw07+LZex/7sRCh2OA2FuWN8WOuMbcu0zlWIR8UBlj/F0KY0 7nCNoNcjzETCWqZS8W1Hf0Nzbw== X-Google-Smtp-Source: ALg8bN6vQcUbop9wggg0bQdVnWyOZIX8r9aUj+OTXipI1W7vjlg/s/XyeyBSRLHbnraTVmp1WthWCA== X-Received: by 2002:a65:64c8:: with SMTP id t8mr32253386pgv.31.1548962954664; Thu, 31 Jan 2019 11:29:14 -0800 (PST) Received: from skynet.sea.corp.google.com ([2620:15c:17:4:29de:3bb1:1270:e679]) by smtp.gmail.com with ESMTPSA id s130sm11164399pgc.60.2019.01.31.11.29.13 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 31 Jan 2019 11:29:14 -0800 (PST) From: Thomas Garnier To: kernel-hardening@lists.openwall.com Cc: kristen@linux.intel.com, Thomas Garnier , Steven Rostedt , Ingo Molnar , Thomas Gleixner , Borislav Petkov , "H. Peter Anvin" , x86@kernel.org, Joe Lawrence , Thomas Garnier , James Hogan , "Peter Zijlstra (Intel)" , nixiaoming , linux-kernel@vger.kernel.org Subject: [PATCH v6 21/27] x86/ftrace: Adapt function tracing for PIE support Date: Thu, 31 Jan 2019 11:24:28 -0800 Message-Id: <20190131192533.34130-22-thgarnie@chromium.org> X-Mailer: git-send-email 2.20.1.495.gaa96b0ce6b-goog In-Reply-To: <20190131192533.34130-1-thgarnie@chromium.org> References: <20190131192533.34130-1-thgarnie@chromium.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org When using PIE with function tracing, the compiler generates a call through the GOT (call *__fentry__@GOTPCREL). This instruction takes 6-bytes instead of 5-bytes with a relative call. If PIE is enabled, replace the 6th byte of the GOT call by a 1-byte nop so ftrace can handle the previous 5-bytes as before. Position Independent Executable (PIE) support will allow to extend the KASLR randomization range below 0xffffffff80000000. Signed-off-by: Thomas Garnier Reviewed-by: Steven Rostedt (VMware) --- arch/x86/kernel/ftrace.c | 51 ++++++++++++++++++++++++-- scripts/recordmcount.c | 78 ++++++++++++++++++++++++++-------------- 2 files changed, 101 insertions(+), 28 deletions(-) diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index 8257a59704ae..82feb8c7a47e 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c @@ -102,7 +102,7 @@ static const unsigned char *ftrace_nop_replace(void) static int ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, - unsigned const char *new_code) + unsigned const char *new_code) { unsigned char replaced[MCOUNT_INSN_SIZE]; @@ -135,6 +135,53 @@ ftrace_modify_code_direct(unsigned long ip, unsigned const char *old_code, return 0; } +/* Bytes before call GOT offset */ +static const unsigned char got_call_preinsn[] = { 0xff, 0x15 }; + +static int +ftrace_modify_initial_code(unsigned long ip, unsigned const char *old_code, + unsigned const char *new_code) +{ + unsigned char replaced[MCOUNT_INSN_SIZE + 1]; + + /* + * If PIE is not enabled default to the original approach to code + * modification. + */ + if (!IS_ENABLED(CONFIG_X86_PIE)) + return ftrace_modify_code_direct(ip, old_code, new_code); + + ftrace_expected = old_code; + + /* Ensure the instructions point to a call to the GOT */ + if (probe_kernel_read(replaced, (void *)ip, sizeof(replaced))) { + WARN_ONCE(1, "invalid function"); + return -EFAULT; + } + + if (memcmp(replaced, got_call_preinsn, sizeof(got_call_preinsn))) { + WARN_ONCE(1, "invalid function call"); + return -EINVAL; + } + + /* + * Build a nop slide with a 5-byte nop and 1-byte nop to keep the ftrace + * hooking algorithm working with the expected 5 bytes instruction. + */ + memset(replaced, ideal_nops[1][0], sizeof(replaced)); + memcpy(replaced, new_code, MCOUNT_INSN_SIZE); + + ip = text_ip_addr(ip); + + if (probe_kernel_write((void *)ip, replaced, sizeof(replaced))) + return -EPERM; + + sync_core(); + + return 0; + +} + int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr) { @@ -153,7 +200,7 @@ int ftrace_make_nop(struct module *mod, * just modify the code directly. */ if (addr == MCOUNT_ADDR) - return ftrace_modify_code_direct(rec->ip, old, new); + return ftrace_modify_initial_code(rec->ip, old, new); ftrace_expected = NULL; diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index a50a2aa963ad..4b8bd746ed2e 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c @@ -171,33 +171,9 @@ umalloc(size_t size) return addr; } -static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 }; -static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 }; -static unsigned char *ideal_nop; - static char rel_type_nop; - static int (*make_nop)(void *map, size_t const offset); - -static int make_nop_x86(void *map, size_t const offset) -{ - uint32_t *ptr; - unsigned char *op; - - /* Confirm we have 0xe8 0x0 0x0 0x0 0x0 */ - ptr = map + offset; - if (*ptr != 0) - return -1; - - op = map + offset - 1; - if (*op != 0xe8) - return -1; - - /* convert to nop */ - ulseek(fd_map, offset - 1, SEEK_SET); - uwrite(fd_map, ideal_nop, 5); - return 0; -} +static unsigned char *ideal_nop; static unsigned char ideal_nop4_arm_le[4] = { 0x00, 0x00, 0xa0, 0xe1 }; /* mov r0, r0 */ static unsigned char ideal_nop4_arm_be[4] = { 0xe1, 0xa0, 0x00, 0x00 }; /* mov r0, r0 */ @@ -447,6 +423,49 @@ static void MIPS64_r_info(Elf64_Rel *const rp, unsigned sym, unsigned type) }).r_info; } +static unsigned char ideal_nop5_x86_64[5] = { 0x0f, 0x1f, 0x44, 0x00, 0x00 }; +static unsigned char ideal_nop6_x86_64[6] = { 0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00 }; +static unsigned char ideal_nop5_x86_32[5] = { 0x3e, 0x8d, 0x74, 0x26, 0x00 }; +static size_t ideal_nop_x86_size; + +static unsigned char stub_default_x86[2] = { 0xe8, 0x00 }; /* call relative */ +static unsigned char stub_got_x86[3] = { 0xff, 0x15, 0x00 }; /* call .got */ +static unsigned char *stub_x86; +static size_t stub_x86_size; + +static int make_nop_x86(void *map, size_t const offset) +{ + uint32_t *ptr; + size_t stub_offset = offset - stub_x86_size; + + /* confirm we have the expected stub */ + ptr = map + stub_offset; + if (memcmp(ptr, stub_x86, stub_x86_size)) + return -1; + + /* convert to nop */ + ulseek(fd_map, stub_offset, SEEK_SET); + uwrite(fd_map, ideal_nop, ideal_nop_x86_size); + return 0; +} + +/* Swap the stub and nop for a got call if the binary is built with PIE */ +static int is_fake_mcount_x86_x64(Elf64_Rel const *rp) +{ + if (ELF64_R_TYPE(rp->r_info) == R_X86_64_GOTPCREL) { + ideal_nop = ideal_nop6_x86_64; + ideal_nop_x86_size = sizeof(ideal_nop6_x86_64); + stub_x86 = stub_got_x86; + stub_x86_size = sizeof(stub_got_x86); + mcount_adjust_64 = 1 - stub_x86_size; + } + + /* Once the relocation was checked, rollback to default */ + is_fake_mcount64 = fn_is_fake_mcount64; + return is_fake_mcount64(rp); +} + + static void do_file(char const *const fname) { @@ -509,6 +528,9 @@ do_file(char const *const fname) rel_type_nop = R_386_NONE; make_nop = make_nop_x86; ideal_nop = ideal_nop5_x86_32; + ideal_nop_x86_size = sizeof(ideal_nop5_x86_32); + stub_x86 = stub_default_x86; + stub_x86_size = sizeof(stub_default_x86); mcount_adjust_32 = -1; break; case EM_ARM: reltype = R_ARM_ABS32; @@ -533,9 +555,13 @@ do_file(char const *const fname) case EM_X86_64: make_nop = make_nop_x86; ideal_nop = ideal_nop5_x86_64; + ideal_nop_x86_size = sizeof(ideal_nop5_x86_64); + stub_x86 = stub_default_x86; + stub_x86_size = sizeof(stub_default_x86); reltype = R_X86_64_64; rel_type_nop = R_X86_64_NONE; - mcount_adjust_64 = -1; + is_fake_mcount64 = is_fake_mcount_x86_x64; + mcount_adjust_64 = 1 - stub_x86_size; break; } /* end switch */ -- 2.20.1.495.gaa96b0ce6b-goog