From: Peter Zijlstra <peterz@infradead.org>
To: tglx@linutronix.de, jpoimboe@redhat.com
Cc: linux-kernel@vger.kernel.org, x86@kernel.org, peterz@infradead.org
Subject: [RFC][PATCH 15/16] objtool: Implement noinstr validation
Date: Thu, 12 Mar 2020 14:41:22 +0100 [thread overview]
Message-ID: <20200312135042.288201372@infradead.org> (raw)
In-Reply-To: 20200312134107.700205216@infradead.org
Validate that any call out of .noinstr.text is in between
instr_begin() and instr_end() annotations.
This annotation is useful to ensure correct behaviour wrt tracing
sensitive code like entry/exit and idle code. When we run code in a
sensitive context we want a guarantee no unknown code is ran.
Since this validation relies on knowing the section of call
destination symbols, we must run it on vmlinux.o instead of on
individual object files.
Add the -i "noinstr validation only" option because:
- vmlinux.o isn't 'clean' vs the existing validations
- skipping the other validations (which have already been done
earlier in the build) saves around a second of runtime.
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
tools/objtool/builtin-check.c | 4 -
tools/objtool/builtin.h | 2
tools/objtool/check.c | 155 ++++++++++++++++++++++++++++++++++++------
tools/objtool/check.h | 3
4 files changed, 140 insertions(+), 24 deletions(-)
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -17,7 +17,7 @@
#include "builtin.h"
#include "check.h"
-bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, noinstr, vmlinux;
static const char * const check_usage[] = {
"objtool check [<options>] file.o",
@@ -32,6 +32,8 @@ const struct option check_options[] = {
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
+ OPT_BOOLEAN('i', "noinstr", &noinstr, "noinstr validation only"),
+ OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
OPT_END(),
};
--- a/tools/objtool/builtin.h
+++ b/tools/objtool/builtin.h
@@ -8,7 +8,7 @@
#include <subcmd/parse-options.h>
extern const struct option check_options[];
-extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
+extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, noinstr, vmlinux;
extern int cmd_check(int argc, const char **argv);
extern int cmd_orc(int argc, const char **argv);
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1292,6 +1292,53 @@ static int read_retpoline_hints(struct o
return 0;
}
+static int read_instr_hints(struct objtool_file *file)
+{
+ struct section *sec;
+ struct instruction *insn;
+ struct rela *rela;
+
+ sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ if (rela->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!insn) {
+ WARN("bad .discard.instr_end entry");
+ return -1;
+ }
+
+ insn->instr--;
+ }
+
+ sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
+ if (!sec)
+ return 0;
+
+ list_for_each_entry(rela, &sec->rela_list, list) {
+ if (rela->sym->type != STT_SECTION) {
+ WARN("unexpected relocation symbol type in %s", sec->name);
+ return -1;
+ }
+
+ insn = find_insn(file, rela->sym->sec, rela->addend);
+ if (!insn) {
+ WARN("bad .discard.instr_begin entry");
+ return -1;
+ }
+
+ insn->instr++;
+ }
+
+ return 0;
+}
+
static void mark_rodata(struct objtool_file *file)
{
struct section *sec;
@@ -1363,6 +1410,10 @@ static int decode_sections(struct objtoo
if (ret)
return ret;
+ ret = read_instr_hints(file);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -1914,6 +1965,14 @@ static inline const char *call_dest_name
static int validate_call(struct instruction *insn, struct insn_state *state)
{
+ if (state->noinstr && state->instr <= 0 &&
+ (!insn->call_dest || insn->call_dest->sec != insn->sec)) {
+ WARN_FUNC("call to %s() leaves .noinstr.text section",
+ insn->sec, insn->offset, call_dest_name(insn));
+ return 1;
+ }
+
+
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
WARN_FUNC("call to %s() with UACCESS enabled",
insn->sec, insn->offset, call_dest_name(insn));
@@ -1942,6 +2001,12 @@ static int validate_sibling_call(struct
static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
{
+ if (state->noinstr && state->instr > 0) {
+ WARN_FUNC("return with instrumentation enabled",
+ insn->sec, insn->offset);
+ return 1;
+ }
+
if (state->uaccess && !func_uaccess_safe(func)) {
WARN_FUNC("return with UACCESS enabled",
insn->sec, insn->offset);
@@ -2023,6 +2088,9 @@ static int validate_branch(struct objtoo
return 0;
}
+ if (state.noinstr)
+ state.instr += insn->instr;
+
if (insn->hint) {
if (insn->restore) {
struct instruction *save_insn, *i;
@@ -2355,9 +2423,8 @@ static bool ignore_unreachable_insn(stru
return false;
}
-static int validate_functions(struct objtool_file *file)
+static int validate_sec_functions(struct objtool_file *file, struct section *sec)
{
- struct section *sec;
struct symbol *func;
struct instruction *insn;
struct insn_state state;
@@ -2370,33 +2437,68 @@ static int validate_functions(struct obj
CFI_NUM_REGS * sizeof(struct cfi_reg));
state.stack_size = initial_func_cfi.cfa.offset;
- for_each_sec(file, sec) {
- list_for_each_entry(func, &sec->symbol_list, list) {
- if (func->type != STT_FUNC)
- continue;
+ /*
+ * We need the full vmlinux for noinstr validation, otherwise we can
+ * not correctly determine insn->call_dest->sec (external symbols do
+ * not have a section).
+ */
+ if (vmlinux && !strcmp(sec->name, ".noinstr.text"))
+ state.noinstr = true;
- if (!func->len) {
- WARN("%s() is missing an ELF size annotation",
- func->name);
- warnings++;
- }
+ list_for_each_entry(func, &sec->symbol_list, list) {
+ if (func->type != STT_FUNC)
+ continue;
- if (func->pfunc != func || func->alias != func)
- continue;
+ if (!func->len) {
+ WARN("%s() is missing an ELF size annotation",
+ func->name);
+ warnings++;
+ }
- insn = find_insn(file, sec, func->offset);
- if (!insn || insn->ignore || insn->visited)
- continue;
+ if (func->pfunc != func || func->alias != func)
+ continue;
- state.uaccess = func->uaccess_safe;
+ insn = find_insn(file, sec, func->offset);
+ if (!insn || insn->ignore || insn->visited)
+ continue;
- ret = validate_branch(file, func, insn, state);
- if (ret && backtrace)
- BT_FUNC("<=== (func)", insn);
- warnings += ret;
- }
+ state.uaccess = func->uaccess_safe;
+
+ ret = validate_branch(file, func, insn, state);
+ if (ret && backtrace)
+ BT_FUNC("<=== (func)", insn);
+ warnings += ret;
+ }
+
+ return warnings;
+}
+
+static int validate_noinstr_functions(struct objtool_file *file)
+{
+ struct section *sec;
+
+ if (!vmlinux) {
+ WARN("noinstr validation needs vmlinux\n");
+ return -1;
}
+ sec = find_section_by_name(file->elf, ".noinstr.text");
+ if (!sec) {
+ WARN("No .noinstr.text section");
+ return -1;
+ }
+
+ return validate_sec_functions(file, sec);
+}
+
+static int validate_functions(struct objtool_file *file)
+{
+ struct section *sec;
+ int warnings = 0;
+
+ for_each_sec(file, sec)
+ warnings += validate_sec_functions(file, sec);
+
return warnings;
}
@@ -2446,6 +2548,15 @@ int check(const char *_objname, bool orc
if (list_empty(&file.insn_list))
goto out;
+ if (noinstr) {
+ ret = validate_noinstr_functions(&file);
+ if (ret < 0)
+ goto out;
+
+ warnings += ret;
+ goto out;
+ }
+
if (retpoline) {
ret = validate_retpoline(&file);
if (ret < 0)
--- a/tools/objtool/check.h
+++ b/tools/objtool/check.h
@@ -20,6 +20,8 @@ struct insn_state {
unsigned char type;
bool bp_scratch;
bool drap, end, uaccess, df;
+ bool noinstr;
+ s8 instr;
unsigned int uaccess_stack;
int drap_reg, drap_offset;
struct cfi_reg vals[CFI_NUM_REGS];
@@ -35,6 +37,7 @@ struct instruction {
unsigned long immediate;
bool alt_group, dead_end, ignore, hint, save, restore, ignore_alts;
bool retpoline_safe;
+ s8 instr;
u8 visited;
struct symbol *call_dest;
struct instruction *jump_dest;
next prev parent reply other threads:[~2020-03-12 13:51 UTC|newest]
Thread overview: 50+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-03-12 13:41 [RFC][PATCH 00/16] objtool: vmlinux.o and noinstr validation Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 01/16] objtool: Introduce validate_return() Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 02/16] objtool: Rename func_for_each_insn() Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 03/16] objtool: Rename func_for_each_insn_all() Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 04/16] objtool: Annotate identity_mapped() Peter Zijlstra
2020-03-13 14:34 ` Peter Zijlstra
2020-03-15 15:45 ` Josh Poimboeuf
2020-03-13 16:46 ` Brian Gerst
2020-03-13 17:22 ` Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 05/16] objtool: Optimize find_symbol_by_index() Peter Zijlstra
2020-03-15 16:09 ` Josh Poimboeuf
2020-03-15 16:18 ` Josh Poimboeuf
2020-03-15 16:10 ` Josh Poimboeuf
2020-03-17 11:55 ` Miroslav Benes
2020-03-17 14:08 ` Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 06/16] objtool: Add a statistics mode Peter Zijlstra
2020-03-15 16:20 ` Josh Poimboeuf
2020-03-12 13:41 ` [RFC][PATCH 07/16] objtool: Optimize find_section_by_index() Peter Zijlstra
2020-03-15 16:24 ` Josh Poimboeuf
2020-03-12 13:41 ` [RFC][PATCH 08/16] Optimize find_section_by_name() Peter Zijlstra
2020-03-15 16:25 ` Josh Poimboeuf
2020-03-17 12:22 ` Miroslav Benes
2020-03-17 14:10 ` Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 09/16] objtool: Optimize find_symbol_*() and read_symbols() Peter Zijlstra
2020-03-15 15:48 ` Josh Poimboeuf
2020-03-15 16:41 ` Josh Poimboeuf
2020-03-12 13:41 ` [RFC][PATCH 10/16] objtool: Resize insn_hash Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 11/16] objtool: Optimize find_symbol_by_name() Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 12/16] objtool: Optimize read_sections() Peter Zijlstra
2020-03-15 16:53 ` Josh Poimboeuf
2020-03-12 13:41 ` [RFC][PATCH 13/16] objtool: Delete cleanup() Peter Zijlstra
2020-03-12 13:41 ` [RFC][PATCH 14/16] objtool: Optimize find_rela_by_dest_range() Peter Zijlstra
2020-03-12 13:41 ` Peter Zijlstra [this message]
2020-03-15 18:03 ` [RFC][PATCH 15/16] objtool: Implement noinstr validation Josh Poimboeuf
2020-03-16 13:24 ` Peter Zijlstra
2020-03-16 16:19 ` Josh Poimboeuf
2020-03-16 16:21 ` Josh Poimboeuf
2020-03-16 16:46 ` Peter Zijlstra
2020-03-16 16:48 ` Peter Zijlstra
2020-03-16 19:20 ` Josh Poimboeuf
2020-03-12 13:41 ` [RFC][PATCH 16/16] objtool: Optimize !vmlinux.o again Peter Zijlstra
2020-03-12 21:57 ` [RFC][PATCH v2 " Peter Zijlstra
2020-03-12 16:23 ` [RFC][PATCH 00/16] objtool: vmlinux.o and noinstr validation Peter Zijlstra
2020-03-12 17:44 ` Josh Poimboeuf
2020-03-12 22:23 ` Peter Zijlstra
2020-03-17 0:56 ` Masami Hiramatsu
2020-03-17 9:26 ` Thomas Gleixner
2020-03-17 14:20 ` Masami Hiramatsu
2020-03-17 12:14 ` Peter Zijlstra
2020-03-15 18:12 ` Josh Poimboeuf
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200312135042.288201372@infradead.org \
--to=peterz@infradead.org \
--cc=jpoimboe@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=x86@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).