summaryrefslogtreecommitdiffstats
path: root/tools/objtool
diff options
context:
space:
mode:
Diffstat (limited to 'tools/objtool')
-rw-r--r--tools/objtool/arch.h6
-rw-r--r--tools/objtool/arch/x86/decode.c13
-rw-r--r--tools/objtool/builtin-check.c3
-rw-r--r--tools/objtool/builtin.h2
-rw-r--r--tools/objtool/check.c197
-rw-r--r--tools/objtool/check.h3
-rw-r--r--tools/objtool/elf.h1
-rw-r--r--tools/objtool/special.c18
-rw-r--r--tools/objtool/special.h1
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
39enum op_dest_type { 41enum 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
32bool no_fp, no_unreachable, retpoline, module, backtrace; 32bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
33 33
34static const char * const check_usage[] = { 34static 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
22extern const struct option check_options[]; 22extern const struct option check_options[];
23extern bool no_fp, no_unreachable, retpoline, module, backtrace; 23extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess;
24 24
25extern int cmd_check(int argc, const char **argv); 25extern int cmd_check(int argc, const char **argv);
26extern int cmd_orc(int argc, const char **argv); 26extern 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 */
452static 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
504static 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
1882static inline bool func_uaccess_safe(struct symbol *func)
1883{
1884 if (func)
1885 return func->alias->uaccess_safe;
1886
1887 return false;
1888}
1889
1890static 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
1898static 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
1802static int validate_sibling_call(struct instruction *insn, struct insn_state *state) 1909static 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
67struct rela { 68struct 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
46struct special_entry { 48struct 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;