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