diff options
Diffstat (limited to 'tools/objtool')
-rw-r--r-- | tools/objtool/arch.h | 6 | ||||
-rw-r--r-- | tools/objtool/arch/x86/decode.c | 13 | ||||
-rw-r--r-- | tools/objtool/builtin-check.c | 3 | ||||
-rw-r--r-- | tools/objtool/builtin.h | 2 | ||||
-rw-r--r-- | tools/objtool/check.c | 197 | ||||
-rw-r--r-- | tools/objtool/check.h | 3 | ||||
-rw-r--r-- | tools/objtool/elf.h | 1 | ||||
-rw-r--r-- | tools/objtool/special.c | 18 | ||||
-rw-r--r-- | tools/objtool/special.h | 1 |
9 files changed, 223 insertions, 21 deletions
diff --git a/tools/objtool/arch.h b/tools/objtool/arch.h index b0d7dc3d71b5..467c2fe798a9 100644 --- a/tools/objtool/arch.h +++ b/tools/objtool/arch.h | |||
@@ -33,7 +33,9 @@ | |||
33 | #define INSN_STACK 8 | 33 | #define INSN_STACK 8 |
34 | #define INSN_BUG 9 | 34 | #define INSN_BUG 9 |
35 | #define INSN_NOP 10 | 35 | #define INSN_NOP 10 |
36 | #define INSN_OTHER 11 | 36 | #define INSN_STAC 11 |
37 | #define INSN_CLAC 12 | ||
38 | #define INSN_OTHER 13 | ||
37 | #define INSN_LAST INSN_OTHER | 39 | #define INSN_LAST INSN_OTHER |
38 | 40 | ||
39 | enum op_dest_type { | 41 | enum op_dest_type { |
@@ -41,6 +43,7 @@ enum op_dest_type { | |||
41 | OP_DEST_REG_INDIRECT, | 43 | OP_DEST_REG_INDIRECT, |
42 | OP_DEST_MEM, | 44 | OP_DEST_MEM, |
43 | OP_DEST_PUSH, | 45 | OP_DEST_PUSH, |
46 | OP_DEST_PUSHF, | ||
44 | OP_DEST_LEAVE, | 47 | OP_DEST_LEAVE, |
45 | }; | 48 | }; |
46 | 49 | ||
@@ -55,6 +58,7 @@ enum op_src_type { | |||
55 | OP_SRC_REG_INDIRECT, | 58 | OP_SRC_REG_INDIRECT, |
56 | OP_SRC_CONST, | 59 | OP_SRC_CONST, |
57 | OP_SRC_POP, | 60 | OP_SRC_POP, |
61 | OP_SRC_POPF, | ||
58 | OP_SRC_ADD, | 62 | OP_SRC_ADD, |
59 | OP_SRC_AND, | 63 | OP_SRC_AND, |
60 | }; | 64 | }; |
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 540a209b78ab..ab20a96fee50 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c | |||
@@ -357,19 +357,26 @@ int arch_decode_instruction(struct elf *elf, struct section *sec, | |||
357 | /* pushf */ | 357 | /* pushf */ |
358 | *type = INSN_STACK; | 358 | *type = INSN_STACK; |
359 | op->src.type = OP_SRC_CONST; | 359 | op->src.type = OP_SRC_CONST; |
360 | op->dest.type = OP_DEST_PUSH; | 360 | op->dest.type = OP_DEST_PUSHF; |
361 | break; | 361 | break; |
362 | 362 | ||
363 | case 0x9d: | 363 | case 0x9d: |
364 | /* popf */ | 364 | /* popf */ |
365 | *type = INSN_STACK; | 365 | *type = INSN_STACK; |
366 | op->src.type = OP_SRC_POP; | 366 | op->src.type = OP_SRC_POPF; |
367 | op->dest.type = OP_DEST_MEM; | 367 | op->dest.type = OP_DEST_MEM; |
368 | break; | 368 | break; |
369 | 369 | ||
370 | case 0x0f: | 370 | case 0x0f: |
371 | 371 | ||
372 | if (op2 >= 0x80 && op2 <= 0x8f) { | 372 | if (op2 == 0x01) { |
373 | |||
374 | if (modrm == 0xca) | ||
375 | *type = INSN_CLAC; | ||
376 | else if (modrm == 0xcb) | ||
377 | *type = INSN_STAC; | ||
378 | |||
379 | } else if (op2 >= 0x80 && op2 <= 0x8f) { | ||
373 | 380 | ||
374 | *type = INSN_JUMP_CONDITIONAL; | 381 | *type = INSN_JUMP_CONDITIONAL; |
375 | 382 | ||
diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 99f10c585cbe..f3b378126011 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #include "builtin.h" | 29 | #include "builtin.h" |
30 | #include "check.h" | 30 | #include "check.h" |
31 | 31 | ||
32 | bool no_fp, no_unreachable, retpoline, module, backtrace; | 32 | bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess; |
33 | 33 | ||
34 | static const char * const check_usage[] = { | 34 | static const char * const check_usage[] = { |
35 | "objtool check [<options>] file.o", | 35 | "objtool check [<options>] file.o", |
@@ -42,6 +42,7 @@ const struct option check_options[] = { | |||
42 | OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), | 42 | OPT_BOOLEAN('r', "retpoline", &retpoline, "Validate retpoline assumptions"), |
43 | OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), | 43 | OPT_BOOLEAN('m', "module", &module, "Indicates the object will be part of a kernel module"), |
44 | OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"), | 44 | OPT_BOOLEAN('b', "backtrace", &backtrace, "unwind on error"), |
45 | OPT_BOOLEAN('a', "uaccess", &uaccess, "enable uaccess checking"), | ||
45 | OPT_END(), | 46 | OPT_END(), |
46 | }; | 47 | }; |
47 | 48 | ||
diff --git a/tools/objtool/builtin.h b/tools/objtool/builtin.h index 65fd3cc3c98b..69762f9c5602 100644 --- a/tools/objtool/builtin.h +++ b/tools/objtool/builtin.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <subcmd/parse-options.h> | 20 | #include <subcmd/parse-options.h> |
21 | 21 | ||
22 | extern const struct option check_options[]; | 22 | extern const struct option check_options[]; |
23 | extern bool no_fp, no_unreachable, retpoline, module, backtrace; | 23 | extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess; |
24 | 24 | ||
25 | extern int cmd_check(int argc, const char **argv); | 25 | extern int cmd_check(int argc, const char **argv); |
26 | extern int cmd_orc(int argc, const char **argv); | 26 | extern int cmd_orc(int argc, const char **argv); |
diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8118361295dd..965e954e07f4 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c | |||
@@ -443,6 +443,82 @@ static void add_ignores(struct objtool_file *file) | |||
443 | } | 443 | } |
444 | 444 | ||
445 | /* | 445 | /* |
446 | * This is a whitelist of functions that is allowed to be called with AC set. | ||
447 | * The list is meant to be minimal and only contains compiler instrumentation | ||
448 | * ABI and a few functions used to implement *_{to,from}_user() functions. | ||
449 | * | ||
450 | * These functions must not directly change AC, but may PUSHF/POPF. | ||
451 | */ | ||
452 | static const char *uaccess_safe_builtin[] = { | ||
453 | /* KASAN */ | ||
454 | "kasan_report", | ||
455 | "check_memory_region", | ||
456 | /* KASAN out-of-line */ | ||
457 | "__asan_loadN_noabort", | ||
458 | "__asan_load1_noabort", | ||
459 | "__asan_load2_noabort", | ||
460 | "__asan_load4_noabort", | ||
461 | "__asan_load8_noabort", | ||
462 | "__asan_load16_noabort", | ||
463 | "__asan_storeN_noabort", | ||
464 | "__asan_store1_noabort", | ||
465 | "__asan_store2_noabort", | ||
466 | "__asan_store4_noabort", | ||
467 | "__asan_store8_noabort", | ||
468 | "__asan_store16_noabort", | ||
469 | /* KASAN in-line */ | ||
470 | "__asan_report_load_n_noabort", | ||
471 | "__asan_report_load1_noabort", | ||
472 | "__asan_report_load2_noabort", | ||
473 | "__asan_report_load4_noabort", | ||
474 | "__asan_report_load8_noabort", | ||
475 | "__asan_report_load16_noabort", | ||
476 | "__asan_report_store_n_noabort", | ||
477 | "__asan_report_store1_noabort", | ||
478 | "__asan_report_store2_noabort", | ||
479 | "__asan_report_store4_noabort", | ||
480 | "__asan_report_store8_noabort", | ||
481 | "__asan_report_store16_noabort", | ||
482 | /* KCOV */ | ||
483 | "write_comp_data", | ||
484 | "__sanitizer_cov_trace_pc", | ||
485 | "__sanitizer_cov_trace_const_cmp1", | ||
486 | "__sanitizer_cov_trace_const_cmp2", | ||
487 | "__sanitizer_cov_trace_const_cmp4", | ||
488 | "__sanitizer_cov_trace_const_cmp8", | ||
489 | "__sanitizer_cov_trace_cmp1", | ||
490 | "__sanitizer_cov_trace_cmp2", | ||
491 | "__sanitizer_cov_trace_cmp4", | ||
492 | "__sanitizer_cov_trace_cmp8", | ||
493 | /* UBSAN */ | ||
494 | "ubsan_type_mismatch_common", | ||
495 | "__ubsan_handle_type_mismatch", | ||
496 | "__ubsan_handle_type_mismatch_v1", | ||
497 | /* misc */ | ||
498 | "csum_partial_copy_generic", | ||
499 | "__memcpy_mcsafe", | ||
500 | "ftrace_likely_update", /* CONFIG_TRACE_BRANCH_PROFILING */ | ||
501 | NULL | ||
502 | }; | ||
503 | |||
504 | static void add_uaccess_safe(struct objtool_file *file) | ||
505 | { | ||
506 | struct symbol *func; | ||
507 | const char **name; | ||
508 | |||
509 | if (!uaccess) | ||
510 | return; | ||
511 | |||
512 | for (name = uaccess_safe_builtin; *name; name++) { | ||
513 | func = find_symbol_by_name(file->elf, *name); | ||
514 | if (!func) | ||
515 | continue; | ||
516 | |||
517 | func->alias->uaccess_safe = true; | ||
518 | } | ||
519 | } | ||
520 | |||
521 | /* | ||
446 | * FIXME: For now, just ignore any alternatives which add retpolines. This is | 522 | * FIXME: For now, just ignore any alternatives which add retpolines. This is |
447 | * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline. | 523 | * a temporary hack, as it doesn't allow ORC to unwind from inside a retpoline. |
448 | * But it at least allows objtool to understand the control flow *around* the | 524 | * But it at least allows objtool to understand the control flow *around* the |
@@ -818,6 +894,7 @@ static int add_special_section_alts(struct objtool_file *file) | |||
818 | 894 | ||
819 | alt->insn = new_insn; | 895 | alt->insn = new_insn; |
820 | alt->skip_orig = special_alt->skip_orig; | 896 | alt->skip_orig = special_alt->skip_orig; |
897 | orig_insn->ignore_alts |= special_alt->skip_alt; | ||
821 | list_add_tail(&alt->list, &orig_insn->alts); | 898 | list_add_tail(&alt->list, &orig_insn->alts); |
822 | 899 | ||
823 | list_del(&special_alt->list); | 900 | list_del(&special_alt->list); |
@@ -1239,6 +1316,7 @@ static int decode_sections(struct objtool_file *file) | |||
1239 | return ret; | 1316 | return ret; |
1240 | 1317 | ||
1241 | add_ignores(file); | 1318 | add_ignores(file); |
1319 | add_uaccess_safe(file); | ||
1242 | 1320 | ||
1243 | ret = add_ignore_alternatives(file); | 1321 | ret = add_ignore_alternatives(file); |
1244 | if (ret) | 1322 | if (ret) |
@@ -1320,11 +1398,11 @@ static int update_insn_state_regs(struct instruction *insn, struct insn_state *s | |||
1320 | return 0; | 1398 | return 0; |
1321 | 1399 | ||
1322 | /* push */ | 1400 | /* push */ |
1323 | if (op->dest.type == OP_DEST_PUSH) | 1401 | if (op->dest.type == OP_DEST_PUSH || op->dest.type == OP_DEST_PUSHF) |
1324 | cfa->offset += 8; | 1402 | cfa->offset += 8; |
1325 | 1403 | ||
1326 | /* pop */ | 1404 | /* pop */ |
1327 | if (op->src.type == OP_SRC_POP) | 1405 | if (op->src.type == OP_SRC_POP || op->src.type == OP_SRC_POPF) |
1328 | cfa->offset -= 8; | 1406 | cfa->offset -= 8; |
1329 | 1407 | ||
1330 | /* add immediate to sp */ | 1408 | /* add immediate to sp */ |
@@ -1581,6 +1659,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) | |||
1581 | break; | 1659 | break; |
1582 | 1660 | ||
1583 | case OP_SRC_POP: | 1661 | case OP_SRC_POP: |
1662 | case OP_SRC_POPF: | ||
1584 | if (!state->drap && op->dest.type == OP_DEST_REG && | 1663 | if (!state->drap && op->dest.type == OP_DEST_REG && |
1585 | op->dest.reg == cfa->base) { | 1664 | op->dest.reg == cfa->base) { |
1586 | 1665 | ||
@@ -1645,6 +1724,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) | |||
1645 | break; | 1724 | break; |
1646 | 1725 | ||
1647 | case OP_DEST_PUSH: | 1726 | case OP_DEST_PUSH: |
1727 | case OP_DEST_PUSHF: | ||
1648 | state->stack_size += 8; | 1728 | state->stack_size += 8; |
1649 | if (cfa->base == CFI_SP) | 1729 | if (cfa->base == CFI_SP) |
1650 | cfa->offset += 8; | 1730 | cfa->offset += 8; |
@@ -1735,7 +1815,7 @@ static int update_insn_state(struct instruction *insn, struct insn_state *state) | |||
1735 | break; | 1815 | break; |
1736 | 1816 | ||
1737 | case OP_DEST_MEM: | 1817 | case OP_DEST_MEM: |
1738 | if (op->src.type != OP_SRC_POP) { | 1818 | if (op->src.type != OP_SRC_POP && op->src.type != OP_SRC_POPF) { |
1739 | WARN_FUNC("unknown stack-related memory operation", | 1819 | WARN_FUNC("unknown stack-related memory operation", |
1740 | insn->sec, insn->offset); | 1820 | insn->sec, insn->offset); |
1741 | return -1; | 1821 | return -1; |
@@ -1799,6 +1879,33 @@ static bool insn_state_match(struct instruction *insn, struct insn_state *state) | |||
1799 | return false; | 1879 | return false; |
1800 | } | 1880 | } |
1801 | 1881 | ||
1882 | static inline bool func_uaccess_safe(struct symbol *func) | ||
1883 | { | ||
1884 | if (func) | ||
1885 | return func->alias->uaccess_safe; | ||
1886 | |||
1887 | return false; | ||
1888 | } | ||
1889 | |||
1890 | static inline const char *insn_dest_name(struct instruction *insn) | ||
1891 | { | ||
1892 | if (insn->call_dest) | ||
1893 | return insn->call_dest->name; | ||
1894 | |||
1895 | return "{dynamic}"; | ||
1896 | } | ||
1897 | |||
1898 | static int validate_call(struct instruction *insn, struct insn_state *state) | ||
1899 | { | ||
1900 | if (state->uaccess && !func_uaccess_safe(insn->call_dest)) { | ||
1901 | WARN_FUNC("call to %s() with UACCESS enabled", | ||
1902 | insn->sec, insn->offset, insn_dest_name(insn)); | ||
1903 | return 1; | ||
1904 | } | ||
1905 | |||
1906 | return 0; | ||
1907 | } | ||
1908 | |||
1802 | static int validate_sibling_call(struct instruction *insn, struct insn_state *state) | 1909 | static int validate_sibling_call(struct instruction *insn, struct insn_state *state) |
1803 | { | 1910 | { |
1804 | if (has_modified_stack_frame(state)) { | 1911 | if (has_modified_stack_frame(state)) { |
@@ -1807,7 +1914,7 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st | |||
1807 | return 1; | 1914 | return 1; |
1808 | } | 1915 | } |
1809 | 1916 | ||
1810 | return 0; | 1917 | return validate_call(insn, state); |
1811 | } | 1918 | } |
1812 | 1919 | ||
1813 | /* | 1920 | /* |
@@ -1855,7 +1962,9 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
1855 | if (!insn->hint && !insn_state_match(insn, &state)) | 1962 | if (!insn->hint && !insn_state_match(insn, &state)) |
1856 | return 1; | 1963 | return 1; |
1857 | 1964 | ||
1858 | return 0; | 1965 | /* If we were here with AC=0, but now have AC=1, go again */ |
1966 | if (insn->state.uaccess || !state.uaccess) | ||
1967 | return 0; | ||
1859 | } | 1968 | } |
1860 | 1969 | ||
1861 | if (insn->hint) { | 1970 | if (insn->hint) { |
@@ -1925,6 +2034,16 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
1925 | switch (insn->type) { | 2034 | switch (insn->type) { |
1926 | 2035 | ||
1927 | case INSN_RETURN: | 2036 | case INSN_RETURN: |
2037 | if (state.uaccess && !func_uaccess_safe(func)) { | ||
2038 | WARN_FUNC("return with UACCESS enabled", sec, insn->offset); | ||
2039 | return 1; | ||
2040 | } | ||
2041 | |||
2042 | if (!state.uaccess && func_uaccess_safe(func)) { | ||
2043 | WARN_FUNC("return with UACCESS disabled from a UACCESS-safe function", sec, insn->offset); | ||
2044 | return 1; | ||
2045 | } | ||
2046 | |||
1928 | if (func && has_modified_stack_frame(&state)) { | 2047 | if (func && has_modified_stack_frame(&state)) { |
1929 | WARN_FUNC("return with modified stack frame", | 2048 | WARN_FUNC("return with modified stack frame", |
1930 | sec, insn->offset); | 2049 | sec, insn->offset); |
@@ -1940,17 +2059,22 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
1940 | return 0; | 2059 | return 0; |
1941 | 2060 | ||
1942 | case INSN_CALL: | 2061 | case INSN_CALL: |
1943 | if (is_fentry_call(insn)) | 2062 | case INSN_CALL_DYNAMIC: |
1944 | break; | 2063 | ret = validate_call(insn, &state); |
2064 | if (ret) | ||
2065 | return ret; | ||
1945 | 2066 | ||
1946 | ret = dead_end_function(file, insn->call_dest); | 2067 | if (insn->type == INSN_CALL) { |
1947 | if (ret == 1) | 2068 | if (is_fentry_call(insn)) |
1948 | return 0; | 2069 | break; |
1949 | if (ret == -1) | 2070 | |
1950 | return 1; | 2071 | ret = dead_end_function(file, insn->call_dest); |
2072 | if (ret == 1) | ||
2073 | return 0; | ||
2074 | if (ret == -1) | ||
2075 | return 1; | ||
2076 | } | ||
1951 | 2077 | ||
1952 | /* fallthrough */ | ||
1953 | case INSN_CALL_DYNAMIC: | ||
1954 | if (!no_fp && func && !has_valid_stack_frame(&state)) { | 2078 | if (!no_fp && func && !has_valid_stack_frame(&state)) { |
1955 | WARN_FUNC("call without frame pointer save/setup", | 2079 | WARN_FUNC("call without frame pointer save/setup", |
1956 | sec, insn->offset); | 2080 | sec, insn->offset); |
@@ -2003,6 +2127,49 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |||
2003 | if (update_insn_state(insn, &state)) | 2127 | if (update_insn_state(insn, &state)) |
2004 | return 1; | 2128 | return 1; |
2005 | 2129 | ||
2130 | if (insn->stack_op.dest.type == OP_DEST_PUSHF) { | ||
2131 | if (!state.uaccess_stack) { | ||
2132 | state.uaccess_stack = 1; | ||
2133 | } else if (state.uaccess_stack >> 31) { | ||
2134 | WARN_FUNC("PUSHF stack exhausted", sec, insn->offset); | ||
2135 | return 1; | ||
2136 | } | ||
2137 | state.uaccess_stack <<= 1; | ||
2138 | state.uaccess_stack |= state.uaccess; | ||
2139 | } | ||
2140 | |||
2141 | if (insn->stack_op.src.type == OP_SRC_POPF) { | ||
2142 | if (state.uaccess_stack) { | ||
2143 | state.uaccess = state.uaccess_stack & 1; | ||
2144 | state.uaccess_stack >>= 1; | ||
2145 | if (state.uaccess_stack == 1) | ||
2146 | state.uaccess_stack = 0; | ||
2147 | } | ||
2148 | } | ||
2149 | |||
2150 | break; | ||
2151 | |||
2152 | case INSN_STAC: | ||
2153 | if (state.uaccess) { | ||
2154 | WARN_FUNC("recursive UACCESS enable", sec, insn->offset); | ||
2155 | return 1; | ||
2156 | } | ||
2157 | |||
2158 | state.uaccess = true; | ||
2159 | break; | ||
2160 | |||
2161 | case INSN_CLAC: | ||
2162 | if (!state.uaccess && insn->func) { | ||
2163 | WARN_FUNC("redundant UACCESS disable", sec, insn->offset); | ||
2164 | return 1; | ||
2165 | } | ||
2166 | |||
2167 | if (func_uaccess_safe(func) && !state.uaccess_stack) { | ||
2168 | WARN_FUNC("UACCESS-safe disables UACCESS", sec, insn->offset); | ||
2169 | return 1; | ||
2170 | } | ||
2171 | |||
2172 | state.uaccess = false; | ||
2006 | break; | 2173 | break; |
2007 | 2174 | ||
2008 | default: | 2175 | default: |
@@ -2168,6 +2335,8 @@ static int validate_functions(struct objtool_file *file) | |||
2168 | if (!insn || insn->ignore) | 2335 | if (!insn || insn->ignore) |
2169 | continue; | 2336 | continue; |
2170 | 2337 | ||
2338 | state.uaccess = func->alias->uaccess_safe; | ||
2339 | |||
2171 | ret = validate_branch(file, insn, state); | 2340 | ret = validate_branch(file, insn, state); |
2172 | if (ret && backtrace) | 2341 | if (ret && backtrace) |
2173 | BT_FUNC("<=== (func)", insn); | 2342 | BT_FUNC("<=== (func)", insn); |
diff --git a/tools/objtool/check.h b/tools/objtool/check.h index d8896eb43521..78a95d06c165 100644 --- a/tools/objtool/check.h +++ b/tools/objtool/check.h | |||
@@ -31,7 +31,8 @@ struct insn_state { | |||
31 | int stack_size; | 31 | int stack_size; |
32 | unsigned char type; | 32 | unsigned char type; |
33 | bool bp_scratch; | 33 | bool bp_scratch; |
34 | bool drap, end; | 34 | bool drap, end, uaccess; |
35 | unsigned int uaccess_stack; | ||
35 | int drap_reg, drap_offset; | 36 | int drap_reg, drap_offset; |
36 | struct cfi_reg vals[CFI_NUM_REGS]; | 37 | struct cfi_reg vals[CFI_NUM_REGS]; |
37 | }; | 38 | }; |
diff --git a/tools/objtool/elf.h b/tools/objtool/elf.h index 968265b4b4cd..2cc2ed49322d 100644 --- a/tools/objtool/elf.h +++ b/tools/objtool/elf.h | |||
@@ -62,6 +62,7 @@ struct symbol { | |||
62 | unsigned long offset; | 62 | unsigned long offset; |
63 | unsigned int len; | 63 | unsigned int len; |
64 | struct symbol *pfunc, *cfunc, *alias; | 64 | struct symbol *pfunc, *cfunc, *alias; |
65 | bool uaccess_safe; | ||
65 | }; | 66 | }; |
66 | 67 | ||
67 | struct rela { | 68 | struct rela { |
diff --git a/tools/objtool/special.c b/tools/objtool/special.c index 50af4e1274b3..4e50563d87c6 100644 --- a/tools/objtool/special.c +++ b/tools/objtool/special.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <string.h> | 24 | #include <string.h> |
25 | 25 | ||
26 | #include "builtin.h" | ||
26 | #include "special.h" | 27 | #include "special.h" |
27 | #include "warn.h" | 28 | #include "warn.h" |
28 | 29 | ||
@@ -42,6 +43,7 @@ | |||
42 | #define ALT_NEW_LEN_OFFSET 11 | 43 | #define ALT_NEW_LEN_OFFSET 11 |
43 | 44 | ||
44 | #define X86_FEATURE_POPCNT (4*32+23) | 45 | #define X86_FEATURE_POPCNT (4*32+23) |
46 | #define X86_FEATURE_SMAP (9*32+20) | ||
45 | 47 | ||
46 | struct special_entry { | 48 | struct special_entry { |
47 | const char *sec; | 49 | const char *sec; |
@@ -110,6 +112,22 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, | |||
110 | */ | 112 | */ |
111 | if (feature == X86_FEATURE_POPCNT) | 113 | if (feature == X86_FEATURE_POPCNT) |
112 | alt->skip_orig = true; | 114 | alt->skip_orig = true; |
115 | |||
116 | /* | ||
117 | * If UACCESS validation is enabled; force that alternative; | ||
118 | * otherwise force it the other way. | ||
119 | * | ||
120 | * What we want to avoid is having both the original and the | ||
121 | * alternative code flow at the same time, in that case we can | ||
122 | * find paths that see the STAC but take the NOP instead of | ||
123 | * CLAC and the other way around. | ||
124 | */ | ||
125 | if (feature == X86_FEATURE_SMAP) { | ||
126 | if (uaccess) | ||
127 | alt->skip_orig = true; | ||
128 | else | ||
129 | alt->skip_alt = true; | ||
130 | } | ||
113 | } | 131 | } |
114 | 132 | ||
115 | orig_rela = find_rela_by_dest(sec, offset + entry->orig); | 133 | orig_rela = find_rela_by_dest(sec, offset + entry->orig); |
diff --git a/tools/objtool/special.h b/tools/objtool/special.h index fad1d092f679..d5c062e718ef 100644 --- a/tools/objtool/special.h +++ b/tools/objtool/special.h | |||
@@ -26,6 +26,7 @@ struct special_alt { | |||
26 | 26 | ||
27 | bool group; | 27 | bool group; |
28 | bool skip_orig; | 28 | bool skip_orig; |
29 | bool skip_alt; | ||
29 | bool jump_or_nop; | 30 | bool jump_or_nop; |
30 | 31 | ||
31 | struct section *orig_sec; | 32 | struct section *orig_sec; |