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.
Unified · Split
| @@ -2437,10 +2437,6 @@ config CALL_THUNKS · 2436 unmodified lines | |||
| 2437 | def_bool n | 2437 | def_bool n |
| 2438 | select CALL_PADDING | 2438 | select CALL_PADDING |
| 2439 | 2439 | ||
| 2440 | config PREFIX_SYMBOLS | ||
| 2441 | def_bool y | ||
| 2442 | depends on CALL_PADDING && !CFI | ||
| 2443 | |||
| 2444 | menuconfig CPU_MITIGATIONS | 2440 | menuconfig CPU_MITIGATIONS |
| 2445 | bool "Mitigations for CPU vulnerabilities" | 2441 | bool "Mitigations for CPU vulnerabilities" |
| 2446 | default y | 2442 | default y |
| @@ -3070,7 +3070,7 @@ config FORTIFY_KUNIT_TEST · 3069 unmodified lines | |||
| 3070 | config LONGEST_SYM_KUNIT_TEST | 3070 | config LONGEST_SYM_KUNIT_TEST |
| 3071 | tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS | 3071 | tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS |
| 3072 | depends on KUNIT && KPROBES | 3072 | depends on KUNIT && KPROBES |
| 3073 | depends on !PREFIX_SYMBOLS && !CFI && !GCOV_KERNEL | 3073 | depends on !CALL_PADDING && !CFI && !GCOV_KERNEL |
| 3074 | default KUNIT_ALL_TESTS | 3074 | default KUNIT_ALL_TESTS |
| 3075 | help | 3075 | help |
| 3076 | Tests the longest symbol possible | 3076 | Tests the longest symbol possible |
| @@ -187,7 +187,11 @@ objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label · 186 unmodified lines | |||
| 187 | objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr | 187 | objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr |
| 188 | objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake | 188 | objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake |
| 189 | objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt | 189 | 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 | ||
| 191 | objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount | 195 | objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount |
| 192 | ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL | 196 | ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL |
| 193 | objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop | 197 | objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop |
| @@ -200,7 +204,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval · 6 unmodified lines | |||
| 200 | objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call | 204 | objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call |
| 201 | objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess | 205 | objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess |
| 202 | objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable | 206 | objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable |
| 203 | objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) | ||
| 204 | objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror | 207 | objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror |
| 205 | 208 | ||
| 206 | objtool-args = $(objtool-args-y) \ | 209 | objtool-args = $(objtool-args-y) \ |
| @@ -73,7 +73,6 @@ static int parse_hacks(const struct option *opt, const char *str, int unset) · 72 unmodified lines | |||
| 73 | 73 | ||
| 74 | static const struct option check_options[] = { | 74 | static 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 || |
| @@ -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 | */ | ||
| 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 | |||
| 884 | static int create_cfi_sections(struct objtool_file *file) | 909 | static 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) { |
| @@ -997,6 +997,26 @@ non_local: · 996 unmodified lines | |||
| 997 | return sym; | 997 | return sym; |
| 998 | } | 998 | } |
| 999 | 999 | ||
| 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 | |||
| 1000 | struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec) | 1020 | struct 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)); |
| @@ -9,8 +9,8 @@ · 8 unmodified lines | |||
| 9 | 9 | ||
| 10 | struct opts { | 10 | struct 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; |
| @@ -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 | ||
| 202 | int elf_write_symbol(struct elf *elf, struct symbol *sym); | ||
| 202 | int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset, | 203 | int 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 | ||