lkml.org 
[lkml]   [2022]   [Feb]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[PATCH v2 35/39] objtool: IBT fix direct JMP/CALL
Optionally rewrite all direct JMP/CALL that target ENDBR. 

By doing this it is guaranteed that only indirect code flow uses
ENDBR, at which point it becomes possible to poison unused ENDBR
instructions (a later patch).

By having this rely on --lto the only direct code flow missed is that
fixed up by the module loader.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
tools/objtool/arch/x86/decode.c | 71 ++++++++++++++++++++++++++++++++
tools/objtool/builtin-check.c | 3 -
tools/objtool/check.c | 45 ++++++++++++++++++--
tools/objtool/include/objtool/arch.h | 1
tools/objtool/include/objtool/builtin.h | 2
5 files changed, 116 insertions(+), 6 deletions(-)

--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -729,6 +729,77 @@ const char *arch_nop_insn(int len)
return nops[len-1];
}

+const char *arch_mod_immediate(struct instruction *insn, unsigned long target)
+{
+ struct section *sec = insn->sec;
+ Elf_Data *data = sec->data;
+ unsigned char op1, op2;
+ static char bytes[16];
+ struct insn x86_insn;
+ int ret, disp;
+
+ disp = (long)(target - (insn->offset + insn->len));
+
+ if (data->d_type != ELF_T_BYTE || data->d_off) {
+ WARN("unexpected data for section: %s", sec->name);
+ return NULL;
+ }
+
+ ret = insn_decode(&x86_insn, data->d_buf + insn->offset, insn->len,
+ INSN_MODE_64);
+ if (ret < 0) {
+ WARN("can't decode instruction at %s:0x%lx", sec->name, insn->offset);
+ return NULL;
+ }
+
+ op1 = x86_insn.opcode.bytes[0];
+ op2 = x86_insn.opcode.bytes[1];
+
+ switch (op1) {
+ case 0x0f: /* escape */
+ switch (op2) {
+ case 0x80 ... 0x8f: /* jcc.d32 */
+ if (insn->len != 6)
+ return NULL;
+ bytes[0] = op1;
+ bytes[1] = op2;
+ *(int *)&bytes[2] = disp;
+ break;
+
+ default:
+ return NULL;
+ }
+ break;
+
+ case 0x70 ... 0x7f: /* jcc.d8 */
+ case 0xeb: /* jmp.d8 */
+ if (insn->len != 2)
+ return NULL;
+
+ if (disp >> 7 != disp >> 31) {
+ WARN("displacement doesn't fit\n");
+ return NULL;
+ }
+
+ bytes[0] = op1;
+ bytes[1] = disp & 0xff;
+ break;
+
+ case 0xe8: /* call */
+ case 0xe9: /* jmp.d32 */
+ if (insn->len != 5)
+ return NULL;
+ bytes[0] = op1;
+ *(int *)&bytes[1] = disp;
+ break;
+
+ default:
+ return NULL;
+ }
+
+ return bytes;
+}
+
#define BYTE_RET 0xC3

const char *arch_ret_insn(int len)
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -21,7 +21,7 @@

bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats,
lto, vmlinux, mcount, noinstr, backup, sls, dryrun,
- ibt;
+ ibt, ibt_fix_direct;

static const char * const check_usage[] = {
"objtool check [<options>] file.o",
@@ -49,6 +49,7 @@ const struct option check_options[] = {
OPT_BOOLEAN('S', "sls", &sls, "validate straight-line-speculation"),
OPT_BOOLEAN(0, "dry-run", &dryrun, "don't write the modifications"),
OPT_BOOLEAN(0, "ibt", &ibt, "validate ENDBR placement"),
+ OPT_BOOLEAN(0, "ibt-fix-direct", &ibt_fix_direct, "fixup direct jmp/call to ENDBR"),
OPT_END(),
};

--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1240,9 +1240,25 @@ static int add_jump_destinations(struct
insn->jump_dest->func &&
insn->jump_dest->offset == insn->jump_dest->func->offset) {
if (reloc) {
- WARN_FUNC("Direct RELOC jump to ENDBR", insn->sec, insn->offset);
+ if (ibt_fix_direct) {
+ reloc->addend += 4;
+ elf_write_reloc(file->elf, reloc);
+ } else {
+ WARN_FUNC("Direct RELOC jump to ENDBR", insn->sec, insn->offset);
+ }
} else {
- WARN_FUNC("Direct IMM jump to ENDBR", insn->sec, insn->offset);
+ if (ibt_fix_direct) {
+ const char *bytes = arch_mod_immediate(insn, dest_off + 4);
+ if (bytes) {
+ elf_write_insn(file->elf, insn->sec,
+ insn->offset, insn->len,
+ bytes);
+ } else {
+ WARN_FUNC("Direct IMM jump to ENDBR; cannot fix", insn->sec, insn->offset);
+ }
+ } else {
+ WARN_FUNC("Direct IMM jump to ENDBR", insn->sec, insn->offset);
+ }
}
}

@@ -1378,9 +1394,25 @@ static int add_call_destinations(struct

if (ibt && target && target->type == INSN_ENDBR) {
if (reloc) {
- WARN_FUNC("Direct RELOC call to ENDBR", insn->sec, insn->offset);
+ if (ibt_fix_direct) {
+ reloc->addend += 4;
+ elf_write_reloc(file->elf, reloc);
+ } else {
+ WARN_FUNC("Direct RELOC call to ENDBR", insn->sec, insn->offset);
+ }
} else {
- WARN_FUNC("Direct IMM call to ENDBR", insn->sec, insn->offset);
+ if (ibt_fix_direct) {
+ const char *bytes = arch_mod_immediate(insn, dest_off + 4);
+ if (bytes) {
+ elf_write_insn(file->elf, insn->sec,
+ insn->offset, insn->len,
+ bytes);
+ } else {
+ WARN_FUNC("Direct IMM call to ENDBR; cannot fix", insn->sec, insn->offset);
+ }
+ } else {
+ WARN_FUNC("Direct IMM call to ENDBR", insn->sec, insn->offset);
+ }
}
}
}
@@ -3740,6 +3772,11 @@ int check(struct objtool_file *file)
return 1;
}

+ if (ibt_fix_direct && !ibt) {
+ fprintf(stderr, "--ibt-fix-direct requires: --ibt\n");
+ return 1;
+ }
+
arch_initial_func_cfi_state(&initial_func_cfi);
init_cfi_state(&init_cfi);
init_cfi_state(&func_cfi);
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -85,6 +85,7 @@ unsigned long arch_dest_reloc_offset(int

const char *arch_nop_insn(int len);
const char *arch_ret_insn(int len);
+const char *arch_mod_immediate(struct instruction *insn, unsigned long target);

int arch_decode_hint_reg(u8 sp_reg, int *base);

--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -10,7 +10,7 @@
extern const struct option check_options[];
extern bool no_fp, no_unreachable, retpoline, module, backtrace, uaccess, stats,
lto, vmlinux, mcount, noinstr, backup, sls, dryrun,
- ibt;
+ ibt, ibt_fix_direct;

extern int cmd_parse_options(int argc, const char **argv, const char * const usage[]);


\
 
 \ /
  Last update: 2022-02-24 16:36    [W:1.144 / U:0.092 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site