History

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.

Josh Poimboeuf committed 6 months ago

Commit fc0bb991 parent cca84cb8 files · +84 −21Browse files

Unified · Split

8 files
  1. arch/x86/Kconfig +0 −4
  2. lib/Kconfig.debug +1 −1
  3. scripts/Makefile.lib +5 −2
  4. tools/objtool/builtin-check.c +13 −2
  5. tools/objtool/check.c +40 −9
  6. tools/objtool/elf.c +20 −0
  7. tools/objtool/include/objtool/builtin.h +4 −3
  8. tools/objtool/include/objtool/elf.h +1 −0
arch/x86/Kconfig+0−4
@@ -2437,10 +2437,6 @@ config CALL_THUNKS · 2436 unmodified lines
24372437 def_bool n
24382438 select CALL_PADDING
24392439
2440−config PREFIX_SYMBOLS
2441− def_bool y
2442− depends on CALL_PADDING && !CFI
2443−
24442440 menuconfig CPU_MITIGATIONS
24452441 bool "Mitigations for CPU vulnerabilities"
24462442 default y
lib/Kconfig.debug+1−1
@@ -3070,7 +3070,7 @@ config FORTIFY_KUNIT_TEST · 3069 unmodified lines
30703070 config LONGEST_SYM_KUNIT_TEST
30713071 tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS
30723072 depends on KUNIT && KPROBES
3073− depends on !PREFIX_SYMBOLS && !CFI && !GCOV_KERNEL
3073+ depends on !CALL_PADDING && !CFI && !GCOV_KERNEL
30743074 default KUNIT_ALL_TESTS
30753075 help
30763076 Tests the longest symbol possible
scripts/Makefile.lib+5−2
@@ -187,7 +187,11 @@ objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label · 186 unmodified lines
187187 objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
188188 objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
189189 objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
190−objtool-args-$(CONFIG_FINEIBT) += --cfi
190+objtool-args-$(CONFIG_CALL_PADDING) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
191+ifdef CONFIG_CALL_PADDING
192+objtool-args-$(CONFIG_CFI) += --cfi
193+objtool-args-$(CONFIG_FINEIBT) += --fineibt
194+endif
191195 objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
192196 ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
193197 objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
@@ -200,7 +204,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval · 6 unmodified lines
200204 objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
201205 objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
202206 objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
203−objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
204207 objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
205208
206209 objtool-args = $(objtool-args-y) \
tools/objtool/builtin-check.c+13−2
@@ -73,7 +73,6 @@ static int parse_hacks(const struct option *opt, const char *str, int unset) · 72 unmodified lines
7373
7474 static const struct option check_options[] = {
7575 OPT_GROUP("Actions:"),
76− OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
7776 OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
7877 OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
7978 OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
@@ -84,7 +83,7 @@ static const struct option check_options[] = { · 4 unmodified lines
8483 OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
8584 OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
8685 OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
87− OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"),
86+ OPT_INTEGER(0, "prefix", &opts.prefix, "generate or grow prefix symbols for N-byte function padding"),
8887 OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
8988 OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
9089 OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
@@ -92,6 +91,8 @@ static const struct option check_options[] = { · 1 unmodified line
9291 OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
9392
9493 OPT_GROUP("Options:"),
94+ OPT_BOOLEAN(0, "cfi", &opts.cfi, "grow kCFI preamble symbols (use with --prefix)"),
95+ OPT_BOOLEAN(0, "fineibt", &opts.fineibt, "create .cfi_sites section for FineIBT"),
9596 OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
9697 OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),
9798 OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
@@ -163,6 +164,16 @@ static bool opts_valid(void) · 65 unmodified lines
163164 return false;
164165 }
165166
167+ if (opts.cfi && !opts.prefix) {
168+ ERROR("--cfi requires --prefix");
169+ return false;
170+ }
171+
172+ if (opts.fineibt && !opts.cfi) {
173+ ERROR("--fineibt requires --cfi");
174+ return false;
175+ }
176+
166177 if (opts.disas ||
167178 opts.hack_jump_label ||
168179 opts.hack_noinstr ||
tools/objtool/check.c+40−9
@@ -881,6 +881,31 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) · 880 unmodified lines
881881 return 0;
882882 }
883883
884+/*
885+* Grow __cfi_ symbols to fill the NOP gap between the 'mov <hash>, %rax' and
886+* the start of the function.
887+*/
888+static int grow_cfi_symbols(struct objtool_file *file)
889+{
890+ struct symbol *sym;
891+
892+ for_each_sym(file->elf, sym) {
893+ if (!is_func_sym(sym) || !strstarts(sym->name, "__cfi_") ||
894+ sym->len != 5)
895+ continue;
896+
897+ if (!find_func_by_offset(sym->sec, sym->offset + sym->len + opts.prefix))
898+ continue;
899+
900+ sym->len += opts.prefix;
901+ sym->sym.st_size = sym->len;
902+ if (elf_write_symbol(file->elf, sym))
903+ return -1;
904+ }
905+
906+ return 0;
907+}
908+
884909 static int create_cfi_sections(struct objtool_file *file)
885910 {
886911 struct section *sec;
@@ -4903,12 +4928,6 @@ int check(struct objtool_file *file) · 4016 unmodified lines
49034928 goto out;
49044929 }
49054930
4906− if (opts.cfi) {
4907− ret = create_cfi_sections(file);
4908− if (ret)
4909− goto out;
4910− }
4911−
49124931 if (opts.rethunk) {
49134932 ret = create_return_sites_sections(file);
49144933 if (ret)
@@ -4928,9 +4947,21 @@ int check(struct objtool_file *file) · 13 unmodified lines
49284947 }
49294948
49304949 if (opts.prefix) {
4931− ret = create_prefix_symbols(file);
4932− if (ret)
4933− goto out;
4950+ if (!opts.cfi) {
4951+ ret = create_prefix_symbols(file);
4952+ if (ret)
4953+ goto out;
4954+ } else {
4955+ ret = grow_cfi_symbols(file);
4956+ if (ret)
4957+ goto out;
4958+
4959+ if (opts.fineibt) {
4960+ ret = create_cfi_sections(file);
4961+ if (ret)
4962+ goto out;
4963+ }
4964+ }
49344965 }
49354966
49364967 if (opts.ibt) {
tools/objtool/elf.c+20−0
@@ -997,6 +997,26 @@ non_local: · 996 unmodified lines
997997 return sym;
998998 }
999999
1000+int elf_write_symbol(struct elf *elf, struct symbol *sym)
1001+{
1002+ struct section *symtab, *symtab_shndx;
1003+
1004+ symtab = find_section_by_name(elf, ".symtab");
1005+ if (!symtab) {
1006+ ERROR("no .symtab");
1007+ return -1;
1008+ }
1009+
1010+ symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
1011+
1012+ if (elf_update_symbol(elf, symtab, symtab_shndx, sym))
1013+ return -1;
1014+
1015+ mark_sec_changed(elf, symtab, true);
1016+
1017+ return 0;
1018+}
1019+
10001020 struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
10011021 {
10021022 struct symbol *sym = calloc(1, sizeof(*sym));
tools/objtool/include/objtool/builtin.h+4−3
@@ -9,8 +9,8 @@ · 8 unmodified lines
99
1010 struct opts {
1111 /* actions: */
12− bool cfi;
1312 bool checksum;
13+ const char *disas;
1414 bool dump_orc;
1515 bool hack_jump_label;
1616 bool hack_noinstr;
@@ -20,6 +20,7 @@ struct opts { · 3 unmodified lines
2020 bool noabs;
2121 bool noinstr;
2222 bool orc;
23+ int prefix;
2324 bool retpoline;
2425 bool rethunk;
2526 bool unret;
@@ -27,14 +28,14 @@ struct opts { · 1 unmodified line
2728 bool stackval;
2829 bool static_call;
2930 bool uaccess;
30− int prefix;
31− const char *disas;
3231
3332 /* options: */
3433 bool backtrace;
3534 bool backup;
35+ bool cfi;
3636 const char *debug_checksum;
3737 bool dryrun;
38+ bool fineibt;
3839 bool link;
3940 bool mnop;
4041 bool module;
tools/objtool/include/objtool/elf.h+1−0
@@ -199,6 +199,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec, · 198 unmodified lines
199199 struct symbol *sym,
200200 s64 addend);
201201
202+int elf_write_symbol(struct elf *elf, struct symbol *sym);
202203 int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
203204 unsigned int len, const char *insn);
204205