mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
objtool: Grow __cfi_* prefix symbols for all CFI+CALL_PADDING
For all CONFIG_CFI+CONFIG_CALL_PADDING configs, for C functions, the __cfi_ symbols only cover the 5-byte kCFI type hash. After that there also N bytes of NOP padding between the hash and the function entry which aren't associated with any symbol. The NOPs can be replaced with actual code at runtime. Without a symbol, unwinders and tooling have no way of knowing where those bytes belong. Grow the existing __cfi_* symbols to fill that gap. Note that assembly functions with SYM_TYPED_FUNC_START() aren't affected by this issue, their __cfi_ symbols also cover the padding. Also, CONFIG_PREFIX_SYMBOLS has no reason to exist: CONFIG_CALL_PADDING is what causes the compiler to emit NOP padding before function entry (via -fpatchable-function-entry), so it's the right condition for creating prefix symbols. Remove CONFIG_PREFIX_SYMBOLS, as it's no longer needed. Simplify the LONGEST_SYM_KUNIT_TEST dependency accordingly. Rework objtool's arguments a bit to handle the variety of prefix/cfi-related cases. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>
This commit is contained in:
@@ -2437,10 +2437,6 @@ config CALL_THUNKS
|
||||
def_bool n
|
||||
select CALL_PADDING
|
||||
|
||||
config PREFIX_SYMBOLS
|
||||
def_bool y
|
||||
depends on CALL_PADDING && !CFI
|
||||
|
||||
menuconfig CPU_MITIGATIONS
|
||||
bool "Mitigations for CPU vulnerabilities"
|
||||
default y
|
||||
|
||||
@@ -3070,7 +3070,7 @@ config FORTIFY_KUNIT_TEST
|
||||
config LONGEST_SYM_KUNIT_TEST
|
||||
tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT && KPROBES
|
||||
depends on !PREFIX_SYMBOLS && !CFI && !GCOV_KERNEL
|
||||
depends on !CALL_PADDING && !CFI && !GCOV_KERNEL
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
Tests the longest symbol possible
|
||||
|
||||
@@ -187,7 +187,11 @@ objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label
|
||||
objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
|
||||
objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
|
||||
objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
|
||||
objtool-args-$(CONFIG_FINEIBT) += --cfi
|
||||
objtool-args-$(CONFIG_CALL_PADDING) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
||||
ifdef CONFIG_CALL_PADDING
|
||||
objtool-args-$(CONFIG_CFI) += --cfi
|
||||
objtool-args-$(CONFIG_FINEIBT) += --fineibt
|
||||
endif
|
||||
objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
|
||||
ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
|
||||
objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
|
||||
@@ -200,7 +204,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
|
||||
objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
|
||||
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
|
||||
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
|
||||
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
||||
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
|
||||
|
||||
objtool-args = $(objtool-args-y) \
|
||||
|
||||
@@ -73,7 +73,6 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
|
||||
|
||||
static const struct option check_options[] = {
|
||||
OPT_GROUP("Actions:"),
|
||||
OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
|
||||
OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
|
||||
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
|
||||
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
|
||||
@@ -84,7 +83,7 @@ static const struct option check_options[] = {
|
||||
OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
|
||||
OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
|
||||
OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate or grow prefix symbols for N-byte function padding"),
|
||||
OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
|
||||
OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
|
||||
OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
|
||||
@@ -92,6 +91,8 @@ static const struct option check_options[] = {
|
||||
OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
|
||||
|
||||
OPT_GROUP("Options:"),
|
||||
OPT_BOOLEAN(0, "cfi", &opts.cfi, "grow kCFI preamble symbols (use with --prefix)"),
|
||||
OPT_BOOLEAN(0, "fineibt", &opts.fineibt, "create .cfi_sites section for FineIBT"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
@@ -163,6 +164,16 @@ static bool opts_valid(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.cfi && !opts.prefix) {
|
||||
ERROR("--cfi requires --prefix");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.fineibt && !opts.cfi) {
|
||||
ERROR("--fineibt requires --cfi");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.disas ||
|
||||
opts.hack_jump_label ||
|
||||
opts.hack_noinstr ||
|
||||
|
||||
@@ -881,6 +881,31 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grow __cfi_ symbols to fill the NOP gap between the 'mov <hash>, %rax' and
|
||||
* the start of the function.
|
||||
*/
|
||||
static int grow_cfi_symbols(struct objtool_file *file)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
for_each_sym(file->elf, sym) {
|
||||
if (!is_func_sym(sym) || !strstarts(sym->name, "__cfi_") ||
|
||||
sym->len != 5)
|
||||
continue;
|
||||
|
||||
if (!find_func_by_offset(sym->sec, sym->offset + sym->len + opts.prefix))
|
||||
continue;
|
||||
|
||||
sym->len += opts.prefix;
|
||||
sym->sym.st_size = sym->len;
|
||||
if (elf_write_symbol(file->elf, sym))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_cfi_sections(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
@@ -4903,12 +4928,6 @@ int check(struct objtool_file *file)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opts.cfi) {
|
||||
ret = create_cfi_sections(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opts.rethunk) {
|
||||
ret = create_return_sites_sections(file);
|
||||
if (ret)
|
||||
@@ -4928,9 +4947,21 @@ int check(struct objtool_file *file)
|
||||
}
|
||||
|
||||
if (opts.prefix) {
|
||||
ret = create_prefix_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!opts.cfi) {
|
||||
ret = create_prefix_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ret = grow_cfi_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (opts.fineibt) {
|
||||
ret = create_cfi_sections(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.ibt) {
|
||||
|
||||
@@ -997,6 +997,26 @@ struct symbol *elf_create_symbol(struct elf *elf, const char *name,
|
||||
return sym;
|
||||
}
|
||||
|
||||
int elf_write_symbol(struct elf *elf, struct symbol *sym)
|
||||
{
|
||||
struct section *symtab, *symtab_shndx;
|
||||
|
||||
symtab = find_section_by_name(elf, ".symtab");
|
||||
if (!symtab) {
|
||||
ERROR("no .symtab");
|
||||
return -1;
|
||||
}
|
||||
|
||||
symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
|
||||
|
||||
if (elf_update_symbol(elf, symtab, symtab_shndx, sym))
|
||||
return -1;
|
||||
|
||||
mark_sec_changed(elf, symtab, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
|
||||
{
|
||||
struct symbol *sym = calloc(1, sizeof(*sym));
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
|
||||
struct opts {
|
||||
/* actions: */
|
||||
bool cfi;
|
||||
bool checksum;
|
||||
const char *disas;
|
||||
bool dump_orc;
|
||||
bool hack_jump_label;
|
||||
bool hack_noinstr;
|
||||
@@ -20,6 +20,7 @@ struct opts {
|
||||
bool noabs;
|
||||
bool noinstr;
|
||||
bool orc;
|
||||
int prefix;
|
||||
bool retpoline;
|
||||
bool rethunk;
|
||||
bool unret;
|
||||
@@ -27,14 +28,14 @@ struct opts {
|
||||
bool stackval;
|
||||
bool static_call;
|
||||
bool uaccess;
|
||||
int prefix;
|
||||
const char *disas;
|
||||
|
||||
/* options: */
|
||||
bool backtrace;
|
||||
bool backup;
|
||||
bool cfi;
|
||||
const char *debug_checksum;
|
||||
bool dryrun;
|
||||
bool fineibt;
|
||||
bool link;
|
||||
bool mnop;
|
||||
bool module;
|
||||
|
||||
@@ -199,6 +199,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
|
||||
struct symbol *sym,
|
||||
s64 addend);
|
||||
|
||||
int elf_write_symbol(struct elf *elf, struct symbol *sym);
|
||||
int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
|
||||
unsigned int len, const char *insn);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user