aboutsummaryrefslogtreecommitdiffstats
path: root/tools/objtool
diff options
context:
space:
mode:
authorJosh Poimboeuf <jpoimboe@kernel.org>2023-03-01 07:13:12 -0800
committerPeter Zijlstra <peterz@infradead.org>2023-03-23 23:18:58 +0100
commitfb799447ae2974a07907906dff5bd4b9e47b7123 (patch)
tree05f8233eb66094db1be1801c5544776674c5913a /tools/objtool
parent4708ea14bef314fc901857eefd65678236a9f2d9 (diff)
downloadlinux-fb799447ae2974a07907906dff5bd4b9e47b7123.tar.gz
x86,objtool: Split UNWIND_HINT_EMPTY in two
Mark reported that the ORC unwinder incorrectly marks an unwind as reliable when the unwind terminates prematurely in the dark corners of return_to_handler() due to lack of information about the next frame. The problem is UNWIND_HINT_EMPTY is used in two different situations: 1) The end of the kernel stack unwind before hitting user entry, boot code, or fork entry 2) A blind spot in ORC coverage where the unwinder has to bail due to lack of information about the next frame The ORC unwinder has no way to tell the difference between the two. When it encounters an undefined stack state with 'end=1', it blindly marks the stack reliable, which can break the livepatch consistency model. Fix it by splitting UNWIND_HINT_EMPTY into UNWIND_HINT_UNDEFINED and UNWIND_HINT_END_OF_STACK. Reported-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Steven Rostedt (Google) <rostedt@goodmis.org> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/fd6212c8b450d3564b855e1cb48404d6277b4d9f.1677683419.git.jpoimboe@kernel.org
Diffstat (limited to 'tools/objtool')
-rw-r--r--tools/objtool/check.c2
-rw-r--r--tools/objtool/orc_dump.c8
-rw-r--r--tools/objtool/orc_gen.c26
3 files changed, 20 insertions, 16 deletions
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 10be80b3fe672..cae6ac6ff2469 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2243,6 +2243,7 @@ static void set_func_state(struct cfi_state *state)
memcpy(&state->regs, &initial_func_cfi.regs,
CFI_NUM_REGS * sizeof(struct cfi_reg));
state->stack_size = initial_func_cfi.cfa.offset;
+ state->type = UNWIND_HINT_TYPE_CALL;
}
static int read_unwind_hints(struct objtool_file *file)
@@ -2327,7 +2328,6 @@ static int read_unwind_hints(struct objtool_file *file)
cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset);
cfi.type = hint->type;
cfi.signal = hint->signal;
- cfi.end = hint->end;
insn->cfi = cfi_hash_find_or_add(&cfi);
}
diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c
index 97ecbb8b9034f..0e183bb1c7205 100644
--- a/tools/objtool/orc_dump.c
+++ b/tools/objtool/orc_dump.c
@@ -38,6 +38,10 @@ static const char *reg_name(unsigned int reg)
static const char *orc_type_name(unsigned int type)
{
switch (type) {
+ case ORC_TYPE_UNDEFINED:
+ return "(und)";
+ case ORC_TYPE_END_OF_STACK:
+ return "end";
case ORC_TYPE_CALL:
return "call";
case ORC_TYPE_REGS:
@@ -201,6 +205,7 @@ int orc_dump(const char *_objname)
printf("%llx:", (unsigned long long)(orc_ip_addr + (i * sizeof(int)) + orc_ip[i]));
}
+ printf("type:%s", orc_type_name(orc[i].type));
printf(" sp:");
@@ -210,8 +215,7 @@ int orc_dump(const char *_objname)
print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset));
- printf(" type:%s signal:%d end:%d\n",
- orc_type_name(orc[i].type), orc[i].signal, orc[i].end);
+ printf(" signal:%d\n", orc[i].signal);
}
elf_end(elf);
diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c
index e85bbb996f6c6..b327f9ccfe733 100644
--- a/tools/objtool/orc_gen.c
+++ b/tools/objtool/orc_gen.c
@@ -21,12 +21,22 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
memset(orc, 0, sizeof(*orc));
if (!cfi) {
- orc->end = 0;
- orc->sp_reg = ORC_REG_UNDEFINED;
+ /*
+ * This is usually either unreachable nops/traps (which don't
+ * trigger unreachable instruction warnings), or
+ * STACK_FRAME_NON_STANDARD functions.
+ */
+ orc->type = ORC_TYPE_UNDEFINED;
return 0;
}
switch (cfi->type) {
+ case UNWIND_HINT_TYPE_UNDEFINED:
+ orc->type = ORC_TYPE_UNDEFINED;
+ return 0;
+ case UNWIND_HINT_TYPE_END_OF_STACK:
+ orc->type = ORC_TYPE_END_OF_STACK;
+ return 0;
case UNWIND_HINT_TYPE_CALL:
orc->type = ORC_TYPE_CALL;
break;
@@ -42,14 +52,8 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
return -1;
}
- orc->end = cfi->end;
orc->signal = cfi->signal;
- if (cfi->cfa.base == CFI_UNDEFINED) {
- orc->sp_reg = ORC_REG_UNDEFINED;
- return 0;
- }
-
switch (cfi->cfa.base) {
case CFI_SP:
orc->sp_reg = ORC_REG_SP;
@@ -163,11 +167,7 @@ int orc_create(struct objtool_file *file)
struct orc_list_entry *entry;
struct list_head orc_list;
- struct orc_entry null = {
- .sp_reg = ORC_REG_UNDEFINED,
- .bp_reg = ORC_REG_UNDEFINED,
- .type = ORC_TYPE_CALL,
- };
+ struct orc_entry null = { .type = ORC_TYPE_UNDEFINED };
/* Build a deduplicated list of ORC entries: */
INIT_LIST_HEAD(&orc_list);