Skip to content

Commit c4a3393

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
objtool: Implement noinstr validation
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 two options: -d/--duplicate "duplicate validation for vmlinux" -l/--vmlinux "vmlinux.o validation" Where the latter auto-detects when objname ends with "vmlinux.o" and the former will force all validations, also those already done on !vmlinux object files. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Miroslav Benes <[email protected]> Reviewed-by: Alexandre Chartre <[email protected]> Acked-by: Josh Poimboeuf <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent e7c0219 commit c4a3393

File tree

5 files changed

+112
-4
lines changed

5 files changed

+112
-4
lines changed

tools/objtool/builtin-check.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
*/
1515

1616
#include <subcmd/parse-options.h>
17+
#include <string.h>
1718
#include "builtin.h"
1819
#include "check.h"
1920

20-
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
21+
bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
2122

2223
static const char * const check_usage[] = {
2324
"objtool check [<options>] file.o",
@@ -32,12 +33,14 @@ const struct option check_options[] = {
3233
OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"),
3334
OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"),
3435
OPT_BOOLEAN('s', "stats", &stats, "print statistics"),
36+
OPT_BOOLEAN('d', "duplicate", &validate_dup, "duplicate validation for vmlinux.o"),
37+
OPT_BOOLEAN('l', "vmlinux", &vmlinux, "vmlinux.o validation"),
3538
OPT_END(),
3639
};
3740

3841
int cmd_check(int argc, const char **argv)
3942
{
40-
const char *objname;
43+
const char *objname, *s;
4144

4245
argc = parse_options(argc, argv, check_options, check_usage, 0);
4346

@@ -46,5 +49,9 @@ int cmd_check(int argc, const char **argv)
4649

4750
objname = argv[0];
4851

52+
s = strstr(objname, "vmlinux.o");
53+
if (s && !s[9])
54+
vmlinux = true;
55+
4956
return check(objname, false);
5057
}

tools/objtool/builtin.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <subcmd/parse-options.h>
99

1010
extern const struct option check_options[];
11-
extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats;
11+
extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats, validate_dup, vmlinux;
1212

1313
extern int cmd_check(int argc, const char **argv);
1414
extern int cmd_orc(int argc, const char **argv);

tools/objtool/check.c

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ static int decode_instructions(struct objtool_file *file)
257257
strncmp(sec->name, ".discard.", 9))
258258
sec->text = true;
259259

260+
if (!strcmp(sec->name, ".noinstr.text"))
261+
sec->noinstr = true;
262+
260263
for (offset = 0; offset < sec->len; offset += insn->len) {
261264
insn = malloc(sizeof(*insn));
262265
if (!insn) {
@@ -1340,6 +1343,53 @@ static int read_retpoline_hints(struct objtool_file *file)
13401343
return 0;
13411344
}
13421345

1346+
static int read_instr_hints(struct objtool_file *file)
1347+
{
1348+
struct section *sec;
1349+
struct instruction *insn;
1350+
struct rela *rela;
1351+
1352+
sec = find_section_by_name(file->elf, ".rela.discard.instr_end");
1353+
if (!sec)
1354+
return 0;
1355+
1356+
list_for_each_entry(rela, &sec->rela_list, list) {
1357+
if (rela->sym->type != STT_SECTION) {
1358+
WARN("unexpected relocation symbol type in %s", sec->name);
1359+
return -1;
1360+
}
1361+
1362+
insn = find_insn(file, rela->sym->sec, rela->addend);
1363+
if (!insn) {
1364+
WARN("bad .discard.instr_end entry");
1365+
return -1;
1366+
}
1367+
1368+
insn->instr--;
1369+
}
1370+
1371+
sec = find_section_by_name(file->elf, ".rela.discard.instr_begin");
1372+
if (!sec)
1373+
return 0;
1374+
1375+
list_for_each_entry(rela, &sec->rela_list, list) {
1376+
if (rela->sym->type != STT_SECTION) {
1377+
WARN("unexpected relocation symbol type in %s", sec->name);
1378+
return -1;
1379+
}
1380+
1381+
insn = find_insn(file, rela->sym->sec, rela->addend);
1382+
if (!insn) {
1383+
WARN("bad .discard.instr_begin entry");
1384+
return -1;
1385+
}
1386+
1387+
insn->instr++;
1388+
}
1389+
1390+
return 0;
1391+
}
1392+
13431393
static void mark_rodata(struct objtool_file *file)
13441394
{
13451395
struct section *sec;
@@ -1411,6 +1461,10 @@ static int decode_sections(struct objtool_file *file)
14111461
if (ret)
14121462
return ret;
14131463

1464+
ret = read_instr_hints(file);
1465+
if (ret)
1466+
return ret;
1467+
14141468
return 0;
14151469
}
14161470

@@ -2007,6 +2061,13 @@ static inline const char *call_dest_name(struct instruction *insn)
20072061

20082062
static int validate_call(struct instruction *insn, struct insn_state *state)
20092063
{
2064+
if (state->noinstr && state->instr <= 0 &&
2065+
(!insn->call_dest || insn->call_dest->sec != insn->sec)) {
2066+
WARN_FUNC("call to %s() leaves .noinstr.text section",
2067+
insn->sec, insn->offset, call_dest_name(insn));
2068+
return 1;
2069+
}
2070+
20102071
if (state->uaccess && !func_uaccess_safe(insn->call_dest)) {
20112072
WARN_FUNC("call to %s() with UACCESS enabled",
20122073
insn->sec, insn->offset, call_dest_name(insn));
@@ -2035,6 +2096,12 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
20352096

20362097
static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
20372098
{
2099+
if (state->noinstr && state->instr > 0) {
2100+
WARN_FUNC("return with instrumentation enabled",
2101+
insn->sec, insn->offset);
2102+
return 1;
2103+
}
2104+
20382105
if (state->uaccess && !func_uaccess_safe(func)) {
20392106
WARN_FUNC("return with UACCESS enabled",
20402107
insn->sec, insn->offset);
@@ -2115,6 +2182,9 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
21152182
return 0;
21162183
}
21172184

2185+
if (state.noinstr)
2186+
state.instr += insn->instr;
2187+
21182188
if (insn->hint)
21192189
state.cfi = insn->cfi;
21202190
else
@@ -2422,6 +2492,14 @@ static int validate_section(struct objtool_file *file, struct section *sec)
24222492
struct insn_state state;
24232493
int ret, warnings = 0;
24242494

2495+
/*
2496+
* We need the full vmlinux for noinstr validation, otherwise we can
2497+
* not correctly determine insn->call_dest->sec (external symbols do
2498+
* not have a section).
2499+
*/
2500+
if (vmlinux)
2501+
state.noinstr = sec->noinstr;
2502+
24252503
list_for_each_entry(func, &sec->symbol_list, list) {
24262504
if (func->type != STT_FUNC)
24272505
continue;
@@ -2456,6 +2534,17 @@ static int validate_section(struct objtool_file *file, struct section *sec)
24562534
return warnings;
24572535
}
24582536

2537+
static int validate_vmlinux_functions(struct objtool_file *file)
2538+
{
2539+
struct section *sec;
2540+
2541+
sec = find_section_by_name(file->elf, ".noinstr.text");
2542+
if (!sec)
2543+
return 0;
2544+
2545+
return validate_section(file, sec);
2546+
}
2547+
24592548
static int validate_functions(struct objtool_file *file)
24602549
{
24612550
struct section *sec;
@@ -2513,6 +2602,15 @@ int check(const char *_objname, bool orc)
25132602
if (list_empty(&file.insn_list))
25142603
goto out;
25152604

2605+
if (vmlinux && !validate_dup) {
2606+
ret = validate_vmlinux_functions(&file);
2607+
if (ret < 0)
2608+
goto out;
2609+
2610+
warnings += ret;
2611+
goto out;
2612+
}
2613+
25162614
if (retpoline) {
25172615
ret = validate_retpoline(&file);
25182616
if (ret < 0)

tools/objtool/check.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ struct insn_state {
1818
unsigned int uaccess_stack;
1919
bool uaccess;
2020
bool df;
21+
bool noinstr;
22+
s8 instr;
2123
};
2224

2325
struct instruction {
@@ -31,6 +33,7 @@ struct instruction {
3133
bool alt_group, dead_end, ignore, ignore_alts;
3234
bool hint;
3335
bool retpoline_safe;
36+
s8 instr;
3437
u8 visited;
3538
u8 ret_offset;
3639
struct symbol *call_dest;

tools/objtool/elf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct section {
3939
char *name;
4040
int idx;
4141
unsigned int len;
42-
bool changed, text, rodata;
42+
bool changed, text, rodata, noinstr;
4343
};
4444

4545
struct symbol {

0 commit comments

Comments
 (0)