From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751578AbeAPOex (ORCPT + 1 other); Tue, 16 Jan 2018 09:34:53 -0500 Received: from bombadil.infradead.org ([65.50.211.133]:48563 "EHLO bombadil.infradead.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751438AbeAPOeq (ORCPT ); Tue, 16 Jan 2018 09:34:46 -0500 Message-Id: <20180116143241.120894616@infradead.org> User-Agent: quilt/0.63-1 Date: Tue, 16 Jan 2018 15:28:29 +0100 From: Peter Zijlstra To: David Woodhouse , Josh Poimboeuf Cc: linux-kernel@vger.kernel.org, Dave Hansen , Ashok Raj , Thomas Gleixner , Tim Chen , Andy Lutomirski , Linus Torvalds , Greg KH , Andrea Arcangeli , Andi Kleen , Arjan Van De Ven , Dan Williams , Paolo Bonzini , Jun Nakajima , Asit Mallick , Peter Zijlstra , Borislav Petkov Subject: [PATCH v2 04/10] objtool: Implement base jump_assert support References: <20180116142825.376986833@infradead.org> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Disposition: inline; filename=peter_zijlstra-q-objtool_vs_jump_label.patch Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: Implement a jump_label assertion that asserts that the code location is indeed only reachable through a static_branch. Because if GCC is absolutely retaded it could generate code like: xor rax,rax NOP/JMP 1f mov $1, rax 1: test rax,rax jz 2f 2: instead of the sensible: NOP/JMP 1f 1: This implements objtool infrastructure for ensuring the code ends up sane, since we'll rely on that for correctness and security. We tag the instructions after the static branch with static_jump_dest=true; that is the instruction after the NOP and the instruction at the JMP+disp site. Then, when we read the .discard.jump_assert section, we assert that each entry points to an instruction that has static_jump_dest set. With this we can assert that the code emitted for the if statement ends up at the static jump location and nothing untowards happened. Cc: Borislav Petkov Cc: Thomas Gleixner Cc: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) --- tools/objtool/check.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++-- tools/objtool/check.h | 1 2 files changed, 69 insertions(+), 2 deletions(-) --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -687,8 +687,17 @@ static int handle_jump_alt(struct objtoo struct instruction *orig_insn, struct instruction **new_insn) { - if (orig_insn->type == INSN_NOP) + struct instruction *next_insn = list_next_entry(orig_insn, list); + + if (orig_insn->type == INSN_NOP) { + /* + * If orig_insn is a NOP, then new_insn is the branch target + * for when it would've been a JMP. + */ + next_insn->static_jump_dest = true; + (*new_insn)->static_jump_dest = true; return 0; + } if (orig_insn->type != INSN_JUMP_UNCONDITIONAL) { WARN_FUNC("unsupported instruction at jump label", @@ -696,7 +705,16 @@ static int handle_jump_alt(struct objtoo return -1; } - *new_insn = list_next_entry(orig_insn, list); + /* + * Otherwise, orig_insn is a JMP and it will have orig_insn->jump_dest. + * In this case we'll effectively NOP the alt by pointing new_insn at + * next_insn. + */ + orig_insn->jump_dest->static_jump_dest = true; + next_insn->static_jump_dest = true; + + *new_insn = next_insn; + return 0; } @@ -1067,6 +1085,50 @@ static int read_unwind_hints(struct objt return 0; } +static int assert_static_jumps(struct objtool_file *file) +{ + struct section *sec, *relasec; + struct instruction *insn; + struct rela *rela; + int i; + + sec = find_section_by_name(file->elf, ".discard.jump_assert"); + if (!sec) + return 0; + + relasec = sec->rela; + if (!relasec) { + WARN("missing .rela.discard.jump_assert section"); + return -1; + } + + if (sec->len % sizeof(unsigned long)) { + WARN("jump_assert size mismatch: %d %ld", sec->len, sizeof(unsigned long)); + return -1; + } + + for (i = 0; i < sec->len / sizeof(unsigned long); i++) { + rela = find_rela_by_dest(sec, i * sizeof(unsigned long)); + if (!rela) { + WARN("can't find rela for jump_assert[%d]", i); + return -1; + } + + insn = find_insn(file, rela->sym->sec, rela->addend); + if (!insn) { + WARN("can't find insn for jump_assert[%d]", i); + return -1; + } + + if (!insn->static_jump_dest) { + WARN_FUNC("static assert FAIL", insn->sec, insn->offset); + return -1; + } + } + + return 0; +} + static int decode_sections(struct objtool_file *file) { int ret; @@ -2000,6 +2062,10 @@ int check(const char *_objname, bool _no goto out; warnings += ret; + ret = assert_static_jumps(&file); + if (ret < 0) + return ret; + if (list_empty(&file.insn_list)) goto out; --- a/tools/objtool/check.h +++ b/tools/objtool/check.h @@ -45,6 +45,7 @@ struct instruction { unsigned char type; unsigned long immediate; bool alt_group, visited, dead_end, ignore, hint, save, restore, ignore_alts; + bool static_jump_dest; struct symbol *call_dest; struct instruction *jump_dest; struct list_head alts;