Received: by 2002:a05:6a10:5bc5:0:0:0:0 with SMTP id os5csp3418416pxb; Wed, 13 Oct 2021 05:45:02 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxlN9RmhWQe6JqMFSqS8KeoQroD1VDGr7jlhl/8mO36h3ERHf5kBy5bc6/xFb8iW4m4pop5 X-Received: by 2002:a17:906:fc11:: with SMTP id ov17mr40514231ejb.249.1634129101913; Wed, 13 Oct 2021 05:45:01 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1634129101; cv=none; d=google.com; s=arc-20160816; b=H9u1Uhh6KnEh8zNbbmOWkHxwg++ULHpOJ2gwg5ldXW/XnZ5CKdjqrj+xcNBiO1fWBk 3Z6hdRSsFW4WjgeUUdiO6R2Vf1bptCL+EckUEdDfCc6y23chQUCPNIC3MydUSkwtQSNp NHlB0AfZdK2Py4kBLnS5NNiBdN0H2Vh/3/Lj/VlUDem5iBhp1VOfG5DL8woNt5H7OW93 KKakxxPnHfWjF9EWru1eeMYvpXrrdV1FWsu7Nvx26IRLtc2ELh2y7ydDTrsqJp+yFG7C 5E6rY0K85ED7Sp7MzSw6AmXFIShdM7Qna/e778QgZbSNMdKZTzQOJ77w94SWXVXH+/O9 qD9A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:mime-version:references:subject:cc:to:from:date :user-agent:message-id:dkim-signature; bh=vbf9BaeI1plLVLDpsIASZ2eRE9K+lyKJqw1hQNBXXuY=; b=EFh+NMSgAmGyHYwHi031IFJEUV182AkmhI4EOBGTBDOfBu8rQ8TB1DJA/0k77YjquW bQ9d52yd58ZjsKuTFyQgbZP4nnFmeYw3xDgcM46oA8Lx2Xt8p8IB7velM0OuVvq4izDm by5ujHSOvUTUYBHap5d735QLo03rScpXzy7LcdBA6R9mSDIehD+TWalHuIWuTruLlUuA R7ERgDg2TOWOtkbFbQhCBFuB/tcpb6e25/hBb4IyTGPZ5f99HcMO/s28LLHBUsUR49N5 KvJIY4Yoa+rZjGaJ09Klg3SprIKls5zhV0+/t/S/bgA7UzhuVgbvTObMaDPo6kpt7M5w cZ5Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@infradead.org header.s=desiato.20200630 header.b=gdGy3fBx; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id t21si13208937ejd.630.2021.10.13.05.44.36; Wed, 13 Oct 2021 05:45:01 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@infradead.org header.s=desiato.20200630 header.b=gdGy3fBx; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S233964AbhJMMmg (ORCPT + 99 others); Wed, 13 Oct 2021 08:42:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52158 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S233627AbhJMMm2 (ORCPT ); Wed, 13 Oct 2021 08:42:28 -0400 Received: from desiato.infradead.org (desiato.infradead.org [IPv6:2001:8b0:10b:1:d65d:64ff:fe57:4e05]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 70383C061570 for ; Wed, 13 Oct 2021 05:40:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=desiato.20200630; h=Content-Type:MIME-Version:References: Subject:Cc:To:From:Date:Message-ID:Sender:Reply-To:Content-Transfer-Encoding: Content-ID:Content-Description:In-Reply-To; bh=vbf9BaeI1plLVLDpsIASZ2eRE9K+lyKJqw1hQNBXXuY=; b=gdGy3fBxxdFwTXgtxaMhlqxRsg I5B7tegEA28XLpY2fUpin4xvPmcDglyc2xZdVXpI9QgKW4CBmJU7y4nUbqIQAL3vt93xGDs8n33Dr v1m1sNHhj/NSm/mMJrZOPPqLCwQMcq4A/DgbF2lL4uAST9SRQyvIEC+Pr/dmSTrzLeLiqUOUrSVRl gspCWUlMVgDjlv4O6RjOjEUqFDtRIWIugHTqKmlCwoZggLuaP7qHWY46ZjLVvXp6I/Msh83RkZqJ7 M3v+55l1D9rlPQ3r07i6i+G016J51kCMIlZ989qBBUlmILpjZOkMFmA0s1hR8vB+MjO2H2OQy4UCZ 9JlqvJsg==; Received: from j217100.upc-j.chello.nl ([24.132.217.100] helo=noisy.programming.kicks-ass.net) by desiato.infradead.org with esmtpsa (Exim 4.94.2 #2 (Red Hat Linux)) id 1madYM-009bno-Cf; Wed, 13 Oct 2021 12:40:14 +0000 Received: from hirez.programming.kicks-ass.net (hirez.programming.kicks-ass.net [192.168.1.225]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (Client did not present a certificate) by noisy.programming.kicks-ass.net (Postfix) with ESMTPS id D352930030B; Wed, 13 Oct 2021 14:40:13 +0200 (CEST) Received: by hirez.programming.kicks-ass.net (Postfix, from userid 0) id B43E72083A87D; Wed, 13 Oct 2021 14:40:13 +0200 (CEST) Message-ID: <20211013123645.002402102@infradead.org> User-Agent: quilt/0.66 Date: Wed, 13 Oct 2021 14:22:21 +0200 From: Peter Zijlstra To: x86@kernel.org, jpoimboe@redhat.com, andrew.cooper3@citrix.com Cc: linux-kernel@vger.kernel.org, peterz@infradead.org, alexei.starovoitov@gmail.com, ndesaulniers@google.com Subject: [PATCH 4/9] x86/alternative: Implement .retpoline_sites support References: <20211013122217.304265366@infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Rewrite retpoline thunk call sites to be indirect calls for spectre_v2=off. This ensures spectre_v2=off is as near to a RETPOLINE=n build as possible. This is the replacement for objtool writing alternative entries to ensure the same and achieves feature-parity with the previous approach. One noteworthy feature is that it relies on the thunks to be in machine order to compute the register index. Specifically, this does not yet address the Jcc __x86_indirect_thunk_* calls generated by clang, a future patch will add this. Signed-off-by: Peter Zijlstra (Intel) --- arch/x86/include/asm/alternative.h | 1 arch/x86/kernel/alternative.c | 136 +++++++++++++++++++++++++++++++++++-- arch/x86/kernel/module.c | 9 ++ 3 files changed, 141 insertions(+), 5 deletions(-) --- a/arch/x86/include/asm/alternative.h +++ b/arch/x86/include/asm/alternative.h @@ -75,6 +75,7 @@ extern int alternatives_patched; extern void alternative_instructions(void); extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end); +extern void apply_retpolines(s32 *start, s32 *end); struct module; --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -29,6 +29,7 @@ #include #include #include +#include int __read_mostly alternatives_patched; @@ -113,6 +114,7 @@ static void __init_or_module add_nops(vo } } +extern s32 __retpoline_sites[], __retpoline_sites_end[]; extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; extern s32 __smp_locks[], __smp_locks_end[]; void text_poke_early(void *addr, const void *opcode, size_t len); @@ -221,7 +223,7 @@ static __always_inline int optimize_nops * "noinline" to cause control flow change and thus invalidate I$ and * cause refetch after modification. */ -static void __init_or_module noinline optimize_nops(struct alt_instr *a, u8 *instr) +static void __init_or_module noinline optimize_nops(u8 *instr, size_t len) { struct insn insn; int i = 0; @@ -239,11 +241,11 @@ static void __init_or_module noinline op * optimized. */ if (insn.length == 1 && insn.opcode.bytes[0] == 0x90) - i += optimize_nops_range(instr, a->instrlen, i); + i += optimize_nops_range(instr, len, i); else i += insn.length; - if (i >= a->instrlen) + if (i >= len) return; } } @@ -331,10 +333,130 @@ void __init_or_module noinline apply_alt text_poke_early(instr, insn_buff, insn_buff_sz); next: - optimize_nops(a, instr); + optimize_nops(instr, a->instrlen); } } +#ifdef CONFIG_X86_64 + +/* + * CALL/JMP *%\reg + */ +static int emit_indirect(int op, int reg, u8 *bytes) +{ + int i = 0; + u8 modrm; + + switch (op) { + case CALL_INSN_OPCODE: + modrm = 0x10; /* Reg = 2; CALL r/m */ + break; + + case JMP32_INSN_OPCODE: + modrm = 0x20; /* Reg = 4; JMP r/m */ + break; + + default: + WARN_ON_ONCE(1); + return -1; + } + + if (reg >= 8) { + bytes[i++] = 0x41; /* REX.B prefix */ + reg -= 8; + } + + modrm |= 0xc0; /* Mod = 3 */ + modrm += reg; + + bytes[i++] = 0xff; /* opcode */ + bytes[i++] = modrm; + + return i; +} + +/* + * Rewrite the compiler generated retpoline thunk calls. + * + * For spectre_v2=off (!X86_FEATURE_RETPOLINE), rewrite them into immediate + * indirect instructions, avoiding the extra indirection. + * + * For example, convert: + * + * CALL __x86_indirect_thunk_\reg + * + * into: + * + * CALL *%\reg + * + */ +static int patch_retpoline(void *addr, struct insn *insn, u8 *bytes) +{ + void (*target)(void); + int reg, i = 0; + + if (cpu_feature_enabled(X86_FEATURE_RETPOLINE)) + return -1; + + target = addr + insn->length + insn->immediate.value; + reg = (target - &__x86_indirect_thunk_rax) / + (&__x86_indirect_thunk_rcx - &__x86_indirect_thunk_rax); + + if (WARN_ON_ONCE(reg & ~0xf)) + return -1; + + i = emit_indirect(insn->opcode.bytes[0], reg, bytes); + if (i < 0) + return i; + + for (; i < insn->length;) + bytes[i++] = BYTES_NOP1; + + return i; +} + +void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) +{ + s32 *s; + + for (s = start; s < end; s++) { + void *addr = (void *)s + *s; + struct insn insn; + int len, ret; + u8 bytes[16]; + u8 op1, op2; + + ret = insn_decode_kernel(&insn, addr); + if (WARN_ON_ONCE(ret < 0)) + continue; + + op1 = insn.opcode.bytes[0]; + op2 = insn.opcode.bytes[1]; + + switch (op1) { + case CALL_INSN_OPCODE: + case JMP32_INSN_OPCODE: + break; + + default: + WARN_ON_ONCE(1); + continue; + } + + len = patch_retpoline(addr, &insn, bytes); + if (len == insn.length) { + optimize_nops(bytes, len); + text_poke_early(addr, bytes, len); + } + } +} + +#else /* CONFIG_X86_32 */ + +void __init_or_module noinline apply_retpolines(s32 *start, s32 *end) { } + +#endif /* CONFIG_X86_64 */ + #ifdef CONFIG_SMP static void alternatives_smp_lock(const s32 *start, const s32 *end, u8 *text, u8 *text_end) @@ -643,6 +765,12 @@ void __init alternative_instructions(voi apply_paravirt(__parainstructions, __parainstructions_end); /* + * Rewrite the retpolines, must be done before alternatives since + * those can rewrite the retpoline thunks. + */ + apply_retpolines(__retpoline_sites, __retpoline_sites_end); + + /* * Then patch alternatives, such that those paravirt calls that are in * alternatives can be overwritten by their immediate fragments. */ --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -251,7 +251,8 @@ int module_finalize(const Elf_Ehdr *hdr, struct module *me) { const Elf_Shdr *s, *text = NULL, *alt = NULL, *locks = NULL, - *para = NULL, *orc = NULL, *orc_ip = NULL; + *para = NULL, *orc = NULL, *orc_ip = NULL, + *retpolines = NULL; char *secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; for (s = sechdrs; s < sechdrs + hdr->e_shnum; s++) { @@ -267,8 +268,14 @@ int module_finalize(const Elf_Ehdr *hdr, orc = s; if (!strcmp(".orc_unwind_ip", secstrings + s->sh_name)) orc_ip = s; + if (!strcmp(".retpoline_sites", secstrings + s->sh_name)) + retpolines = s; } + if (retpolines) { + void *rseg = (void *)retpolines->sh_addr; + apply_retpolines(rseg, rseg + retpolines->sh_size); + } if (alt) { /* patch .altinstructions */ void *aseg = (void *)alt->sh_addr;