From cc465285cc8871fa32fd0f1475aa871299de68e5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Feb 27 2023 11:39:25 +0000 Subject: x86/alternatives: Teach text_poke_bp() to patch Jcc.d32 instructions (bsc#1012628). --- diff --git a/patches.kernel.org/6.2.1-003-x86-alternatives-Teach-text_poke_bp-to-patch-Jc.patch b/patches.kernel.org/6.2.1-003-x86-alternatives-Teach-text_poke_bp-to-patch-Jc.patch new file mode 100644 index 0000000..3421348 --- /dev/null +++ b/patches.kernel.org/6.2.1-003-x86-alternatives-Teach-text_poke_bp-to-patch-Jc.patch @@ -0,0 +1,193 @@ +From: Peter Zijlstra +Date: Mon, 23 Jan 2023 21:59:17 +0100 +Subject: [PATCH] x86/alternatives: Teach text_poke_bp() to patch Jcc.d32 + instructions +References: bsc#1012628 +Patch-mainline: 6.2.1 +Git-commit: ac0ee0a9560c97fa5fe1409e450c2425d4ebd17a + +commit ac0ee0a9560c97fa5fe1409e450c2425d4ebd17a upstream. + +In order to re-write Jcc.d32 instructions text_poke_bp() needs to be +taught about them. + +The biggest hurdle is that the whole machinery is currently made for 5 +byte instructions and extending this would grow struct text_poke_loc +which is currently a nice 16 bytes and used in an array. + +However, since text_poke_loc contains a full copy of the (s32) +displacement, it is possible to map the Jcc.d32 2 byte opcodes to +Jcc.d8 1 byte opcode for the int3 emulation. + +This then leaves the replacement bytes; fudge that by only storing the +last 5 bytes and adding the rule that 'length == 6' instruction will +be prefixed with a 0x0f byte. + +Signed-off-by: Peter Zijlstra (Intel) +Signed-off-by: Ingo Molnar +Reviewed-by: Masami Hiramatsu (Google) +Link: https://lore.kernel.org/r/20230123210607.115718513@infradead.org +Cc: Nathan Chancellor +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Jiri Slaby +--- + arch/x86/kernel/alternative.c | 62 ++++++++++++++++++++++++++--------- + 1 file changed, 47 insertions(+), 15 deletions(-) + +diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c +index 7d8c3cbd..81381a01 100644 +--- a/arch/x86/kernel/alternative.c ++++ b/arch/x86/kernel/alternative.c +@@ -340,6 +340,12 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start, + } + } + ++static inline bool is_jcc32(struct insn *insn) ++{ ++ /* Jcc.d32 second opcode byte is in the range: 0x80-0x8f */ ++ return insn->opcode.bytes[0] == 0x0f && (insn->opcode.bytes[1] & 0xf0) == 0x80; ++} ++ + #if defined(CONFIG_RETPOLINE) && defined(CONFIG_OBJTOOL) + + /* +@@ -378,12 +384,6 @@ static int emit_indirect(int op, int reg, u8 *bytes) + return i; + } + +-static inline bool is_jcc32(struct insn *insn) +-{ +- /* Jcc.d32 second opcode byte is in the range: 0x80-0x8f */ +- return insn->opcode.bytes[0] == 0x0f && (insn->opcode.bytes[1] & 0xf0) == 0x80; +-} +- + static int emit_call_track_retpoline(void *addr, struct insn *insn, int reg, u8 *bytes) + { + u8 op = insn->opcode.bytes[0]; +@@ -1772,6 +1772,11 @@ void text_poke_sync(void) + on_each_cpu(do_sync_core, NULL, 1); + } + ++/* ++ * NOTE: crazy scheme to allow patching Jcc.d32 but not increase the size of ++ * this thing. When len == 6 everything is prefixed with 0x0f and we map ++ * opcode to Jcc.d8, using len to distinguish. ++ */ + struct text_poke_loc { + /* addr := _stext + rel_addr */ + s32 rel_addr; +@@ -1893,6 +1898,10 @@ noinstr int poke_int3_handler(struct pt_regs *regs) + int3_emulate_jmp(regs, (long)ip + tp->disp); + break; + ++ case 0x70 ... 0x7f: /* Jcc */ ++ int3_emulate_jcc(regs, tp->opcode & 0xf, (long)ip, tp->disp); ++ break; ++ + default: + BUG(); + } +@@ -1966,16 +1975,26 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries + * Second step: update all but the first byte of the patched range. + */ + for (do_sync = 0, i = 0; i < nr_entries; i++) { +- u8 old[POKE_MAX_OPCODE_SIZE] = { tp[i].old, }; ++ u8 old[POKE_MAX_OPCODE_SIZE+1] = { tp[i].old, }; ++ u8 _new[POKE_MAX_OPCODE_SIZE+1]; ++ const u8 *new = tp[i].text; + int len = tp[i].len; + + if (len - INT3_INSN_SIZE > 0) { + memcpy(old + INT3_INSN_SIZE, + text_poke_addr(&tp[i]) + INT3_INSN_SIZE, + len - INT3_INSN_SIZE); ++ ++ if (len == 6) { ++ _new[0] = 0x0f; ++ memcpy(_new + 1, new, 5); ++ new = _new; ++ } ++ + text_poke(text_poke_addr(&tp[i]) + INT3_INSN_SIZE, +- (const char *)tp[i].text + INT3_INSN_SIZE, ++ new + INT3_INSN_SIZE, + len - INT3_INSN_SIZE); ++ + do_sync++; + } + +@@ -2003,8 +2022,7 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries + * The old instruction is recorded so that the event can be + * processed forwards or backwards. + */ +- perf_event_text_poke(text_poke_addr(&tp[i]), old, len, +- tp[i].text, len); ++ perf_event_text_poke(text_poke_addr(&tp[i]), old, len, new, len); + } + + if (do_sync) { +@@ -2021,10 +2039,15 @@ static void text_poke_bp_batch(struct text_poke_loc *tp, unsigned int nr_entries + * replacing opcode. + */ + for (do_sync = 0, i = 0; i < nr_entries; i++) { +- if (tp[i].text[0] == INT3_INSN_OPCODE) ++ u8 byte = tp[i].text[0]; ++ ++ if (tp[i].len == 6) ++ byte = 0x0f; ++ ++ if (byte == INT3_INSN_OPCODE) + continue; + +- text_poke(text_poke_addr(&tp[i]), tp[i].text, INT3_INSN_SIZE); ++ text_poke(text_poke_addr(&tp[i]), &byte, INT3_INSN_SIZE); + do_sync++; + } + +@@ -2042,9 +2065,11 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr, + const void *opcode, size_t len, const void *emulate) + { + struct insn insn; +- int ret, i; ++ int ret, i = 0; + +- memcpy((void *)tp->text, opcode, len); ++ if (len == 6) ++ i = 1; ++ memcpy((void *)tp->text, opcode+i, len-i); + if (!emulate) + emulate = opcode; + +@@ -2055,6 +2080,13 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr, + tp->len = len; + tp->opcode = insn.opcode.bytes[0]; + ++ if (is_jcc32(&insn)) { ++ /* ++ * Map Jcc.d32 onto Jcc.d8 and use len to distinguish. ++ */ ++ tp->opcode = insn.opcode.bytes[1] - 0x10; ++ } ++ + switch (tp->opcode) { + case RET_INSN_OPCODE: + case JMP32_INSN_OPCODE: +@@ -2071,7 +2103,6 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr, + BUG_ON(len != insn.length); + } + +- + switch (tp->opcode) { + case INT3_INSN_OPCODE: + case RET_INSN_OPCODE: +@@ -2080,6 +2111,7 @@ static void text_poke_loc_init(struct text_poke_loc *tp, void *addr, + case CALL_INSN_OPCODE: + case JMP32_INSN_OPCODE: + case JMP8_INSN_OPCODE: ++ case 0x70 ... 0x7f: /* Jcc */ + tp->disp = insn.immediate.value; + break; + +-- +2.35.3 + diff --git a/series.conf b/series.conf index 14ab745..ecfca5c 100644 --- a/series.conf +++ b/series.conf @@ -29,6 +29,7 @@ ######################################################## patches.kernel.org/6.2.1-001-uaccess-Add-speculation-barrier-to-copy_from_us.patch patches.kernel.org/6.2.1-002-x86-alternatives-Introduce-int3_emulate_jcc.patch + patches.kernel.org/6.2.1-003-x86-alternatives-Teach-text_poke_bp-to-patch-Jc.patch ######################################################## # Build fixes that apply to the vanilla kernel too.