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
2437 def_bool n2437 def_bool n
2438 select CALL_PADDING2438 select CALL_PADDING
2439 2439
2440config PREFIX_SYMBOLS
2441 def_bool y
2442 depends on CALL_PADDING && !CFI
2443
2444menuconfig CPU_MITIGATIONS2440menuconfig CPU_MITIGATIONS
2445 bool "Mitigations for CPU vulnerabilities"2441 bool "Mitigations for CPU vulnerabilities"
2446 default y2442 default y
lib/Kconfig.debug+1−1
@@ -3070,7 +3070,7 @@ config FORTIFY_KUNIT_TEST · 3069 unmodified lines
3070config LONGEST_SYM_KUNIT_TEST3070config LONGEST_SYM_KUNIT_TEST
3071 tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS3071 tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS
3072 depends on KUNIT && KPROBES3072 depends on KUNIT && KPROBES
3073 depends on !PREFIX_SYMBOLS && !CFI && !GCOV_KERNEL3073 depends on !CALL_PADDING && !CFI && !GCOV_KERNEL
3074 default KUNIT_ALL_TESTS3074 default KUNIT_ALL_TESTS
3075 help3075 help
3076 Tests the longest symbol possible3076 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
187objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr187objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
188objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake188objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
189objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt189objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
190objtool-args-$(CONFIG_FINEIBT) += --cfi190objtool-args-$(CONFIG_CALL_PADDING) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
191ifdef CONFIG_CALL_PADDING
192objtool-args-$(CONFIG_CFI) += --cfi
193objtool-args-$(CONFIG_FINEIBT) += --fineibt
194endif
191objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount195objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
192ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL196ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
193objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop197objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
@@ -200,7 +204,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval · 6 unmodified lines
200objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call204objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
201objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess205objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
202objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable206objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
203objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
204objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror207objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
205 208
206objtool-args = $(objtool-args-y) \209objtool-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
73 73
74static const struct option check_options[] = {74static const struct option check_options[] = {
75 OPT_GROUP("Actions:"),75 OPT_GROUP("Actions:"),
76 OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
77 OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),76 OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
78 OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),77 OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
79 OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),78 OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
@@ -84,7 +83,7 @@ static const struct option check_options[] = { · 4 unmodified lines
84 OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),83 OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
85 OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),84 OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
86 OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),85 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"),
88 OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),87 OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
89 OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),88 OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
90 OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),89 OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
@@ -92,6 +91,8 @@ static const struct option check_options[] = { · 1 unmodified line
92 OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),91 OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
93 92
94 OPT_GROUP("Options:"),93 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"),
95 OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),96 OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
96 OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),97 OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),
97 OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),98 OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
@@ -163,6 +164,16 @@ static bool opts_valid(void) · 65 unmodified lines
163 return false;164 return false;
164 }165 }
165 166
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
166 if (opts.disas ||177 if (opts.disas ||
167 opts.hack_jump_label ||178 opts.hack_jump_label ||
168 opts.hack_noinstr ||179 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
881 return 0;881 return 0;
882}882}
883 883
884/*
885* Grow __cfi_ symbols to fill the NOP gap between the 'mov <hash>, %rax' and
886* the start of the function.
887*/
888static 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
884static int create_cfi_sections(struct objtool_file *file)909static int create_cfi_sections(struct objtool_file *file)
885{910{
886 struct section *sec;911 struct section *sec;
@@ -4903,12 +4928,6 @@ int check(struct objtool_file *file) · 4016 unmodified lines
4903 goto out;4928 goto out;
4904 }4929 }
4905 4930
4906 if (opts.cfi) {
4907 ret = create_cfi_sections(file);
4908 if (ret)
4909 goto out;
4910 }
4911
4912 if (opts.rethunk) {4931 if (opts.rethunk) {
4913 ret = create_return_sites_sections(file);4932 ret = create_return_sites_sections(file);
4914 if (ret)4933 if (ret)
@@ -4928,9 +4947,21 @@ int check(struct objtool_file *file) · 13 unmodified lines
4928 }4947 }
4929 4948
4930 if (opts.prefix) {4949 if (opts.prefix) {
4931 ret = create_prefix_symbols(file);4950 if (!opts.cfi) {
4932 if (ret)4951 ret = create_prefix_symbols(file);
4933 goto out;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 }
4934 }4965 }
4935 4966
4936 if (opts.ibt) {4967 if (opts.ibt) {
tools/objtool/elf.c+20−0
@@ -997,6 +997,26 @@ non_local: · 996 unmodified lines
997 return sym;997 return sym;
998}998}
999 999
1000int 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
1000struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)1020struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
1001{1021{
1002 struct symbol *sym = calloc(1, sizeof(*sym));1022 struct symbol *sym = calloc(1, sizeof(*sym));
tools/objtool/include/objtool/builtin.h+4−3
@@ -9,8 +9,8 @@ · 8 unmodified lines
9 9
10struct opts {10struct opts {
11 /* actions: */11 /* actions: */
12 bool cfi;
13 bool checksum;12 bool checksum;
13 const char *disas;
14 bool dump_orc;14 bool dump_orc;
15 bool hack_jump_label;15 bool hack_jump_label;
16 bool hack_noinstr;16 bool hack_noinstr;
@@ -20,6 +20,7 @@ struct opts { · 3 unmodified lines
20 bool noabs;20 bool noabs;
21 bool noinstr;21 bool noinstr;
22 bool orc;22 bool orc;
23 int prefix;
23 bool retpoline;24 bool retpoline;
24 bool rethunk;25 bool rethunk;
25 bool unret;26 bool unret;
@@ -27,14 +28,14 @@ struct opts { · 1 unmodified line
27 bool stackval;28 bool stackval;
28 bool static_call;29 bool static_call;
29 bool uaccess;30 bool uaccess;
30 int prefix;
31 const char *disas;
32 31
33 /* options: */32 /* options: */
34 bool backtrace;33 bool backtrace;
35 bool backup;34 bool backup;
35 bool cfi;
36 const char *debug_checksum;36 const char *debug_checksum;
37 bool dryrun;37 bool dryrun;
38 bool fineibt;
38 bool link;39 bool link;
39 bool mnop;40 bool mnop;
40 bool module;41 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
199 struct symbol *sym,199 struct symbol *sym,
200 s64 addend);200 s64 addend);
201 201
202int elf_write_symbol(struct elf *elf, struct symbol *sym);
202int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,203int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
203 unsigned int len, const char *insn);204 unsigned int len, const char *insn);
204 205